001
014
015 package com.liferay.portal.kernel.search;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
018 import com.liferay.portal.kernel.util.ArrayUtil;
019 import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
020 import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil;
021 import com.liferay.portal.kernel.util.FileUtil;
022 import com.liferay.portal.kernel.util.GetterUtil;
023 import com.liferay.portal.kernel.util.LocaleUtil;
024 import com.liferay.portal.kernel.util.PropsKeys;
025 import com.liferay.portal.kernel.util.PropsUtil;
026 import com.liferay.portal.kernel.util.SetUtil;
027 import com.liferay.portal.kernel.util.StringBundler;
028 import com.liferay.portal.kernel.util.StringPool;
029 import com.liferay.portal.kernel.util.StringUtil;
030 import com.liferay.portal.kernel.util.Validator;
031 import com.liferay.portlet.dynamicdatamapping.util.DDMIndexer;
032
033 import java.io.File;
034 import java.io.FileInputStream;
035 import java.io.IOException;
036 import java.io.InputStream;
037
038 import java.text.DateFormat;
039 import java.text.Format;
040 import java.text.ParseException;
041
042 import java.util.Arrays;
043 import java.util.Date;
044 import java.util.HashMap;
045 import java.util.Locale;
046 import java.util.Map;
047 import java.util.Set;
048
049
053 public class DocumentImpl implements Document {
054
055 public static String getLocalizedName(Locale locale, String name) {
056 if (locale == null) {
057 return name;
058 }
059
060 String languageId = LocaleUtil.toLanguageId(locale);
061
062 return getLocalizedName(languageId, name);
063 }
064
065 public static String getLocalizedName(String languageId, String name) {
066 return name.concat(StringPool.UNDERLINE).concat(languageId);
067 }
068
069 public static String getSortableFieldName(String name) {
070 return name.concat(StringPool.UNDERLINE).concat(_SORTABLE_FIELD_SUFFIX);
071 }
072
073 public static String getSortFieldName(Sort sort, String scoreFieldName) {
074 if (sort.getType() == Sort.SCORE_TYPE) {
075 return scoreFieldName;
076 }
077
078 String fieldName = sort.getFieldName();
079
080 if (DocumentImpl.isSortableFieldName(fieldName)) {
081 return fieldName;
082 }
083
084 if (fieldName.startsWith(DDMIndexer.DDM_FIELD_PREFIX)) {
085 return fieldName;
086 }
087
088 if ((sort.getType() == Sort.STRING_TYPE) &&
089 !DocumentImpl.isSortableTextField(fieldName)) {
090
091 return scoreFieldName;
092 }
093
094 return DocumentImpl.getSortableFieldName(fieldName);
095 }
096
097 public static boolean isSortableFieldName(String name) {
098 return name.endsWith(_SORTABLE_FIELD_SUFFIX);
099 }
100
101 public static boolean isSortableTextField(String name) {
102 return _defaultSortableTextFields.contains(name);
103 }
104
105 @Override
106 public void add(Field field) {
107 _fields.put(field.getName(), field);
108 }
109
110 @Override
111 public void addDate(String name, Date value) {
112 if (value == null) {
113 return;
114 }
115
116 addDate(name, new Date[] {value});
117 }
118
119 @Override
120 public void addDate(String name, Date[] values) {
121 if (values == null) {
122 return;
123 }
124
125 String[] dates = new String[values.length];
126 String[] datesTime = new String[values.length];
127
128 for (int i = 0; i < values.length; i++) {
129 dates[i] = _dateFormat.format(values[i]);
130 datesTime[i] = String.valueOf(values[i].getTime());
131 }
132
133 String sortableFieldName = getSortableFieldName(name);
134
135 Field field = new Field(sortableFieldName, datesTime);
136
137 field.setNumeric(true);
138 field.setNumericClass(Long.class);
139
140 _fields.put(sortableFieldName, field);
141
142 addKeyword(name, dates);
143 }
144
145 @Override
146 public void addFile(String name, byte[] bytes, String fileExt) {
147 InputStream is = new UnsyncByteArrayInputStream(bytes);
148
149 addFile(name, is, fileExt);
150 }
151
152 @Override
153 public void addFile(String name, File file, String fileExt)
154 throws IOException {
155
156 InputStream is = new FileInputStream(file);
157
158 addFile(name, is, fileExt);
159 }
160
161 @Override
162 public void addFile(String name, InputStream is, String fileExt) {
163 addText(name, FileUtil.extractText(is, fileExt));
164 }
165
166 @Override
167 public void addFile(
168 String name, InputStream is, String fileExt, int maxStringLength) {
169
170 addText(name, FileUtil.extractText(is, fileExt, maxStringLength));
171 }
172
173 @Override
174 public void addKeyword(String name, boolean value) {
175 addKeyword(name, String.valueOf(value));
176 }
177
178 @Override
179 public void addKeyword(String name, Boolean value) {
180 addKeyword(name, String.valueOf(value));
181 }
182
183 @Override
184 public void addKeyword(String name, boolean[] values) {
185 if (values == null) {
186 return;
187 }
188
189 addKeyword(name, ArrayUtil.toStringArray(values));
190 }
191
192 @Override
193 public void addKeyword(String name, Boolean[] values) {
194 if (values == null) {
195 return;
196 }
197
198 addKeyword(name, ArrayUtil.toStringArray(values));
199 }
200
201 @Override
202 public void addKeyword(String name, double value) {
203 addKeyword(name, String.valueOf(value));
204 }
205
206 @Override
207 public void addKeyword(String name, Double value) {
208 addKeyword(name, String.valueOf(value));
209 }
210
211 @Override
212 public void addKeyword(String name, double[] values) {
213 if (values == null) {
214 return;
215 }
216
217 addKeyword(name, ArrayUtil.toStringArray(values));
218 }
219
220 @Override
221 public void addKeyword(String name, Double[] values) {
222 if (values == null) {
223 return;
224 }
225
226 addKeyword(name, ArrayUtil.toStringArray(values));
227 }
228
229 @Override
230 public void addKeyword(String name, float value) {
231 addKeyword(name, String.valueOf(value));
232 }
233
234 @Override
235 public void addKeyword(String name, Float value) {
236 addKeyword(name, String.valueOf(value));
237 }
238
239 @Override
240 public void addKeyword(String name, float[] values) {
241 if (values == null) {
242 return;
243 }
244
245 addKeyword(name, ArrayUtil.toStringArray(values));
246 }
247
248 @Override
249 public void addKeyword(String name, Float[] values) {
250 if (values == null) {
251 return;
252 }
253
254 addKeyword(name, ArrayUtil.toStringArray(values));
255 }
256
257 @Override
258 public void addKeyword(String name, int value) {
259 addKeyword(name, String.valueOf(value));
260 }
261
262 @Override
263 public void addKeyword(String name, int[] values) {
264 if (values == null) {
265 return;
266 }
267
268 addKeyword(name, ArrayUtil.toStringArray(values));
269 }
270
271 @Override
272 public void addKeyword(String name, Integer value) {
273 addKeyword(name, String.valueOf(value));
274 }
275
276 @Override
277 public void addKeyword(String name, Integer[] values) {
278 if (values == null) {
279 return;
280 }
281
282 addKeyword(name, ArrayUtil.toStringArray(values));
283 }
284
285 @Override
286 public void addKeyword(String name, long value) {
287 addKeyword(name, String.valueOf(value));
288 }
289
290 @Override
291 public void addKeyword(String name, Long value) {
292 addKeyword(name, String.valueOf(value));
293 }
294
295 @Override
296 public void addKeyword(String name, long[] values) {
297 if (values == null) {
298 return;
299 }
300
301 addKeyword(name, ArrayUtil.toStringArray(values));
302 }
303
304 @Override
305 public void addKeyword(String name, Long[] values) {
306 if (values == null) {
307 return;
308 }
309
310 addKeyword(name, ArrayUtil.toStringArray(values));
311 }
312
313 @Override
314 public void addKeyword(String name, short value) {
315 addKeyword(name, String.valueOf(value));
316 }
317
318 @Override
319 public void addKeyword(String name, Short value) {
320 addKeyword(name, String.valueOf(value));
321 }
322
323 @Override
324 public void addKeyword(String name, short[] values) {
325 if (values == null) {
326 return;
327 }
328
329 addKeyword(name, ArrayUtil.toStringArray(values));
330 }
331
332 @Override
333 public void addKeyword(String name, Short[] values) {
334 if (values == null) {
335 return;
336 }
337
338 addKeyword(name, ArrayUtil.toStringArray(values));
339 }
340
341 @Override
342 public void addKeyword(String name, String value) {
343 addKeyword(name, value, false);
344 }
345
346 @Override
347 public void addKeyword(String name, String value, boolean lowerCase) {
348 if (lowerCase && Validator.isNotNull(value)) {
349 value = StringUtil.toLowerCase(value);
350 }
351
352 Field field = new Field(name, value);
353
354 for (String fieldName : Field.UNSCORED_FIELD_NAMES) {
355 if (StringUtil.equalsIgnoreCase(name, fieldName)) {
356 field.setBoost(0);
357 }
358 }
359
360 _fields.put(name, field);
361 }
362
363 @Override
364 public void addKeyword(String name, String[] values) {
365 if (values == null) {
366 return;
367 }
368
369 Field field = new Field(name, values);
370
371 _fields.put(name, field);
372 }
373
374 @Override
375 public void addLocalizedKeyword(String name, Map<Locale, String> values) {
376 addLocalizedKeyword(name, values, false);
377 }
378
379 @Override
380 public void addLocalizedKeyword(
381 String name, Map<Locale, String> values, boolean lowerCase) {
382
383 if ((values == null) || values.isEmpty()) {
384 return;
385 }
386
387 if (lowerCase) {
388 Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
389 values.size());
390
391 for (Map.Entry<Locale, String> entry : values.entrySet()) {
392 String value = GetterUtil.getString(entry.getValue());
393
394 lowerCaseValues.put(
395 entry.getKey(), StringUtil.toLowerCase(value));
396 }
397
398 values = lowerCaseValues;
399 }
400
401 Field field = new Field(name, values);
402
403 _fields.put(name, field);
404 }
405
406 @Override
407 public void addLocalizedKeyword(
408 String name, Map<Locale, String> values, boolean lowerCase,
409 boolean sortable) {
410
411 if ((values == null) || values.isEmpty()) {
412 return;
413 }
414
415 if (lowerCase) {
416 Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
417 values.size());
418
419 for (Map.Entry<Locale, String> entry : values.entrySet()) {
420 String value = GetterUtil.getString(entry.getValue());
421
422 lowerCaseValues.put(
423 entry.getKey(), StringUtil.toLowerCase(value));
424 }
425
426 values = lowerCaseValues;
427 }
428
429 Field field = new Field(name, values);
430
431 field.setSortable(sortable);
432
433 _fields.put(name, field);
434 }
435
436 @Override
437 public void addLocalizedText(String name, Map<Locale, String> values) {
438 if ((values == null) || values.isEmpty()) {
439 return;
440 }
441
442 Field field = new Field(name, values);
443
444 field.setTokenized(true);
445
446 _fields.put(name, field);
447 }
448
449
452 @Override
453 public void addModifiedDate() {
454 addModifiedDate(new Date());
455 }
456
457
460 @Override
461 public void addModifiedDate(Date modifiedDate) {
462 addDate(Field.MODIFIED, modifiedDate);
463 }
464
465 @Override
466 public void addNumber(String name, double value) {
467 addNumber(name, String.valueOf(value), Double.class);
468 }
469
470 @Override
471 public void addNumber(String name, Double value) {
472 addNumber(name, String.valueOf(value), Double.class);
473 }
474
475 @Override
476 public void addNumber(String name, double[] values) {
477 addNumber(name, ArrayUtil.toStringArray(values), Double.class);
478 }
479
480 @Override
481 public void addNumber(String name, Double[] values) {
482 addNumber(name, ArrayUtil.toStringArray(values), Double.class);
483 }
484
485 @Override
486 public void addNumber(String name, float value) {
487 addNumber(name, String.valueOf(value), Float.class);
488 }
489
490 @Override
491 public void addNumber(String name, Float value) {
492 addNumber(name, String.valueOf(value), Float.class);
493 }
494
495 @Override
496 public void addNumber(String name, float[] values) {
497 addNumber(name, ArrayUtil.toStringArray(values), Float.class);
498 }
499
500 @Override
501 public void addNumber(String name, Float[] values) {
502 addNumber(name, ArrayUtil.toStringArray(values), Float.class);
503 }
504
505 @Override
506 public void addNumber(String name, int value) {
507 addNumber(name, String.valueOf(value), Integer.class);
508 }
509
510 @Override
511 public void addNumber(String name, int[] values) {
512 addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
513 }
514
515 @Override
516 public void addNumber(String name, Integer value) {
517 addNumber(name, String.valueOf(value), Integer.class);
518 }
519
520 @Override
521 public void addNumber(String name, Integer[] values) {
522 addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
523 }
524
525 @Override
526 public void addNumber(String name, long value) {
527 addNumber(name, String.valueOf(value), Long.class);
528 }
529
530 @Override
531 public void addNumber(String name, Long value) {
532 addNumber(name, String.valueOf(value), Long.class);
533 }
534
535 @Override
536 public void addNumber(String name, long[] values) {
537 addNumber(name, ArrayUtil.toStringArray(values), Long.class);
538 }
539
540 @Override
541 public void addNumber(String name, Long[] values) {
542 addNumber(name, ArrayUtil.toStringArray(values), Long.class);
543 }
544
545 @Override
546 public void addNumber(String name, String value) {
547 addNumber(name, value, Long.class);
548 }
549
550 public void addNumber(
551 String name, String value, Class<? extends Number> clazz) {
552
553 if (Validator.isNull(value)) {
554 return;
555 }
556
557 addNumber(name, new String[] {value}, clazz);
558 }
559
560 @Override
561 public void addNumber(String name, String[] values) {
562 addNumber(name, values, Long.class);
563 }
564
565 public void addNumber(
566 String name, String[] values, Class<? extends Number> clazz) {
567
568 if (values == null) {
569 return;
570 }
571
572 String sortableFieldName = getSortableFieldName(name);
573
574 Field field = new Field(sortableFieldName, values);
575
576 field.setNumeric(true);
577 field.setNumericClass(clazz);
578
579 _fields.put(sortableFieldName, field);
580
581 addKeyword(name, values);
582 }
583
584 @Override
585 public void addText(String name, String value) {
586 if (Validator.isNull(value)) {
587 return;
588 }
589
590 Field field = new Field(name, value);
591
592 field.setTokenized(true);
593
594 _fields.put(name, field);
595
596 if (_sortableTextFields.contains(name)) {
597 String truncatedValue = value;
598
599 if (value.length() > _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH) {
600 truncatedValue = value.substring(
601 0, _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH);
602 }
603
604 addKeyword(getSortableFieldName(name), truncatedValue, true);
605 }
606 }
607
608 @Override
609 public void addText(String name, String[] values) {
610 if (values == null) {
611 return;
612 }
613
614 Field field = new Field(name, values);
615
616 field.setTokenized(true);
617
618 _fields.put(name, field);
619 }
620
621 @Override
622 public void addUID(String portletId, long field1) {
623 addUID(portletId, String.valueOf(field1));
624 }
625
626 @Override
627 public void addUID(String portletId, long field1, String field2) {
628 addUID(portletId, String.valueOf(field1), field2);
629 }
630
631 @Override
632 public void addUID(String portletId, Long field1) {
633 addUID(portletId, field1.longValue());
634 }
635
636 @Override
637 public void addUID(String portletId, Long field1, String field2) {
638 addUID(portletId, field1.longValue(), field2);
639 }
640
641 @Override
642 public void addUID(String portletId, String field1) {
643 addUID(portletId, field1, null);
644 }
645
646 @Override
647 public void addUID(String portletId, String field1, String field2) {
648 addUID(portletId, field1, field2, null);
649 }
650
651 @Override
652 public void addUID(
653 String portletId, String field1, String field2, String field3) {
654
655 addUID(portletId, field1, field2, field3, null);
656 }
657
658 @Override
659 public void addUID(
660 String portletId, String field1, String field2, String field3,
661 String field4) {
662
663 String uid = portletId + _UID_PORTLET + field1;
664
665 if (field2 != null) {
666 uid += _UID_FIELD + field2;
667 }
668
669 if (field3 != null) {
670 uid += _UID_FIELD + field3;
671 }
672
673 if (field4 != null) {
674 uid += _UID_FIELD + field4;
675 }
676
677 addKeyword(Field.UID, uid);
678 }
679
680 @Override
681 public Object clone() {
682 DocumentImpl documentImpl = new DocumentImpl();
683
684 documentImpl.setSortableTextFields(_sortableTextFields);
685
686 return documentImpl;
687 }
688
689 @Override
690 public String get(Locale locale, String name) {
691 if (locale == null) {
692 return get(name);
693 }
694
695 String localizedName = getLocalizedName(locale, name);
696
697 Field field = _fields.get(localizedName);
698
699 if (field == null) {
700 field = _fields.get(name);
701 }
702
703 if (field == null) {
704 return StringPool.BLANK;
705 }
706
707 return field.getValue();
708 }
709
710 @Override
711 public String get(Locale locale, String name, String defaultName) {
712 if (locale == null) {
713 return get(name, defaultName);
714 }
715
716 String localizedName = getLocalizedName(locale, name);
717
718 Field field = _fields.get(localizedName);
719
720 if (field == null) {
721 localizedName = getLocalizedName(locale, defaultName);
722
723 field = _fields.get(localizedName);
724 }
725
726 if (field == null) {
727 return StringPool.BLANK;
728 }
729
730 return field.getValue();
731 }
732
733 @Override
734 public String get(String name) {
735 Field field = _fields.get(name);
736
737 if (field == null) {
738 return StringPool.BLANK;
739 }
740
741 return field.getValue();
742 }
743
744 @Override
745 public String get(String name, String defaultName) {
746 Field field = _fields.get(name);
747
748 if (field == null) {
749 return get(defaultName);
750 }
751
752 return field.getValue();
753 }
754
755 @Override
756 public Date getDate(String name) throws ParseException {
757 DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
758 _INDEX_DATE_FORMAT_PATTERN);
759
760 return dateFormat.parse(get(name));
761 }
762
763 @Override
764 public Field getField(String name) {
765 return _fields.get(name);
766 }
767
768 @Override
769 public Map<String, Field> getFields() {
770 return _fields;
771 }
772
773 @Override
774 public String getPortletId() {
775 String uid = getUID();
776
777 int pos = uid.indexOf(_UID_PORTLET);
778
779 return uid.substring(0, pos);
780 }
781
782 @Override
783 public String getUID() {
784 Field field = _fields.get(Field.UID);
785
786 if (field == null) {
787 throw new RuntimeException("UID is not set");
788 }
789
790 return field.getValue();
791 }
792
793 @Override
794 public String[] getValues(String name) {
795 Field field = _fields.get(name);
796
797 if (field == null) {
798 return new String[] {StringPool.BLANK};
799 }
800
801 return field.getValues();
802 }
803
804 @Override
805 public boolean hasField(String name) {
806 if (_fields.containsKey(name)) {
807 return true;
808 }
809
810 return false;
811 }
812
813 @Override
814 public boolean isDocumentSortableTextField(String name) {
815 return _sortableTextFields.contains(name);
816 }
817
818 @Override
819 public void remove(String name) {
820 _fields.remove(name);
821 }
822
823 public void setFields(Map<String, Field> fields) {
824 _fields = fields;
825 }
826
827 @Override
828 public void setSortableTextFields(String[] sortableTextFields) {
829 _sortableTextFields = SetUtil.fromArray(sortableTextFields);
830 }
831
832 @Override
833 public String toString() {
834 StringBundler sb = new StringBundler();
835
836 sb.append(StringPool.OPEN_CURLY_BRACE);
837
838 boolean firstField = true;
839
840 for (Field field : _fields.values()) {
841 if (!firstField) {
842 sb.append(StringPool.COMMA);
843 sb.append(StringPool.SPACE);
844 }
845 else {
846 firstField = false;
847 }
848
849 sb.append(field.getName());
850 sb.append(StringPool.EQUAL);
851 sb.append(Arrays.toString(field.getValues()));
852 }
853
854 sb.append(StringPool.CLOSE_CURLY_BRACE);
855
856 return sb.toString();
857 }
858
859 protected void setSortableTextFields(Set<String> sortableTextFields) {
860 _sortableTextFields = sortableTextFields;
861 }
862
863 private static final String _INDEX_DATE_FORMAT_PATTERN = PropsUtil.get(
864 PropsKeys.INDEX_DATE_FORMAT_PATTERN);
865
866 private static final String _SORTABLE_FIELD_SUFFIX = "sortable";
867
868 private static final int _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH =
869 GetterUtil.getInteger(
870 PropsUtil.get(
871 PropsKeys.INDEX_SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH));
872
873 private static final String _UID_FIELD = "_FIELD_";
874
875 private static final String _UID_PORTLET = "_PORTLET_";
876
877 private static Format _dateFormat =
878 FastDateFormatFactoryUtil.getSimpleDateFormat(
879 _INDEX_DATE_FORMAT_PATTERN);
880 private static Set<String> _defaultSortableTextFields = SetUtil.fromArray(
881 PropsUtil.getArray(PropsKeys.INDEX_SORTABLE_TEXT_FIELDS));
882
883 private Map<String, Field> _fields = new HashMap<String, Field>();
884 private Set<String> _sortableTextFields = _defaultSortableTextFields;
885
886 }