|
18 | 18 | */
|
19 | 19 | package org.elasticsearch.index.query;
|
20 | 20 |
|
| 21 | +import org.apache.lucene.index.Term; |
21 | 22 | import org.apache.lucene.search.BoostQuery;
|
| 23 | +import org.apache.lucene.search.ConstantScoreQuery; |
22 | 24 | import org.apache.lucene.search.MultiTermQuery;
|
| 25 | +import org.apache.lucene.search.PrefixQuery; |
23 | 26 | import org.apache.lucene.search.Query;
|
| 27 | +import org.apache.lucene.search.TermQuery; |
| 28 | +import org.apache.lucene.search.spans.FieldMaskingSpanQuery; |
24 | 29 | import org.apache.lucene.search.spans.SpanBoostQuery;
|
25 | 30 | import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper;
|
26 | 31 | import org.apache.lucene.search.spans.SpanQuery;
|
| 32 | +import org.apache.lucene.search.spans.SpanTermQuery; |
| 33 | +import org.elasticsearch.Version; |
27 | 34 | import org.elasticsearch.common.ParseField;
|
28 | 35 | import org.elasticsearch.common.ParsingException;
|
29 | 36 | import org.elasticsearch.common.io.stream.StreamInput;
|
30 | 37 | import org.elasticsearch.common.io.stream.StreamOutput;
|
| 38 | +import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; |
31 | 39 | import org.elasticsearch.common.xcontent.XContentBuilder;
|
32 | 40 | import org.elasticsearch.common.xcontent.XContentParser;
|
| 41 | +import org.elasticsearch.index.mapper.TextFieldMapper; |
| 42 | +import org.elasticsearch.index.query.support.QueryParsers; |
33 | 43 |
|
34 | 44 | import java.io.IOException;
|
35 | 45 | import java.util.Objects;
|
@@ -124,22 +134,67 @@ public static SpanMultiTermQueryBuilder fromXContent(XContentParser parser) thro
|
124 | 134 | protected Query doToQuery(QueryShardContext context) throws IOException {
|
125 | 135 | Query subQuery = multiTermQueryBuilder.toQuery(context);
|
126 | 136 | float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
127 |
| - if (subQuery instanceof BoostQuery) { |
128 |
| - BoostQuery boostQuery = (BoostQuery) subQuery; |
129 |
| - subQuery = boostQuery.getQuery(); |
130 |
| - boost = boostQuery.getBoost(); |
| 137 | + while (true) { |
| 138 | + if (subQuery instanceof ConstantScoreQuery) { |
| 139 | + subQuery = ((ConstantScoreQuery) subQuery).getQuery(); |
| 140 | + boost = 1; |
| 141 | + } else if (subQuery instanceof BoostQuery) { |
| 142 | + BoostQuery boostQuery = (BoostQuery) subQuery; |
| 143 | + subQuery = boostQuery.getQuery(); |
| 144 | + boost *= boostQuery.getBoost(); |
| 145 | + } else { |
| 146 | + break; |
| 147 | + } |
131 | 148 | }
|
132 |
| - //no MultiTermQuery extends SpanQuery, so SpanBoostQuery is not supported here |
| 149 | + final SpanQuery spanQuery; |
| 150 | + // no MultiTermQuery extends SpanQuery, so SpanBoostQuery is not supported here |
133 | 151 | assert subQuery instanceof SpanBoostQuery == false;
|
134 |
| - if (subQuery instanceof MultiTermQuery == false) { |
135 |
| - throw new UnsupportedOperationException("unsupported inner query, should be " + MultiTermQuery.class.getName() +" but was " |
136 |
| - + subQuery.getClass().getName()); |
| 152 | + if (subQuery instanceof TermQuery) { |
| 153 | + /** |
| 154 | + * Text fields that index prefixes can rewrite prefix queries |
| 155 | + * into term queries. See {@link TextFieldMapper.TextFieldType#prefixQuery}. |
| 156 | + */ |
| 157 | + if (multiTermQueryBuilder.getClass() != PrefixQueryBuilder.class) { |
| 158 | + throw new UnsupportedOperationException("unsupported inner query generated by " + |
| 159 | + multiTermQueryBuilder.getClass().getName() + ", should be " + MultiTermQuery.class.getName() |
| 160 | + + " but was " + subQuery.getClass().getName()); |
| 161 | + } |
| 162 | + if (context.getIndexSettings().getIndexVersionCreated().before(Version.V_6_4_0)) { |
| 163 | + /** |
| 164 | + * Indices created in this version do not index positions on the prefix field |
| 165 | + * so we cannot use it to match positional queries. Instead, we explicitly create the prefix |
| 166 | + * query on the main field to avoid the rewrite. |
| 167 | + */ |
| 168 | + PrefixQueryBuilder prefixBuilder = (PrefixQueryBuilder) multiTermQueryBuilder; |
| 169 | + PrefixQuery prefixQuery = new PrefixQuery(new Term(prefixBuilder.fieldName(), prefixBuilder.value())); |
| 170 | + if (prefixBuilder.rewrite() != null) { |
| 171 | + MultiTermQuery.RewriteMethod rewriteMethod = |
| 172 | + QueryParsers.parseRewriteMethod(prefixBuilder.rewrite(), null, LoggingDeprecationHandler.INSTANCE); |
| 173 | + prefixQuery.setRewriteMethod(rewriteMethod); |
| 174 | + } |
| 175 | + spanQuery = new SpanMultiTermQueryWrapper<>(prefixQuery); |
| 176 | + } else { |
| 177 | + String origFieldName = ((PrefixQueryBuilder) multiTermQueryBuilder).fieldName(); |
| 178 | + SpanTermQuery spanTermQuery = new SpanTermQuery(((TermQuery) subQuery).getTerm()); |
| 179 | + /** |
| 180 | + * Prefixes are indexed in a different field so we mask the term query with the original field |
| 181 | + * name. This is required because span_near and span_or queries don't work across different field. |
| 182 | + * The masking is safe because the prefix field is indexed using the same content than the original field |
| 183 | + * and the prefix analyzer preserves positions. |
| 184 | + */ |
| 185 | + spanQuery = new FieldMaskingSpanQuery(spanTermQuery, origFieldName); |
| 186 | + } |
| 187 | + } else { |
| 188 | + if (subQuery instanceof MultiTermQuery == false) { |
| 189 | + throw new UnsupportedOperationException("unsupported inner query, should be " |
| 190 | + + MultiTermQuery.class.getName() + " but was " + subQuery.getClass().getName()); |
| 191 | + } |
| 192 | + spanQuery = new SpanMultiTermQueryWrapper<>((MultiTermQuery) subQuery); |
137 | 193 | }
|
138 |
| - SpanQuery wrapper = new SpanMultiTermQueryWrapper<>((MultiTermQuery) subQuery); |
139 | 194 | if (boost != AbstractQueryBuilder.DEFAULT_BOOST) {
|
140 |
| - wrapper = new SpanBoostQuery(wrapper, boost); |
| 195 | + return new SpanBoostQuery(spanQuery, boost); |
141 | 196 | }
|
142 |
| - return wrapper; |
| 197 | + return spanQuery; |
143 | 198 | }
|
144 | 199 |
|
145 | 200 | @Override
|
|
0 commit comments