1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.search;
16  
17  import com.liferay.portal.NoSuchModelException;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.util.Validator;
21  import com.liferay.portal.model.Group;
22  import com.liferay.portal.service.GroupLocalServiceUtil;
23  import com.liferay.portlet.asset.service.AssetCategoryServiceUtil;
24  
25  /**
26   * <a href="BaseIndexer.java.html"><b><i>View Source</i></b></a>
27   *
28   * @author Brian Wing Shun Chan
29   */
30  public abstract class BaseIndexer implements Indexer {
31  
32      public void delete(Object obj) throws SearchException {
33          try {
34              doDelete(obj);
35          }
36          catch (SearchException se) {
37              throw se;
38          }
39          catch (Exception e) {
40              throw new SearchException(e);
41          }
42      }
43  
44      public Document getDocument(Object obj) throws SearchException {
45          try {
46              return doGetDocument(obj);
47          }
48          catch (SearchException se) {
49              throw se;
50          }
51          catch (Exception e) {
52              throw new SearchException(e);
53          }
54      }
55  
56      public void reindex(Object obj) throws SearchException {
57          try {
58              if (SearchEngineUtil.isIndexReadOnly()) {
59                  return;
60              }
61  
62              doReindex(obj);
63          }
64          catch (SearchException se) {
65              throw se;
66          }
67          catch (Exception e) {
68              throw new SearchException(e);
69          }
70      }
71  
72      public void reindex(String className, long classPK) throws SearchException {
73          try {
74              if (SearchEngineUtil.isIndexReadOnly()) {
75                  return;
76              }
77  
78              doReindex(className, classPK);
79          }
80          catch (NoSuchModelException nsme) {
81              if (_log.isWarnEnabled()) {
82                  _log.warn("Unable to index " + className + " " + classPK);
83              }
84          }
85          catch (SearchException se) {
86              throw se;
87          }
88          catch (Exception e) {
89              throw new SearchException(e);
90          }
91      }
92  
93      public void reindex(String[] ids) throws SearchException {
94          try {
95              if (SearchEngineUtil.isIndexReadOnly()) {
96                  return;
97              }
98  
99              doReindex(ids);
100         }
101         catch (SearchException se) {
102             throw se;
103         }
104         catch (Exception e) {
105             throw new SearchException(e);
106         }
107     }
108 
109     public Hits search(SearchContext searchContext) throws SearchException {
110         try {
111             String className = getClassName(searchContext);
112 
113             BooleanQuery contextQuery = BooleanQueryFactoryUtil.create();
114 
115             addSearchAssetCategoryIds(contextQuery, searchContext);
116             addSearchAssetTagNames(contextQuery, searchContext);
117             addSearchGroupId(contextQuery, searchContext);
118             addSearchOwnerUserId(contextQuery, searchContext);
119             addSearchCategoryIds(contextQuery, searchContext);
120             addSearchNodeIds(contextQuery, searchContext);
121             addSearchFolderIds(contextQuery, searchContext);
122             addSearchPortletIds(contextQuery, searchContext);
123 
124             BooleanQuery fullQuery = createFullQuery(
125                 contextQuery, searchContext);
126 
127             return SearchEngineUtil.search(
128                 searchContext.getCompanyId(), searchContext.getGroupIds(),
129                 searchContext.getUserId(), className, fullQuery,
130                 searchContext.getSorts(), searchContext.getStart(),
131                 searchContext.getEnd());
132         }
133         catch (SearchException se) {
134             throw se;
135         }
136         catch (Exception e) {
137             throw new SearchException(e);
138         }
139     }
140 
141     protected void addSearchAssetCategoryIds(
142             BooleanQuery contextQuery, SearchContext searchContext)
143         throws Exception {
144 
145         long[] assetCategoryIds = searchContext.getAssetCategoryIds();
146 
147         if ((assetCategoryIds == null) || (assetCategoryIds.length == 0)) {
148             return;
149         }
150 
151         BooleanQuery assetCategoryIdsQuery = BooleanQueryFactoryUtil.create();
152 
153         for (long assetCategoryId : assetCategoryIds) {
154             if (searchContext.getUserId() > 0) {
155                 try {
156                     AssetCategoryServiceUtil.getCategory(assetCategoryId);
157                 }
158                 catch (Exception e) {
159                     continue;
160                 }
161             }
162 
163             TermQuery termQuery = TermQueryFactoryUtil.create(
164                 Field.ASSET_CATEGORY_IDS, assetCategoryId);
165 
166              assetCategoryIdsQuery.add(termQuery, BooleanClauseOccur.MUST);
167         }
168 
169         if (!assetCategoryIdsQuery.clauses().isEmpty()) {
170             contextQuery.add(assetCategoryIdsQuery, BooleanClauseOccur.MUST);
171         }
172     }
173 
174     protected void addSearchAssetTagNames(
175             BooleanQuery contextQuery, SearchContext searchContext)
176         throws Exception {
177 
178         String[] assetTagNames = searchContext.getAssetTagNames();
179 
180         if ((assetTagNames == null) || (assetTagNames.length == 0)) {
181             return;
182         }
183 
184         BooleanQuery assetTagNamesQuery = BooleanQueryFactoryUtil.create();
185 
186         for (String assetTagName : assetTagNames) {
187             TermQuery termQuery = TermQueryFactoryUtil.create(
188                 Field.ASSET_TAG_NAMES, assetTagName);
189 
190             assetTagNamesQuery.add(termQuery, BooleanClauseOccur.MUST);
191         }
192 
193         if (!assetTagNamesQuery.clauses().isEmpty()) {
194             contextQuery.add(assetTagNamesQuery, BooleanClauseOccur.MUST);
195         }
196     }
197 
198     protected void addSearchCategoryIds(
199             BooleanQuery contextQuery, SearchContext searchContext)
200         throws Exception {
201 
202         long[] categoryIds = searchContext.getCategoryIds();
203 
204         if ((categoryIds == null) || (categoryIds.length == 0)) {
205             return;
206         }
207 
208         BooleanQuery categoryIdsQuery = BooleanQueryFactoryUtil.create();
209 
210         for (long categoryId : categoryIds) {
211             if (searchContext.getUserId() > 0) {
212                 try {
213                     checkSearchCategoryId(categoryId, searchContext);
214                 }
215                 catch (Exception e) {
216                     continue;
217                 }
218             }
219 
220             TermQuery termQuery = TermQueryFactoryUtil.create(
221                 Field.CATEGORY_ID, categoryId);
222 
223             categoryIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
224         }
225 
226         if (!categoryIdsQuery.clauses().isEmpty()) {
227             contextQuery.add(categoryIdsQuery, BooleanClauseOccur.MUST);
228         }
229     }
230 
231     protected void addSearchFolderIds(
232             BooleanQuery contextQuery, SearchContext searchContext)
233         throws Exception {
234 
235         long[] folderIds = searchContext.getFolderIds();
236 
237         if ((folderIds == null) || (folderIds.length == 0)) {
238             return;
239         }
240 
241         BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create();
242 
243         for (long folderId : folderIds) {
244             if (searchContext.getUserId() > 0) {
245                 try {
246                     checkSearchFolderId(folderId, searchContext);
247                 }
248                 catch (Exception e) {
249                     continue;
250                 }
251             }
252 
253             TermQuery termQuery = TermQueryFactoryUtil.create(
254                 Field.FOLDER_ID, folderId);
255 
256             folderIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
257         }
258 
259         if (!folderIdsQuery.clauses().isEmpty()) {
260             contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
261         }
262     }
263 
264     protected void addSearchGroupId(
265             BooleanQuery contextQuery, SearchContext searchContext)
266         throws Exception {
267 
268         long[] groupIds = searchContext.getGroupIds();
269 
270         if ((groupIds == null) || (groupIds.length == 0) ||
271             ((groupIds.length == 1) && (groupIds[0] == 0))){
272 
273             return;
274         }
275 
276         BooleanQuery groupIdsQuery = BooleanQueryFactoryUtil.create();
277 
278         for (int i = 0; i < groupIds.length; i ++) {
279             long groupId = groupIds[i];
280 
281             if (groupId <= 0) {
282                 continue;
283             }
284 
285             try {
286                 Group group = GroupLocalServiceUtil.getGroup(groupId);
287 
288                 long parentGroupId = groupId;
289 
290                 if (group.isLayout() || searchContext.isScopeStrict()) {
291                     contextQuery.addRequiredTerm(
292                         Field.SCOPE_GROUP_ID, groupId);
293                 }
294 
295                 if (group.isLayout()) {
296                     parentGroupId = group.getParentGroupId();
297                 }
298 
299                 contextQuery.addRequiredTerm(Field.GROUP_ID, parentGroupId);
300 
301                 groupIds[i] = parentGroupId;
302             }
303             catch (Exception e) {
304                 continue;
305             }
306 
307             TermQuery termQuery = TermQueryFactoryUtil.create(
308                 Field.GROUP_ID, groupId);
309 
310             groupIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
311         }
312 
313         searchContext.setGroupIds(groupIds);
314 
315         if (!groupIdsQuery.clauses().isEmpty()) {
316             contextQuery.add(groupIdsQuery, BooleanClauseOccur.MUST);
317         }
318     }
319 
320     protected void addSearchKeywords(
321             BooleanQuery searchQuery, SearchContext searchContext)
322         throws Exception {
323 
324         String keywords = searchContext.getKeywords();
325 
326         if (Validator.isNull(keywords)) {
327             return;
328         }
329 
330         searchQuery.addTerms(_KEYWORDS_FIELDS, keywords);
331     }
332 
333     protected void addSearchNodeIds(
334             BooleanQuery contextQuery, SearchContext searchContext)
335         throws Exception {
336 
337         long[] nodeIds = searchContext.getNodeIds();
338 
339         if ((nodeIds == null) || (nodeIds.length == 0)) {
340             return;
341         }
342 
343         BooleanQuery nodeIdsQuery = BooleanQueryFactoryUtil.create();
344 
345         for (long nodeId : nodeIds) {
346             if (searchContext.getUserId() > 0) {
347                 try {
348                     checkSearchNodeId(nodeId, searchContext);
349                 }
350                 catch (Exception e) {
351                     continue;
352                 }
353             }
354 
355             TermQuery termQuery = TermQueryFactoryUtil.create(
356                 Field.NODE_ID, nodeId);
357 
358             nodeIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
359         }
360 
361         if (!nodeIdsQuery.clauses().isEmpty()) {
362             contextQuery.add(nodeIdsQuery, BooleanClauseOccur.MUST);
363         }
364     }
365 
366     protected void addSearchOwnerUserId(
367         BooleanQuery contextQuery, SearchContext searchContext) {
368 
369         long ownerUserId = searchContext.getOwnerUserId();
370 
371         if (ownerUserId > 0) {
372             contextQuery.addRequiredTerm(Field.USER_ID, ownerUserId);
373         }
374     }
375 
376     protected void addSearchPortletIds(
377             BooleanQuery contextQuery, SearchContext searchContext)
378         throws Exception {
379 
380         String[] portletIds = searchContext.getPortletIds();
381 
382         if ((portletIds == null) || (portletIds.length == 0)) {
383             contextQuery.addRequiredTerm(
384                 Field.PORTLET_ID, getPortletId(searchContext));
385         }
386         else {
387             BooleanQuery portletIdsQuery = BooleanQueryFactoryUtil.create();
388 
389             for (String portletId : portletIds) {
390                 if (Validator.isNull(portletId)) {
391                     continue;
392                 }
393 
394                 TermQuery termQuery = TermQueryFactoryUtil.create(
395                     Field.PORTLET_ID, portletId);
396 
397                 portletIdsQuery.add(termQuery, BooleanClauseOccur.SHOULD);
398             }
399 
400             if (!portletIdsQuery.clauses().isEmpty()) {
401                 contextQuery.add(portletIdsQuery, BooleanClauseOccur.MUST);
402             }
403         }
404     }
405 
406     protected void checkSearchCategoryId(
407             long categoryId, SearchContext searchContext)
408         throws Exception {
409     }
410 
411     protected void checkSearchFolderId(
412             long folderId, SearchContext searchContext)
413         throws Exception {
414     }
415 
416     protected void checkSearchNodeId(
417             long nodeId, SearchContext searchContext)
418         throws Exception {
419     }
420 
421     protected BooleanQuery createFullQuery(
422             BooleanQuery contextQuery, SearchContext searchContext)
423         throws Exception {
424 
425         postProcessContextQuery(contextQuery, searchContext);
426 
427         BooleanQuery searchQuery = BooleanQueryFactoryUtil.create();
428 
429         addSearchKeywords(searchQuery, searchContext);
430         postProcessSearchQuery(searchQuery, searchContext);
431 
432         BooleanQuery fullQuery = BooleanQueryFactoryUtil.create();
433 
434         fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
435 
436         if (!searchQuery.clauses().isEmpty()) {
437             fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
438         }
439 
440         BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
441 
442         if (booleanClauses != null) {
443             for (BooleanClause booleanClause : booleanClauses) {
444                 fullQuery.add(
445                     booleanClause.getQuery(),
446                     booleanClause.getBooleanClauseOccur());
447             }
448         }
449 
450         postProcessFullQuery(fullQuery, searchContext);
451 
452         return fullQuery;
453     }
454 
455     protected abstract void doDelete(Object obj) throws Exception;
456 
457     protected abstract Document doGetDocument(Object obj) throws Exception;
458 
459     protected abstract void doReindex(Object obj) throws Exception;
460 
461     protected abstract void doReindex(String className, long classPK)
462         throws Exception;
463 
464     protected abstract void doReindex(String[] ids) throws Exception;
465 
466     protected String getClassName(SearchContext searchContext) {
467         String[] classNames = getClassNames();
468 
469         if (classNames.length != 1) {
470             throw new UnsupportedOperationException(
471                 "Search method needs to be manually implemented for " +
472                     "indexers with more than one class name");
473         }
474 
475         return classNames[0];
476     }
477 
478     protected long getParentGroupId(long groupId) {
479         long parentGroupId = groupId;
480 
481         try {
482             Group group = GroupLocalServiceUtil.getGroup(groupId);
483 
484             if (group.isLayout()) {
485                 parentGroupId = group.getParentGroupId();
486             }
487         }
488         catch (Exception e) {
489         }
490 
491         return parentGroupId;
492     }
493 
494     protected abstract String getPortletId(SearchContext searchContext);
495 
496     protected void postProcessContextQuery(
497             BooleanQuery contextQuery, SearchContext searchContext)
498         throws Exception {
499     }
500 
501     protected void postProcessFullQuery(
502             BooleanQuery fullQuery, SearchContext searchContext)
503         throws Exception {
504     }
505 
506     protected void postProcessSearchQuery(
507             BooleanQuery searchQuery, SearchContext searchContext)
508         throws Exception {
509     }
510 
511     private static final String[] _KEYWORDS_FIELDS = {
512         Field.ASSET_TAG_NAMES, Field.COMMENTS, Field.CONTENT, Field.DESCRIPTION,
513         Field.PROPERTIES, Field.TITLE, Field.URL, Field.USER_NAME
514     };
515 
516     private static Log _log = LogFactoryUtil.getLog(BaseIndexer.class);
517 
518 }