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