1919
2020package org .apache .lucene .queryParser ;
2121
22+ import org .apache .lucene .analysis .TokenStream ;
23+ import org .apache .lucene .analysis .tokenattributes .TermAttribute ;
2224import org .apache .lucene .index .Term ;
2325import org .apache .lucene .search .BooleanClause ;
2426import org .apache .lucene .search .MultiTermQuery ;
2527import org .apache .lucene .search .Query ;
2628import org .elasticsearch .common .collect .ImmutableMap ;
29+ import org .elasticsearch .common .io .FastStringReader ;
2730import org .elasticsearch .common .lucene .Lucene ;
2831import org .elasticsearch .common .lucene .search .Queries ;
2932import org .elasticsearch .index .mapper .AllFieldMapper ;
3235import org .elasticsearch .index .mapper .MapperService ;
3336import org .elasticsearch .index .query .xcontent .QueryParseContext ;
3437
38+ import java .io .IOException ;
39+ import java .io .StringReader ;
40+ import java .util .ArrayList ;
3541import java .util .List ;
3642
3743import static org .elasticsearch .common .lucene .search .Queries .*;
@@ -61,6 +67,8 @@ public class MapperQueryParser extends QueryParser {
6167
6268 private FieldMapper currentMapper ;
6369
70+ private boolean analyzeWildcard ;
71+
6472 public MapperQueryParser (QueryParseContext parseContext ) {
6573 super (Lucene .QUERYPARSER_VERSION , null , null );
6674 this .parseContext = parseContext ;
@@ -83,6 +91,7 @@ public void reset(QueryParserSettings settings) {
8391 setDefaultOperator (settings .defaultOperator ());
8492 setFuzzyMinSim (settings .fuzzyMinSim ());
8593 setFuzzyPrefixLength (settings .fuzzyPrefixLength ());
94+ this .analyzeWildcard = settings .analyzeWildcard ();
8695 }
8796
8897 @ Override protected Query newTermQuery (Term term ) {
@@ -145,7 +154,7 @@ public void reset(QueryParserSettings settings) {
145154 return newRangeQuery (field , part1 , part2 , inclusive );
146155 }
147156
148- @ Override protected Query getPrefixQuery (String field , String termStr ) throws ParseException {
157+ @ Override protected Query getFuzzyQuery (String field , String termStr , float minSimilarity ) throws ParseException {
149158 String indexedNameField = field ;
150159 currentMapper = null ;
151160 if (parseContext .mapperService () != null ) {
@@ -155,13 +164,13 @@ public void reset(QueryParserSettings settings) {
155164 if (currentMapper != null ) {
156165 indexedNameField = currentMapper .names ().indexName ();
157166 }
158- return wrapSmartNameQuery (super .getPrefixQuery (indexedNameField , termStr ), fieldMappers , parseContext );
167+ return wrapSmartNameQuery (super .getFuzzyQuery (indexedNameField , termStr , minSimilarity ), fieldMappers , parseContext );
159168 }
160169 }
161- return super .getPrefixQuery (indexedNameField , termStr );
170+ return super .getFuzzyQuery (indexedNameField , termStr , minSimilarity );
162171 }
163172
164- @ Override protected Query getFuzzyQuery (String field , String termStr , float minSimilarity ) throws ParseException {
173+ @ Override protected Query getPrefixQuery (String field , String termStr ) throws ParseException {
165174 String indexedNameField = field ;
166175 currentMapper = null ;
167176 if (parseContext .mapperService () != null ) {
@@ -171,10 +180,53 @@ public void reset(QueryParserSettings settings) {
171180 if (currentMapper != null ) {
172181 indexedNameField = currentMapper .names ().indexName ();
173182 }
174- return wrapSmartNameQuery (super . getFuzzyQuery (indexedNameField , termStr , minSimilarity ), fieldMappers , parseContext );
183+ return wrapSmartNameQuery (getPossiblyAnalyzedPrefixQuery (indexedNameField , termStr ), fieldMappers , parseContext );
175184 }
176185 }
177- return super .getFuzzyQuery (indexedNameField , termStr , minSimilarity );
186+ return getPossiblyAnalyzedPrefixQuery (indexedNameField , termStr );
187+ }
188+
189+ private Query getPossiblyAnalyzedPrefixQuery (String field , String termStr ) throws ParseException {
190+ if (!analyzeWildcard ) {
191+ return super .getPrefixQuery (field , termStr );
192+ }
193+ // LUCENE MONITOR: TermAttribute deprecated in 3.1
194+ // get Analyzer from superclass and tokenize the term
195+ TokenStream source = null ;
196+ try {
197+ source = getAnalyzer ().reusableTokenStream (field , new StringReader (termStr ));
198+ } catch (IOException e ) {
199+ return super .getPrefixQuery (field , termStr );
200+ }
201+ List <String > tlist = new ArrayList <String >();
202+ TermAttribute termAtt = source .addAttribute (TermAttribute .class );
203+
204+ while (true ) {
205+ try {
206+ if (!source .incrementToken ()) break ;
207+ } catch (IOException e ) {
208+ break ;
209+ }
210+ tlist .add (termAtt .term ());
211+ }
212+
213+ try {
214+ source .close ();
215+ } catch (IOException e ) {
216+ // ignore
217+ }
218+
219+ if (tlist .size () == 1 ) {
220+ return super .getPrefixQuery (field , tlist .get (0 ));
221+ } else {
222+ return super .getPrefixQuery (field , termStr );
223+ /* this means that the analyzer used either added or consumed
224+ * (common for a stemmer) tokens, and we can't build a PrefixQuery */
225+ // throw new ParseException("Cannot build PrefixQuery with analyzer "
226+ // + getAnalyzer().getClass()
227+ // + (tlist.size() > 1 ? " - token(s) added" : " - token consumed"));
228+ }
229+
178230 }
179231
180232 @ Override protected Query getWildcardQuery (String field , String termStr ) throws ParseException {
@@ -190,10 +242,74 @@ public void reset(QueryParserSettings settings) {
190242 if (currentMapper != null ) {
191243 indexedNameField = currentMapper .names ().indexName ();
192244 }
193- return wrapSmartNameQuery (super .getWildcardQuery (indexedNameField , termStr ), fieldMappers , parseContext );
245+ return wrapSmartNameQuery (getPossiblyAnalyzedWildcardQuery (indexedNameField , termStr ), fieldMappers , parseContext );
246+ }
247+ }
248+ return getPossiblyAnalyzedWildcardQuery (indexedNameField , termStr );
249+ }
250+
251+ private Query getPossiblyAnalyzedWildcardQuery (String field , String termStr ) throws ParseException {
252+ if (!analyzeWildcard ) {
253+ return super .getWildcardQuery (field , termStr );
254+ }
255+ boolean isWithinToken = (!termStr .startsWith ("?" ) && !termStr .startsWith ("*" ));
256+ StringBuilder aggStr = new StringBuilder ();
257+ StringBuilder tmp = new StringBuilder ();
258+ for (int i = 0 ; i < termStr .length (); i ++) {
259+ char c = termStr .charAt (i );
260+ if (c == '?' || c == '*' ) {
261+ if (isWithinToken ) {
262+ try {
263+ TokenStream source = getAnalyzer ().reusableTokenStream (field , new FastStringReader (tmp .toString ()));
264+ TermAttribute termAtt = source .addAttribute (TermAttribute .class );
265+ if (source .incrementToken ()) {
266+ String term = termAtt .term ();
267+ if (term .length () == 0 ) {
268+ // no tokens, just use what we have now
269+ aggStr .append (tmp );
270+ } else {
271+ aggStr .append (term );
272+ }
273+ } else {
274+ // no tokens, just use what we have now
275+ aggStr .append (tmp );
276+ }
277+ source .close ();
278+ } catch (IOException e ) {
279+ aggStr .append (tmp );
280+ }
281+ tmp .setLength (0 );
282+ }
283+ isWithinToken = false ;
284+ aggStr .append (c );
285+ } else {
286+ tmp .append (c );
287+ isWithinToken = true ;
288+ }
289+ }
290+ if (isWithinToken ) {
291+ try {
292+ TokenStream source = getAnalyzer ().reusableTokenStream (field , new FastStringReader (tmp .toString ()));
293+ TermAttribute termAtt = source .addAttribute (TermAttribute .class );
294+ if (source .incrementToken ()) {
295+ String term = termAtt .term ();
296+ if (term .length () == 0 ) {
297+ // no tokens, just use what we have now
298+ aggStr .append (tmp );
299+ } else {
300+ aggStr .append (term );
301+ }
302+ } else {
303+ // no tokens, just use what we have now
304+ aggStr .append (tmp );
305+ }
306+ source .close ();
307+ } catch (IOException e ) {
308+ aggStr .append (tmp );
194309 }
195310 }
196- return super .getWildcardQuery (indexedNameField , termStr );
311+
312+ return super .getWildcardQuery (field , aggStr .toString ());
197313 }
198314
199315 @ Override protected Query getBooleanQuery (List <BooleanClause > clauses , boolean disableCoord ) throws ParseException {
0 commit comments