View Javadoc

1   /* Copyright (c) 2008 Sascha Kohlmann
2    *
3    * This program is free software: you can redistribute it and/or modify
4    * it under the terms of the GNU Affero General Public License as published by
5    * the Free Software Foundation, either version 3 of the License, or
6    * (at your option) any later version.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Affero General Public License for more details.
12   *
13   * You should have received a copy of the GNU Affero General Public License
14   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15   */
16  package net.sf.eos.lucene;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.apache.lucene.analysis.Analyzer;
21  import org.apache.lucene.queryParser.ParseException;
22  import org.apache.lucene.queryParser.QueryParser;
23  import org.apache.lucene.search.Hits;
24  import org.apache.lucene.search.Query;
25  import org.apache.lucene.search.Searcher;
26  
27  import java.io.IOException;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import net.sf.eos.EosException;
32  import net.sf.eos.config.Configuration;
33  import net.sf.eos.config.Configured;
34  import net.sf.eos.entity.CommonNameResolver;
35  import net.sf.eos.lucene.DefaultEosQuery.Entry.Bool;
36  import net.sf.eos.search.EosQuery;
37  import net.sf.eos.search.LookupEntry;
38  
39  /**
40   * Use internally {@link AnalyzerSupplier}, {@link SearcherSupplier} and
41   * {@link CommonNameResolver} if configured.
42   * @author Sascha Kohlmann
43   */
44  public class DefaultEosQuery extends Configured implements CommonDocument, EosQuery {
45  
46      /** For logging. */
47      private static final Log LOG =
48          LogFactory.getLog(DefaultEosQuery.class.getName());
49  
50      final static class Entry {
51  
52          public static enum Bool {AND, OR, AND_NOT}
53  
54          private final Bool bool;
55          private final String fieldName;
56          private final String lowerBoundValue;
57          private final String upperBoundValue;
58  
59          public Entry (final Bool op,
60                        final String fieldName,
61                        final String lowerBoundValue) {
62              this(op, fieldName, lowerBoundValue, null);
63          }
64  
65          public Entry (@SuppressWarnings("hiding") final Bool op,
66                        @SuppressWarnings("hiding") final String fieldName,
67                        @SuppressWarnings("hiding") final String lowerBoundValue,
68                        @SuppressWarnings("hiding") final String upperBoundValue) {
69              this.bool = op;
70              this.fieldName = fieldName;
71              this.lowerBoundValue = lowerBoundValue;
72              this.upperBoundValue = upperBoundValue;
73          }
74  
75          public Bool getBool() {
76              return this.bool;
77          }
78  
79          public String getFieldName() {
80              return this.fieldName;
81          }
82  
83          public String getLowerBoundValue() {
84              return this.lowerBoundValue;
85          }
86  
87          public String getUpperBoundValue() {
88              return this.upperBoundValue;
89          }
90      }
91  
92      private final List<Entry> query = new ArrayList<Entry>();
93  
94      public DefaultEosQuery and(final String phrase) {
95          andMeta(FieldName.CONTENT.name(), phrase);
96          return this;
97      }
98  
99      public DefaultEosQuery andNot(final String phrase) throws EosException {
100         andNotMeta(FieldName.CONTENT.name(), phrase);
101         return this;
102     }
103 
104     public DefaultEosQuery andMeta(final String fieldName, final String value) {
105         final Entry entry = new Entry(Bool.AND, fieldName, value);
106         this.query.add(entry);
107         return this;
108     }
109 
110     public DefaultEosQuery andNotMeta(final String fieldName, final String value) {
111         final Entry entry = new Entry(Bool.AND_NOT, fieldName, value);
112         this.query.add(entry);
113         return this;
114     }
115 
116     public DefaultEosQuery andMetaRange(final String fieldName,
117                                         final String lowerBound,
118                                         final String upperBound) {
119         final Entry entry = new Entry(Bool.AND, fieldName, lowerBound, upperBound);
120         this.query.add(entry);
121         return this;
122     }
123 
124     public DefaultEosQuery or(final String phrase) {
125         orMeta(FieldName.CONTENT.name(), phrase);
126         return this;
127     }
128 
129     public DefaultEosQuery orMeta(final String fieldName, final String value) {
130         final Entry entry = new Entry(Bool.OR, fieldName, value);
131         this.query.add(entry);
132         return this;
133     }
134 
135     public DefaultEosQuery orMetaRange(final String fieldName,
136                                        final String lowerBound,
137                                        final String upperBound) {
138         final Entry entry = new Entry(Bool.OR, fieldName, lowerBound, upperBound);
139         this.query.add(entry);
140         return this;
141     }
142 
143     @SuppressWarnings("nls")
144     public List<LookupEntry> execute() throws EosException {
145 
146         final Configuration conf = getConfiguration();
147 
148         final AnalyzerSupplier analyzerProvider =
149             AnalyzerSupplier.newInstance(conf);
150         final Analyzer analyzer = analyzerProvider.get();
151         final String queryString = createLuceneQuery();
152         final QueryParser parser =
153             new QueryParser(CommonDocument.FieldName.CONTENT.name(), analyzer);
154 
155         final SearcherSupplier provider = SearcherSupplier.newInstance(conf);
156         final Searcher searcher = provider.get(conf);
157 
158         try {
159             final Query query = parser.parse(queryString);
160             final Hits hits = searcher.search(query);
161 
162         } catch (final ParseException e) {
163             final String message = e.getMessage();
164             throw new EosException(message, e);
165         } catch (final IOException e) {
166             final String message = e.getMessage();
167             throw new EosException(message, e);
168         } finally {
169             try {
170                 searcher.close();
171             } catch (final IOException e) {
172                 throw new EosException("Unable to close Searcher", e);
173             }
174         }
175 
176         return null;
177     }
178 
179     /**
180      * Creates a textual Lucene query representation.
181      * @return a textual Lucene query representation
182      */
183     final String createLuceneQuery() {
184         final StringBuilder sb = new StringBuilder();
185         final int size = this.query.size();
186         Entry[] entries = this.query.toArray(new Entry[size]);
187 
188         for (int i = 0; i < size; i++) {
189             final Bool b = entries[i].getBool();
190             if (i != 0) {
191                 if (b == Bool.AND || b == Bool.OR) {
192                     sb.append(b);
193                 } else {
194                     sb.append(Bool.AND);
195                 }
196                 sb.append(" ");
197             }
198             if (b == Bool.AND_NOT) {
199                 sb.append("-");
200             }
201             final String fieldName = entries[i].getFieldName();
202             sb.append(fieldName);
203             sb.append(":");
204 
205             final String lower = entries[i].getLowerBoundValue();
206             final String lowerEscaped = QueryParser.escape(lower);
207             final String upper = entries[i].getUpperBoundValue();
208             String upperEscaped = null;
209             if (upper != null) {
210                 upperEscaped = QueryParser.escape(upper);
211             }
212 
213             if (upper != null) {
214                 sb.append("[");
215             }
216             sb.append("\"");
217 
218             sb.append(lowerEscaped);
219             if (upper != null) {
220                 sb.append("\" TO \"");
221                 sb.append(upperEscaped);
222             }
223             sb.append("\"");
224             if (upper != null) {
225                 sb.append("]");
226             }
227             sb.append(" ");
228         }
229         final String q = sb.toString();
230         if (LOG.isDebugEnabled()) {
231             LOG.debug("QUERY: " + q);
232         }
233 
234         return q;
235     }
236 }