001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.repository.search;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.repository.search.RepositorySearchQueryBuilder;
020    import com.liferay.portal.kernel.search.BooleanClause;
021    import com.liferay.portal.kernel.search.BooleanClauseOccur;
022    import com.liferay.portal.kernel.search.BooleanQuery;
023    import com.liferay.portal.kernel.search.BooleanQueryFactoryUtil;
024    import com.liferay.portal.kernel.search.Field;
025    import com.liferay.portal.kernel.search.Query;
026    import com.liferay.portal.kernel.search.QueryTerm;
027    import com.liferay.portal.kernel.search.SearchContext;
028    import com.liferay.portal.kernel.search.SearchException;
029    import com.liferay.portal.kernel.search.TermQuery;
030    import com.liferay.portal.kernel.search.TermRangeQuery;
031    import com.liferay.portal.kernel.search.WildcardQuery;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.Validator;
035    import com.liferay.portal.search.lucene.LuceneHelperUtil;
036    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
037    import com.liferay.portlet.documentlibrary.service.DLAppServiceUtil;
038    import com.liferay.util.lucene.KeywordsUtil;
039    
040    import java.util.HashSet;
041    import java.util.Set;
042    
043    import org.apache.lucene.analysis.Analyzer;
044    import org.apache.lucene.index.Term;
045    import org.apache.lucene.queryParser.QueryParser;
046    
047    /**
048     * @author Mika Koivisto
049     */
050    public class RepositorySearchQueryBuilderImpl
051            implements RepositorySearchQueryBuilder {
052    
053            public BooleanQuery getFullQuery(SearchContext searchContext)
054                    throws SearchException {
055    
056                    try {
057                            BooleanQuery contextQuery = BooleanQueryFactoryUtil.create(
058                                    searchContext);
059    
060                            addContext(contextQuery, searchContext);
061    
062                            BooleanQuery searchQuery = BooleanQueryFactoryUtil.create(
063                                    searchContext);
064    
065                            addSearchKeywords(searchQuery, searchContext);
066    
067                            BooleanQuery fullQuery = BooleanQueryFactoryUtil.create(
068                                    searchContext);
069    
070                            if (contextQuery.hasClauses()) {
071                                    fullQuery.add(contextQuery, BooleanClauseOccur.MUST);
072                            }
073    
074                            if (searchQuery.hasClauses()) {
075                                    fullQuery.add(searchQuery, BooleanClauseOccur.MUST);
076                            }
077    
078                            BooleanClause[] booleanClauses = searchContext.getBooleanClauses();
079    
080                            if (booleanClauses != null) {
081                                    for (BooleanClause booleanClause : booleanClauses) {
082                                            fullQuery.add(
083                                                    booleanClause.getQuery(),
084                                                    booleanClause.getBooleanClauseOccur());
085                                    }
086                            }
087    
088                            fullQuery.setQueryConfig(searchContext.getQueryConfig());
089    
090                            return fullQuery;
091                    }
092                    catch (Exception e) {
093                            throw new SearchException(e);
094                    }
095            }
096    
097            public void setAnalyzer(Analyzer analyzer) {
098                    _analyzer = analyzer;
099            }
100    
101            protected void addContext(
102                            BooleanQuery contextQuery, SearchContext searchContext)
103                    throws Exception {
104    
105                    long[] folderIds = searchContext.getFolderIds();
106    
107                    if ((folderIds != null) && (folderIds.length > 0)) {
108                            if (folderIds[0] == DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
109                                    return;
110                            }
111    
112                            BooleanQuery folderIdsQuery = BooleanQueryFactoryUtil.create(
113                                    searchContext);
114    
115                            for (long folderId : folderIds) {
116                                    try {
117                                            DLAppServiceUtil.getFolder(folderId);
118                                    }
119                                    catch (Exception e) {
120                                            continue;
121                                    }
122    
123                                    folderIdsQuery.addTerm(Field.FOLDER_ID, folderId);
124                            }
125    
126                            contextQuery.add(folderIdsQuery, BooleanClauseOccur.MUST);
127                    }
128            }
129    
130            protected void addSearchKeywords(
131                            BooleanQuery searchQuery, SearchContext searchContext)
132                    throws Exception {
133    
134                    String keywords = searchContext.getKeywords();
135    
136                    if (Validator.isNull(keywords)) {
137                            return;
138                    }
139    
140                    BooleanQuery titleQuery = BooleanQueryFactoryUtil.create(searchContext);
141    
142                    addTerm(titleQuery, searchContext, Field.TITLE, keywords);
143    
144                    if (titleQuery.hasClauses() && !contains(searchQuery, titleQuery)) {
145                            searchQuery.add(titleQuery, BooleanClauseOccur.SHOULD);
146                    }
147    
148                    BooleanQuery userNameQuery = BooleanQueryFactoryUtil.create(
149                            searchContext);
150    
151                    addTerm(userNameQuery, searchContext, Field.USER_NAME, keywords);
152    
153                    if (userNameQuery.hasClauses() &&
154                            !contains(searchQuery, userNameQuery)) {
155    
156                            searchQuery.add(userNameQuery, BooleanClauseOccur.SHOULD);
157                    }
158    
159                    BooleanQuery contentQuery = BooleanQueryFactoryUtil.create(
160                            searchContext);
161    
162                    addTerm(contentQuery, searchContext, Field.CONTENT, keywords);
163    
164                    if (contentQuery.hasClauses() && !contains(searchQuery, contentQuery)) {
165                            searchQuery.add(contentQuery, BooleanClauseOccur.SHOULD);
166                    }
167            }
168    
169            protected void addTerm(
170                    BooleanQuery booleanQuery, SearchContext searchContext, String field,
171                    String value) {
172    
173                    if (Validator.isNull(value)) {
174                            return;
175                    }
176    
177                    try {
178                            QueryParser queryParser = new QueryParser(
179                                    LuceneHelperUtil.getVersion(), field, _analyzer);
180    
181                            queryParser.setAllowLeadingWildcard(true);
182                            queryParser.setLowercaseExpandedTerms(false);
183    
184                            org.apache.lucene.search.Query query = null;
185    
186                            try {
187                                    query = queryParser.parse(value);
188                            }
189                            catch (Exception e) {
190                                    query = queryParser.parse(KeywordsUtil.escape(value));
191                            }
192    
193                            translateQuery(
194                                    booleanQuery, searchContext, query,
195                                    org.apache.lucene.search.BooleanClause.Occur.SHOULD);
196                    }
197                    catch (Exception e) {
198                            _log.error(e, e);
199                    }
200            }
201    
202            protected boolean contains(Query query1, Query query2) {
203                    if (query1 instanceof BooleanQuery) {
204                            BooleanQuery booleanQuery = (BooleanQuery)query1;
205    
206                            for (com.liferay.portal.kernel.search.BooleanClause booleanClause :
207                                            booleanQuery.clauses()) {
208    
209                                    if (contains(booleanClause.getQuery(), query2)) {
210                                            return true;
211                                    }
212                            }
213    
214                            return false;
215                    }
216                    else if (query2 instanceof BooleanQuery) {
217                            BooleanQuery booleanQuery = (BooleanQuery)query2;
218    
219                            for (com.liferay.portal.kernel.search.BooleanClause booleanClause :
220                                            booleanQuery.clauses()) {
221    
222                                    if (contains(query1, booleanClause.getQuery())) {
223                                            return true;
224                                    }
225                            }
226    
227                            return false;
228                    }
229                    else if ((query1 instanceof TermQuery) &&
230                                     (query2 instanceof TermQuery)) {
231    
232                            TermQuery termQuery1 = (TermQuery)query1;
233    
234                            QueryTerm queryTerm1 = termQuery1.getQueryTerm();
235    
236                            String field1 = queryTerm1.getField();
237                            String value1 = queryTerm1.getValue();
238    
239                            TermQuery termQuery2 = (TermQuery)query2;
240    
241                            QueryTerm queryTerm2 = termQuery2.getQueryTerm();
242    
243                            String field2 = queryTerm2.getField();
244                            String value2 = queryTerm2.getValue();
245    
246                            if (field1.equals(field2) && value1.equals(value2)) {
247                                    return true;
248                            }
249                    }
250                    else if ((query1 instanceof TermRangeQuery) &&
251                                     (query2 instanceof TermRangeQuery)) {
252    
253                            TermRangeQuery termRangeQuery1 = (TermRangeQuery)query1;
254    
255                            boolean includesLower1 = termRangeQuery1.includesLower();
256                            boolean includesUpper1 = termRangeQuery1.includesUpper();
257                            String lowerTerm1 = termRangeQuery1.getLowerTerm();
258                            String upperTerm1 = termRangeQuery1.getUpperTerm();
259    
260                            TermRangeQuery termRangeQuery2 = (TermRangeQuery)query2;
261    
262                            boolean includesLower2 = termRangeQuery2.includesLower();
263                            boolean includesUpper2 = termRangeQuery2.includesUpper();
264                            String lowerTerm2 = termRangeQuery2.getLowerTerm();
265                            String upperTerm2 = termRangeQuery2.getUpperTerm();
266    
267                            if ((includesLower1 == includesLower2) &&
268                                    (includesUpper1 == includesUpper2) &&
269                                    lowerTerm1.equals(lowerTerm2) &&
270                                    upperTerm1.equals(upperTerm2)) {
271    
272                                    return true;
273                            }
274                    }
275                    else if ((query1 instanceof WildcardQuery) &&
276                                     (query2 instanceof WildcardQuery)) {
277    
278                            WildcardQuery wildcardQuery1 = (WildcardQuery)query1;
279    
280                            QueryTerm queryTerm1 = wildcardQuery1.getQueryTerm();
281    
282                            String field1 = queryTerm1.getField();
283                            String value1 = queryTerm1.getValue();
284    
285                            WildcardQuery wildcardQuery2 = (WildcardQuery)query2;
286    
287                            QueryTerm queryTerm2 = wildcardQuery2.getQueryTerm();
288    
289                            String field2 = queryTerm2.getField();
290                            String value2 = queryTerm2.getValue();
291    
292                            if (field1.equals(field2) && value1.equals(value2)) {
293                                    return true;
294                            }
295                    }
296    
297                    return false;
298            }
299    
300            protected org.apache.lucene.search.BooleanClause.Occur
301                    getBooleanClauseOccur(BooleanClauseOccur occur) {
302    
303                    if (occur.equals(BooleanClauseOccur.MUST)) {
304                            return org.apache.lucene.search.BooleanClause.Occur.MUST;
305                    }
306                    else if (occur.equals(BooleanClauseOccur.MUST_NOT)) {
307                            return org.apache.lucene.search.BooleanClause.Occur.MUST_NOT;
308                    }
309    
310                    return org.apache.lucene.search.BooleanClause.Occur.SHOULD;
311            }
312    
313            protected BooleanClauseOccur getBooleanClauseOccur(
314                    org.apache.lucene.search.BooleanClause.Occur occur) {
315    
316                    if (occur.equals(org.apache.lucene.search.BooleanClause.Occur.MUST)) {
317                            return BooleanClauseOccur.MUST;
318                    }
319                    else if (occur.equals(
320                                            org.apache.lucene.search.BooleanClause.Occur.MUST_NOT)) {
321    
322                            return BooleanClauseOccur.MUST_NOT;
323                    }
324    
325                    return BooleanClauseOccur.SHOULD;
326            }
327    
328            protected void translateQuery(
329                            BooleanQuery booleanQuery, SearchContext searchContext,
330                            org.apache.lucene.search.Query query,
331                            org.apache.lucene.search.BooleanClause.Occur occur)
332                    throws Exception {
333    
334                    BooleanClauseOccur booleanClauseOccur = getBooleanClauseOccur(occur);
335    
336                    if (query instanceof org.apache.lucene.search.TermQuery) {
337                            Set<Term> terms = new HashSet<Term>();
338    
339                            query.extractTerms(terms);
340    
341                            for (Term term : terms) {
342                                    String termValue = term.text();
343    
344                                    booleanQuery.addTerm(
345                                            term.field(), termValue, false,
346                                            getBooleanClauseOccur(occur));
347                            }
348                    }
349                    else if (query instanceof org.apache.lucene.search.BooleanQuery) {
350                            org.apache.lucene.search.BooleanQuery curBooleanQuery =
351                                    (org.apache.lucene.search.BooleanQuery)query;
352    
353                            BooleanQuery conjunctionQuery = BooleanQueryFactoryUtil.create(
354                                    searchContext);
355                            BooleanQuery disjunctionQuery = BooleanQueryFactoryUtil.create(
356                                    searchContext);
357    
358                            for (org.apache.lucene.search.BooleanClause booleanClause :
359                                            curBooleanQuery.getClauses()) {
360    
361                                    BooleanClauseOccur curBooleanClauseOccur =
362                                            getBooleanClauseOccur(booleanClause.getOccur());
363    
364                                    BooleanQuery subbooleanQuery = null;
365    
366                                    if (curBooleanClauseOccur.equals(BooleanClauseOccur.SHOULD)) {
367                                            subbooleanQuery = disjunctionQuery;
368                                    }
369                                    else {
370                                            subbooleanQuery = conjunctionQuery;
371                                    }
372    
373                                    translateQuery(
374                                            subbooleanQuery, searchContext, booleanClause.getQuery(),
375                                            booleanClause.getOccur());
376    
377                            }
378    
379                            if (conjunctionQuery.hasClauses()) {
380                                    booleanQuery.add(conjunctionQuery, BooleanClauseOccur.MUST);
381                            }
382    
383                            if (disjunctionQuery.hasClauses()) {
384                                    booleanQuery.add(disjunctionQuery, BooleanClauseOccur.SHOULD);
385                            }
386                    }
387                    else if (query instanceof org.apache.lucene.search.FuzzyQuery) {
388                            org.apache.lucene.search.FuzzyQuery fuzzyQuery =
389                                    (org.apache.lucene.search.FuzzyQuery)query;
390    
391                            Term term = fuzzyQuery.getTerm();
392    
393                            String termValue = term.text().concat(StringPool.STAR);
394    
395                            booleanQuery.addTerm(
396                                    term.field(), termValue, true, booleanClauseOccur);
397                    }
398                    else if (query instanceof org.apache.lucene.search.PhraseQuery) {
399                            org.apache.lucene.search.PhraseQuery phraseQuery =
400                                    (org.apache.lucene.search.PhraseQuery)query;
401    
402                            Term[] terms = phraseQuery.getTerms();
403    
404                            StringBundler sb = new StringBundler(terms.length * 2);
405    
406                            for (Term term : terms) {
407                                    sb.append(term.text());
408                                    sb.append(StringPool.SPACE);
409                            }
410    
411                            booleanQuery.addTerm(
412                                    terms[0].field(), sb.toString().trim(), false,
413                                    booleanClauseOccur);
414                    }
415                    else if (query instanceof org.apache.lucene.search.PrefixQuery) {
416                            org.apache.lucene.search.PrefixQuery prefixQuery =
417                                    (org.apache.lucene.search.PrefixQuery)query;
418    
419                            Term prefixTerm = prefixQuery.getPrefix();
420    
421                            String termValue = prefixTerm.text().concat(StringPool.STAR);
422    
423                            booleanQuery.addTerm(
424                                    prefixTerm.field(), termValue, true, booleanClauseOccur);
425                    }
426                    else if (query instanceof org.apache.lucene.search.TermRangeQuery) {
427                            org.apache.lucene.search.TermRangeQuery termRangeQuery =
428                                    (org.apache.lucene.search.TermRangeQuery)query;
429    
430                            booleanQuery.addRangeTerm(
431                                    termRangeQuery.getField(), termRangeQuery.getLowerTerm(),
432                                    termRangeQuery.getUpperTerm());
433                    }
434                    else if (query instanceof org.apache.lucene.search.WildcardQuery) {
435                            org.apache.lucene.search.WildcardQuery wildcardQuery =
436                                    (org.apache.lucene.search.WildcardQuery)query;
437    
438                            Term wildcardTerm = wildcardQuery.getTerm();
439    
440                            booleanQuery.addTerm(
441                                    wildcardTerm.field(), wildcardTerm.text(), true,
442                                    booleanClauseOccur);
443                    }
444                    else {
445                            if (_log.isWarnEnabled()) {
446                                    _log.warn(
447                                            "Ignoring unknown query type " + query.getClass() +
448                                                    " with query " + query);
449                            }
450                    }
451            }
452    
453            private static Log _log = LogFactoryUtil.getLog(
454                    RepositorySearchQueryBuilderImpl.class);
455    
456            private Analyzer _analyzer;
457    
458    }