Search API: Allow to name filters and return per hit the filters it matched on, closes #364.
This commit is contained in:
parent
ab2a655a59
commit
eccc7d5ef2
|
@ -19,7 +19,9 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* The result of parsing a query.
|
||||
|
@ -30,8 +32,16 @@ public class ParsedQuery {
|
|||
|
||||
private final Query query;
|
||||
|
||||
public ParsedQuery(Query query) {
|
||||
private final ImmutableMap<String, Filter> namedFilters;
|
||||
|
||||
public ParsedQuery(Query query, ImmutableMap<String, Filter> namedFilters) {
|
||||
this.query = query;
|
||||
this.namedFilters = namedFilters;
|
||||
}
|
||||
|
||||
public ParsedQuery(Query query, ParsedQuery parsedQuery) {
|
||||
this.query = query;
|
||||
this.namedFilters = parsedQuery.namedFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,4 +50,8 @@ public class ParsedQuery {
|
|||
public Query query() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
public ImmutableMap<String, Filter> namedFilters() {
|
||||
return this.namedFilters;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
|
||||
package org.elasticsearch.index.query.support;
|
||||
|
||||
import org.apache.lucene.search.*;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.FilteredQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.lucene.search.AndFilter;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.query.xcontent.QueryParseContext;
|
||||
|
@ -56,12 +60,6 @@ public final class QueryParsers {
|
|||
return filter;
|
||||
}
|
||||
DocumentMapper docMapper = smartFieldMappers.docMapper();
|
||||
BooleanFilter booleanFilter = new BooleanFilter();
|
||||
|
||||
booleanFilter.add(new FilterClause(parseContext.cacheFilterIfPossible(docMapper.typeFilter()), BooleanClause.Occur.MUST));
|
||||
booleanFilter.add(new FilterClause(filter, BooleanClause.Occur.MUST));
|
||||
|
||||
// don't cache the boolean filter...
|
||||
return booleanFilter;
|
||||
return new AndFilter(ImmutableList.of(parseContext.cacheFilterIfPossible(docMapper.typeFilter()), filter));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ public class AndFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private Boolean cache;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public AndFilterBuilder(XContentFilterBuilder... filters) {
|
||||
for (XContentFilterBuilder filter : filters) {
|
||||
this.filters.add(filter);
|
||||
|
@ -58,6 +60,14 @@ public class AndFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
public AndFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(AndFilterParser.NAME);
|
||||
builder.startArray("filters");
|
||||
|
@ -66,7 +76,10 @@ public class AndFilterBuilder extends BaseFilterBuilder {
|
|||
}
|
||||
builder.endArray();
|
||||
if (cache != null) {
|
||||
builder.field("cache", cache);
|
||||
builder.field("_cache", cache);
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class AndFilterParser extends AbstractIndexComponent implements XContentF
|
|||
|
||||
boolean cache = true;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token == XContentParser.Token.START_ARRAY) {
|
||||
|
@ -77,8 +78,10 @@ public class AndFilterParser extends AbstractIndexComponent implements XContentF
|
|||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("cache".equals(currentFieldName)) {
|
||||
if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +97,10 @@ public class AndFilterParser extends AbstractIndexComponent implements XContentF
|
|||
}
|
||||
}
|
||||
// no need to cache this one
|
||||
return new AndFilter(filters);
|
||||
AndFilter filter = new AndFilter(filters);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private ArrayList<Clause> clauses = new ArrayList<Clause>();
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* Adds a filter that <b>must</b> appear in the matching documents.
|
||||
*/
|
||||
|
@ -60,6 +62,14 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
public BoolFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject("bool");
|
||||
for (Clause clause : clauses) {
|
||||
|
@ -74,6 +84,9 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
|
|||
clause.filterBuilder.toXContent(builder, params);
|
||||
}
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ public class BoolFilterParser extends AbstractIndexComponent implements XContent
|
|||
|
||||
boolean cache = true;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -81,22 +82,27 @@ public class BoolFilterParser extends AbstractIndexComponent implements XContent
|
|||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("cache".equals(currentFieldName)) {
|
||||
if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BooleanFilter booleanFilter = new PublicBooleanFilter();
|
||||
BooleanFilter filter = new PublicBooleanFilter();
|
||||
for (OpenFilterClause filterClause : clauses) {
|
||||
|
||||
if (cache) {
|
||||
filterClause.setFilter(parseContext.cacheFilterIfPossible(filterClause.getFilter()));
|
||||
}
|
||||
|
||||
booleanFilter.add(filterClause);
|
||||
filter.add(filterClause);
|
||||
}
|
||||
// no need to cache this one, inner queries will be cached and that's is good enough (I think...)
|
||||
return booleanFilter;
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ public class ConstantScoreQueryParser extends AbstractIndexComponent implements
|
|||
} else if (token.isValue()) {
|
||||
if ("boost".equals(currentFieldName)) {
|
||||
boost = parser.floatValue();
|
||||
} else if ("cache".equals(currentFieldName)) {
|
||||
} else if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to Elastic Search and Shay Banon under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Elastic Search licenses this
|
||||
* file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query.xcontent;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.QueryWrapperFilter;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.AbstractIndexComponent;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
import org.elasticsearch.index.settings.IndexSettings;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class FQueryFilterParser extends AbstractIndexComponent implements XContentFilterParser {
|
||||
|
||||
public static final String NAME = "fquery";
|
||||
|
||||
@Inject public FQueryFilterParser(Index index, @IndexSettings Settings settings) {
|
||||
super(index, settings);
|
||||
}
|
||||
|
||||
@Override public String[] names() {
|
||||
return new String[]{NAME};
|
||||
}
|
||||
|
||||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
Query query = null;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if ("query".equals(currentFieldName)) {
|
||||
query = parseContext.parseInnerQuery();
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
Filter filter = new QueryWrapperFilter(query);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -40,6 +40,8 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private String bottomRightGeohash;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public GeoBoundingBoxFilterBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -68,6 +70,14 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
public GeoBoundingBoxFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(GeoBoundingBoxFilterParser.NAME);
|
||||
|
||||
|
@ -89,6 +99,10 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
|
|||
}
|
||||
builder.endObject();
|
||||
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,20 +53,21 @@ public class GeoBoundingBoxFilterParser extends AbstractIndexComponent implement
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String latFieldName = parser.currentName() + XContentGeoPointFieldMapper.Names.LAT_SUFFIX;
|
||||
String lonFieldName = parser.currentName() + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
|
||||
|
||||
// now, we move after the field name, which starts the object
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
|
||||
|
||||
String latFieldName = null;
|
||||
String lonFieldName = null;
|
||||
GeoBoundingBoxFilter.Point topLeft = new GeoBoundingBoxFilter.Point();
|
||||
GeoBoundingBoxFilter.Point bottomRight = new GeoBoundingBoxFilter.Point();
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
latFieldName = currentFieldName + XContentGeoPointFieldMapper.Names.LAT_SUFFIX;
|
||||
lonFieldName = currentFieldName + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
|
@ -139,6 +140,12 @@ public class GeoBoundingBoxFilterParser extends AbstractIndexComponent implement
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapperService mapperService = parseContext.mapperService();
|
||||
FieldMapper mapper = mapperService.smartNameFieldMapper(latFieldName);
|
||||
|
@ -154,6 +161,10 @@ public class GeoBoundingBoxFilterParser extends AbstractIndexComponent implement
|
|||
lonFieldName = mapper.names().indexName();
|
||||
|
||||
|
||||
return new GeoBoundingBoxFilter(topLeft, bottomRight, latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
GeoBoundingBoxFilter filter = new GeoBoundingBoxFilter(topLeft, bottomRight, latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private GeoDistance geoDistance;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public GeoDistanceFilterBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -82,6 +84,11 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public GeoDistanceFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(GeoDistanceFilterParser.NAME);
|
||||
if (geohash != null) {
|
||||
|
@ -93,6 +100,9 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
|
|||
if (geoDistance != null) {
|
||||
builder.field("distance_type", geoDistance.name().toLowerCase());
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
|
|||
|
||||
XContentParser.Token token;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
double lat = 0;
|
||||
double lon = 0;
|
||||
|
@ -128,6 +129,8 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
|
|||
lon = values[1];
|
||||
latFieldName = currentFieldName.substring(0, currentFieldName.length() - XContentGeoPointFieldMapper.Names.GEOHASH_SUFFIX.length()) + XContentGeoPointFieldMapper.Names.LAT_SUFFIX;
|
||||
lonFieldName = currentFieldName.substring(0, currentFieldName.length() - XContentGeoPointFieldMapper.Names.GEOHASH_SUFFIX.length()) + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
} else {
|
||||
// assume the value is the actual value
|
||||
String value = parser.text();
|
||||
|
@ -164,6 +167,10 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
|
|||
}
|
||||
lonFieldName = mapper.names().indexName();
|
||||
|
||||
return new GeoDistanceFilter(lat, lon, distance, geoDistance, latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
GeoDistanceFilter filter = new GeoDistanceFilter(lat, lon, distance, geoDistance, latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private final List<GeoPolygonFilter.Point> points = Lists.newArrayList();
|
||||
|
||||
private String filterName;
|
||||
|
||||
public GeoPolygonFilterBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -50,6 +52,11 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
|
|||
return addPoint(values[0], values[1]);
|
||||
}
|
||||
|
||||
public GeoPolygonFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(GeoPolygonFilterParser.NAME);
|
||||
|
||||
|
@ -61,6 +68,10 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
|
|||
builder.endArray();
|
||||
builder.endObject();
|
||||
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,19 +66,22 @@ public class GeoPolygonFilterParser extends AbstractIndexComponent implements XC
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String latFieldName = parser.currentName() + XContentGeoPointFieldMapper.Names.LAT_SUFFIX;
|
||||
String lonFieldName = parser.currentName() + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
|
||||
|
||||
// now, we move after the field name, which starts the object
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
|
||||
String latFieldName = null;
|
||||
String lonFieldName = null;
|
||||
List<GeoPolygonFilter.Point> points = Lists.newArrayList();
|
||||
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
latFieldName = currentFieldName + XContentGeoPointFieldMapper.Names.LAT_SUFFIX;
|
||||
lonFieldName = currentFieldName + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
|
@ -133,6 +136,12 @@ public class GeoPolygonFilterParser extends AbstractIndexComponent implements XC
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (points.isEmpty()) {
|
||||
throw new QueryParsingException(index, "no points defined for geo_polygon filter");
|
||||
|
@ -151,6 +160,10 @@ public class GeoPolygonFilterParser extends AbstractIndexComponent implements XC
|
|||
}
|
||||
lonFieldName = mapper.names().indexName();
|
||||
|
||||
return new GeoPolygonFilter(points.toArray(new GeoPolygonFilter.Point[points.size()]), latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
GeoPolygonFilter filter = new GeoPolygonFilter(points.toArray(new GeoPolygonFilter.Point[points.size()]), latFieldName, lonFieldName, mapper.fieldDataType(), parseContext.indexCache().fieldData());
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ public class NotFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private Boolean cache;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public NotFilterBuilder(XContentFilterBuilder filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
@ -46,12 +48,20 @@ public class NotFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public NotFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NotFilterParser.NAME);
|
||||
builder.field("filter");
|
||||
filter.toXContent(builder, params);
|
||||
if (cache != null) {
|
||||
builder.field("cache", cache);
|
||||
builder.field("_cache", cache);
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public class NotFilterParser extends AbstractIndexComponent implements XContentF
|
|||
Filter filter = null;
|
||||
boolean cache = true;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -62,8 +63,10 @@ public class NotFilterParser extends AbstractIndexComponent implements XContentF
|
|||
filter = parseContext.parseInnerFilter();
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("cache".equals(currentFieldName)) {
|
||||
if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +79,10 @@ public class NotFilterParser extends AbstractIndexComponent implements XContentF
|
|||
filter = parseContext.cacheFilterIfPossible(filter);
|
||||
}
|
||||
// no need to cache this one
|
||||
return new NotFilter(filter);
|
||||
NotFilter notFilter = new NotFilter(filter);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, notFilter);
|
||||
}
|
||||
return notFilter;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,8 @@ public class OrFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private Boolean cache;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public OrFilterBuilder(XContentFilterBuilder... filters) {
|
||||
for (XContentFilterBuilder filter : filters) {
|
||||
this.filters.add(filter);
|
||||
|
@ -58,6 +60,11 @@ public class OrFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public OrFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(OrFilterParser.NAME);
|
||||
builder.startArray("filters");
|
||||
|
@ -66,7 +73,10 @@ public class OrFilterBuilder extends BaseFilterBuilder {
|
|||
}
|
||||
builder.endArray();
|
||||
if (cache != null) {
|
||||
builder.field("cache", cache);
|
||||
builder.field("_cache", cache);
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class OrFilterParser extends AbstractIndexComponent implements XContentFi
|
|||
|
||||
boolean cache = true;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token == XContentParser.Token.START_ARRAY) {
|
||||
|
@ -77,8 +78,10 @@ public class OrFilterParser extends AbstractIndexComponent implements XContentFi
|
|||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("cache".equals(currentFieldName)) {
|
||||
if ("_cache".equals(currentFieldName)) {
|
||||
cache = parser.booleanValue();
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +97,10 @@ public class OrFilterParser extends AbstractIndexComponent implements XContentFi
|
|||
}
|
||||
}
|
||||
// no need to cache this one
|
||||
return new OrFilter(filters);
|
||||
OrFilter filter = new OrFilter(filters);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ public class PrefixFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private final String prefix;
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* A filter that restricts search results to values that have a matching prefix in a given
|
||||
* field.
|
||||
|
@ -47,9 +49,17 @@ public class PrefixFilterBuilder extends BaseFilterBuilder {
|
|||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public PrefixFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(PrefixFilterParser.NAME);
|
||||
builder.field(name, prefix);
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -53,15 +53,27 @@ public class PrefixFilterParser extends AbstractIndexComponent implements XConte
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String fieldName = parser.currentName();
|
||||
parser.nextToken();
|
||||
String value = parser.text();
|
||||
parser.nextToken();
|
||||
String fieldName = null;
|
||||
String value = null;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
} else {
|
||||
fieldName = currentFieldName;
|
||||
value = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new QueryParsingException(index, "No value specified for prefix query");
|
||||
throw new QueryParsingException(index, "No value specified for prefix filter");
|
||||
}
|
||||
|
||||
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
|
||||
|
@ -72,7 +84,11 @@ public class PrefixFilterParser extends AbstractIndexComponent implements XConte
|
|||
}
|
||||
}
|
||||
|
||||
Filter prefixFilter = new PrefixFilter(new Term(fieldName, value));
|
||||
return wrapSmartNameFilter(prefixFilter, smartNameFieldMappers, parseContext);
|
||||
Filter filter = new PrefixFilter(new Term(fieldName, value));
|
||||
filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ public class QueryFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private final XContentQueryBuilder queryBuilder;
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* A filter that simply wraps a query.
|
||||
*
|
||||
|
@ -41,8 +43,21 @@ public class QueryFilterBuilder extends BaseFilterBuilder {
|
|||
this.queryBuilder = queryBuilder;
|
||||
}
|
||||
|
||||
public QueryFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (filterName != null) {
|
||||
builder.field(QueryFilterParser.NAME);
|
||||
queryBuilder.toXContent(builder, params);
|
||||
} else {
|
||||
builder.startObject(FQueryFilterParser.NAME);
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
builder.field("_name", filterName);
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.search.QueryWrapperFilter;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.AbstractIndexComponent;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.query.QueryParsingException;
|
||||
|
@ -48,10 +47,7 @@ public class QueryFilterParser extends AbstractIndexComponent implements XConten
|
|||
}
|
||||
|
||||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
Query query = parseContext.parseInnerQuery();
|
||||
Filter filter = new QueryWrapperFilter(query);
|
||||
return filter;
|
||||
return new QueryWrapperFilter(query);
|
||||
}
|
||||
}
|
|
@ -22,6 +22,8 @@ package org.elasticsearch.index.query.xcontent;
|
|||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.Maps;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.cache.IndexCache;
|
||||
|
@ -35,6 +37,7 @@ import org.elasticsearch.script.ScriptService;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
|
@ -55,6 +58,8 @@ public class QueryParseContext {
|
|||
|
||||
private final XContentQueryParserRegistry queryParserRegistry;
|
||||
|
||||
private final Map<String, Filter> namedFilters = Maps.newHashMap();
|
||||
|
||||
private XContentParser parser;
|
||||
|
||||
public QueryParseContext(Index index, XContentQueryParserRegistry queryParserRegistry,
|
||||
|
@ -72,6 +77,7 @@ public class QueryParseContext {
|
|||
|
||||
public void reset(XContentParser jp) {
|
||||
this.parser = jp;
|
||||
this.namedFilters.clear();
|
||||
}
|
||||
|
||||
public XContentParser parser() {
|
||||
|
@ -106,6 +112,17 @@ public class QueryParseContext {
|
|||
return indexCache.filter().cache(filter);
|
||||
}
|
||||
|
||||
public void addNamedFilter(String name, Filter filter) {
|
||||
namedFilters.put(name, filter);
|
||||
}
|
||||
|
||||
public ImmutableMap<String, Filter> copyNamedFilters() {
|
||||
if (namedFilters.isEmpty()) {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
return ImmutableMap.copyOf(namedFilters);
|
||||
}
|
||||
|
||||
public Query parseInnerQuery() throws IOException, QueryParsingException {
|
||||
// move to START object
|
||||
XContentParser.Token token;
|
||||
|
|
|
@ -40,6 +40,8 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private boolean includeUpper = true;
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* A filter that restricts search results to values that are within the given range.
|
||||
*
|
||||
|
@ -325,14 +327,25 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public RangeFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(RangeFilterParser.NAME);
|
||||
|
||||
builder.startObject(name);
|
||||
builder.field("from", from);
|
||||
builder.field("to", to);
|
||||
builder.field("include_lower", includeLower);
|
||||
builder.field("include_upper", includeUpper);
|
||||
builder.endObject();
|
||||
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -52,20 +52,20 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String fieldName = parser.currentName();
|
||||
|
||||
// now, we move after the field name, which starts the object
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.START_OBJECT;
|
||||
|
||||
String fieldName = null;
|
||||
String from = null;
|
||||
String to = null;
|
||||
boolean includeLower = true;
|
||||
boolean includeUpper = true;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
fieldName = currentFieldName;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
|
@ -93,10 +93,12 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move to the next end object, to close the field name
|
||||
token = parser.nextToken();
|
||||
assert token == XContentParser.Token.END_OBJECT;
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filter filter = null;
|
||||
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
|
||||
|
@ -108,6 +110,10 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
|
|||
if (filter == null) {
|
||||
filter = new TermRangeFilter(fieldName, from, to, includeLower, includeUpper);
|
||||
}
|
||||
return wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
|
||||
filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private Map<String, Object> params;
|
||||
|
||||
private String filterName;
|
||||
|
||||
public ScriptFilterBuilder(String script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
@ -56,12 +58,20 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ScriptFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(ScriptFilterParser.NAME);
|
||||
builder.field("script", script);
|
||||
if (this.params != null) {
|
||||
builder.field("params", this.params);
|
||||
}
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -62,6 +62,7 @@ public class ScriptFilterParser extends AbstractIndexComponent implements XConte
|
|||
String script = null;
|
||||
Map<String, Object> params = null;
|
||||
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
|
@ -73,6 +74,8 @@ public class ScriptFilterParser extends AbstractIndexComponent implements XConte
|
|||
} else if (token.isValue()) {
|
||||
if ("script".equals(currentFieldName)) {
|
||||
script = parser.text();
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +87,11 @@ public class ScriptFilterParser extends AbstractIndexComponent implements XConte
|
|||
params = Maps.newHashMap();
|
||||
}
|
||||
|
||||
return new ScriptFilter(script, params, parseContext.mapperService(), parseContext.indexCache().fieldData(), parseContext.scriptService());
|
||||
Filter filter = new ScriptFilter(script, params, parseContext.mapperService(), parseContext.indexCache().fieldData(), parseContext.scriptService());
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static class ScriptFilter extends Filter {
|
||||
|
|
|
@ -34,6 +34,8 @@ public class TermFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private final Object value;
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* A filter for a field based on a term.
|
||||
*
|
||||
|
@ -95,9 +97,17 @@ public class TermFilterBuilder extends BaseFilterBuilder {
|
|||
this.value = value;
|
||||
}
|
||||
|
||||
public TermFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(TermFilterParser.NAME);
|
||||
builder.field(name, value);
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -53,15 +53,24 @@ public class TermFilterParser extends AbstractIndexComponent implements XContent
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String fieldName = parser.currentName();
|
||||
String fieldName = null;
|
||||
String value = null;
|
||||
|
||||
|
||||
parser.nextToken();
|
||||
String value = parser.text();
|
||||
// move to the next token (from VALUE)
|
||||
parser.nextToken();
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
} else {
|
||||
fieldName = currentFieldName;
|
||||
value = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new QueryParsingException(index, "No value specified for term filter");
|
||||
|
@ -77,6 +86,10 @@ public class TermFilterParser extends AbstractIndexComponent implements XContent
|
|||
if (filter == null) {
|
||||
filter = new TermFilter(new Term(fieldName, value));
|
||||
}
|
||||
return wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
|
||||
filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
private final Object[] values;
|
||||
|
||||
private String filterName;
|
||||
|
||||
/**
|
||||
* A filer for a field based on several terms matching on any of them.
|
||||
*
|
||||
|
@ -111,6 +113,11 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
|
|||
this.values = values;
|
||||
}
|
||||
|
||||
public TermsFilterBuilder filterName(String filterName) {
|
||||
this.filterName = filterName;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(TermsFilterParser.NAME);
|
||||
builder.startArray(name);
|
||||
|
@ -118,6 +125,11 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
|
|||
builder.value(value);
|
||||
}
|
||||
builder.endArray();
|
||||
|
||||
if (filterName != null) {
|
||||
builder.field("_name", filterName);
|
||||
}
|
||||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
|
@ -55,12 +55,18 @@ public class TermsFilterParser extends AbstractIndexComponent implements XConten
|
|||
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
assert token == XContentParser.Token.FIELD_NAME;
|
||||
String fieldName = parser.currentName();
|
||||
|
||||
MapperService.SmartNameFieldMappers smartNameFieldMappers = null;
|
||||
TermsFilter termsFilter = new PublicTermsFilter();
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
String fieldName = currentFieldName;
|
||||
FieldMapper fieldMapper = null;
|
||||
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
|
||||
smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
|
||||
if (smartNameFieldMappers != null) {
|
||||
if (smartNameFieldMappers.hasMapper()) {
|
||||
fieldMapper = smartNameFieldMappers.mapper();
|
||||
|
@ -68,12 +74,6 @@ public class TermsFilterParser extends AbstractIndexComponent implements XConten
|
|||
}
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != XContentParser.Token.START_ARRAY) {
|
||||
throw new QueryParsingException(index, "Terms filter must define the terms to filter on as an array");
|
||||
}
|
||||
|
||||
TermsFilter termsFilter = new PublicTermsFilter();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
String value = parser.text();
|
||||
if (value == null) {
|
||||
|
@ -84,9 +84,17 @@ public class TermsFilterParser extends AbstractIndexComponent implements XConten
|
|||
}
|
||||
termsFilter.addTerm(new Term(fieldName, value));
|
||||
}
|
||||
parser.nextToken();
|
||||
|
||||
|
||||
return wrapSmartNameFilter(termsFilter, smartNameFieldMappers, parseContext);
|
||||
} else if (token.isValue()) {
|
||||
if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Filter filter = wrapSmartNameFilter(termsFilter, smartNameFieldMappers, parseContext);
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,6 +210,7 @@ public class XContentIndexQueryParser extends AbstractIndexComponent implements
|
|||
|
||||
private ParsedQuery parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException {
|
||||
parseContext.reset(parser);
|
||||
return new ParsedQuery(parseContext.parseInnerQuery());
|
||||
Query query = parseContext.parseInnerQuery();
|
||||
return new ParsedQuery(query, parseContext.copyNamedFilters());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ public class XContentQueryParserRegistry {
|
|||
add(filterParsersMap, new GeoBoundingBoxFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new GeoPolygonFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new QueryFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new FQueryFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new BoolFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new AndFilterParser(index, indexSettings));
|
||||
add(filterParsersMap, new OrFilterParser(index, indexSettings));
|
||||
|
|
|
@ -37,7 +37,7 @@ public class SearchContextException extends SearchException {
|
|||
private static String buildMessage(SearchContext context, String msg) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('[').append(context.shardTarget().index()).append("][").append(context.shardTarget().shardId()).append("]: ");
|
||||
sb.append("query[").append(context.originalQuery()).append("],from[").append(context.from()).append("],size[").append(context.size()).append("]");
|
||||
sb.append("query[").append(context.parsedQuery().query()).append("],from[").append(context.from()).append("],size[").append(context.size()).append("]");
|
||||
if (context.sort() != null) {
|
||||
sb.append(",sort[").append(context.sort()).append("]");
|
||||
}
|
||||
|
|
|
@ -147,6 +147,16 @@ public interface SearchHit extends Streamable, ToXContent, Iterable<SearchHitFie
|
|||
*/
|
||||
Object[] getSortValues();
|
||||
|
||||
/**
|
||||
* The set of filter names the query matched. Mainly makes sense for OR filters.
|
||||
*/
|
||||
String[] matchedFilters();
|
||||
|
||||
/**
|
||||
* The set of filter names the query matched. Mainly makes sense for OR filters.
|
||||
*/
|
||||
String[] getMatchedFilters();
|
||||
|
||||
/**
|
||||
* The shard of the search hit.
|
||||
*/
|
||||
|
|
|
@ -23,8 +23,12 @@ import org.apache.lucene.document.Document;
|
|||
import org.apache.lucene.document.FieldSelector;
|
||||
import org.apache.lucene.document.Fieldable;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.DocIdSet;
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.Lists;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.docset.DocSet;
|
||||
import org.elasticsearch.common.thread.ThreadLocals;
|
||||
import org.elasticsearch.index.mapper.*;
|
||||
import org.elasticsearch.search.SearchHitField;
|
||||
|
@ -41,6 +45,7 @@ import org.elasticsearch.search.internal.SearchContext;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -159,6 +164,27 @@ public class FetchPhase implements SearchPhase {
|
|||
sameDocCache.clear();
|
||||
}
|
||||
|
||||
if (!context.parsedQuery().namedFilters().isEmpty()) {
|
||||
int readerIndex = context.searcher().readerIndex(docId);
|
||||
IndexReader subReader = context.searcher().subReaders()[readerIndex];
|
||||
int subDoc = docId - context.searcher().docStarts()[readerIndex];
|
||||
List<String> matchedFilters = Lists.newArrayListWithCapacity(2);
|
||||
for (Map.Entry<String, Filter> entry : context.parsedQuery().namedFilters().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Filter filter = entry.getValue();
|
||||
filter = context.filterCache().cache(filter);
|
||||
try {
|
||||
DocIdSet docIdSet = filter.getDocIdSet(subReader);
|
||||
if (docIdSet instanceof DocSet && ((DocSet) docIdSet).get(subDoc)) {
|
||||
matchedFilters.add(name);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
searchHit.matchedFilters(matchedFilters.toArray(new String[matchedFilters.size()]));
|
||||
}
|
||||
|
||||
doExplanation(context, docId, searchHit);
|
||||
}
|
||||
context.fetchResult().hits(new InternalSearchHits(hits, context.queryResult().topDocs().totalHits, context.queryResult().topDocs().getMaxScore()));
|
||||
|
|
|
@ -80,7 +80,7 @@ public class ContextIndexSearcher extends ExtendedIndexSearcher {
|
|||
}
|
||||
|
||||
@Override public Query rewrite(Query original) throws IOException {
|
||||
if (original == searchContext.query() || original == searchContext.originalQuery()) {
|
||||
if (original == searchContext.query() || original == searchContext.parsedQuery().query()) {
|
||||
// optimize in case its the top level search query and we already rewrote it...
|
||||
if (searchContext.queryRewritten()) {
|
||||
return searchContext.query();
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.search.internal;
|
|||
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.elasticsearch.ElasticSearchParseException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.Unicode;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.compress.lzf.LZFDecoder;
|
||||
|
@ -69,6 +70,8 @@ public class InternalSearchHit implements SearchHit {
|
|||
|
||||
private Object[] sortValues = EMPTY_SORT_VALUES;
|
||||
|
||||
private String[] matchedFilters = Strings.EMPTY_ARRAY;
|
||||
|
||||
private Explanation explanation;
|
||||
|
||||
@Nullable private SearchShardTarget shard;
|
||||
|
@ -248,6 +251,18 @@ public class InternalSearchHit implements SearchHit {
|
|||
this.shard = target;
|
||||
}
|
||||
|
||||
public void matchedFilters(String[] matchedFilters) {
|
||||
this.matchedFilters = matchedFilters;
|
||||
}
|
||||
|
||||
public String[] matchedFilters() {
|
||||
return this.matchedFilters;
|
||||
}
|
||||
|
||||
@Override public String[] getMatchedFilters() {
|
||||
return this.matchedFilters;
|
||||
}
|
||||
|
||||
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("_index", shard.index());
|
||||
|
@ -305,6 +320,13 @@ public class InternalSearchHit implements SearchHit {
|
|||
}
|
||||
builder.endArray();
|
||||
}
|
||||
if (matchedFilters.length > 0) {
|
||||
builder.startArray("matched_filters");
|
||||
for (String matchedFilter : matchedFilters) {
|
||||
builder.value(matchedFilter);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
if (explanation() != null) {
|
||||
builder.field("_explanation");
|
||||
buildExplanation(builder, explanation());
|
||||
|
@ -447,6 +469,14 @@ public class InternalSearchHit implements SearchHit {
|
|||
}
|
||||
}
|
||||
|
||||
size = in.readVInt();
|
||||
if (size > 0) {
|
||||
matchedFilters = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
matchedFilters[i] = in.readUTF();
|
||||
}
|
||||
}
|
||||
|
||||
if (shardLookupMap != null) {
|
||||
int lookupId = in.readVInt();
|
||||
if (lookupId > 0) {
|
||||
|
@ -530,6 +560,15 @@ public class InternalSearchHit implements SearchHit {
|
|||
}
|
||||
}
|
||||
|
||||
if (matchedFilters.length == 0) {
|
||||
out.writeVInt(0);
|
||||
} else {
|
||||
out.writeVInt(matchedFilters.length);
|
||||
for (String matchedFilter : matchedFilters) {
|
||||
out.writeUTF(matchedFilter);
|
||||
}
|
||||
}
|
||||
|
||||
if (shardLookupMap == null) {
|
||||
if (shard == null) {
|
||||
out.writeBoolean(false);
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.elasticsearch.index.mapper.MapperService;
|
|||
import org.elasticsearch.index.query.IndexQueryParser;
|
||||
import org.elasticsearch.index.query.IndexQueryParserMissingException;
|
||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.index.similarity.SimilarityService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
|
@ -94,7 +95,7 @@ public class SearchContext implements Releasable {
|
|||
|
||||
private String queryParserName;
|
||||
|
||||
private Query originalQuery;
|
||||
private ParsedQuery originalQuery;
|
||||
|
||||
private Query query;
|
||||
|
||||
|
@ -272,17 +273,14 @@ public class SearchContext implements Releasable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public SearchContext query(Query query) {
|
||||
public SearchContext parsedQuery(ParsedQuery query) {
|
||||
queryRewritten = false;
|
||||
this.originalQuery = query;
|
||||
this.query = query;
|
||||
this.query = query.query();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The original query to execute, unmodified.
|
||||
*/
|
||||
public Query originalQuery() {
|
||||
public ParsedQuery parsedQuery() {
|
||||
return this.originalQuery;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.elasticsearch.search.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
|
||||
|
@ -35,7 +34,6 @@ public class QueryBinaryParseElement implements SearchParseElement {
|
|||
XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser();
|
||||
byte[] querySource = parser.binaryValue();
|
||||
XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource);
|
||||
Query query = indexQueryParser.parse(qSourceParser).query();
|
||||
context.query(query);
|
||||
context.parsedQuery(indexQueryParser.parse(qSourceParser));
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.elasticsearch.search.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
|
@ -32,7 +31,6 @@ public class QueryParseElement implements SearchParseElement {
|
|||
|
||||
@Override public void parse(XContentParser parser, SearchContext context) throws Exception {
|
||||
XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser();
|
||||
Query query = indexQueryParser.parse(parser).query();
|
||||
context.query(query);
|
||||
context.parsedQuery(indexQueryParser.parse(parser));
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
|
|||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.indices.TypeMissingException;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
import org.elasticsearch.search.SearchParseException;
|
||||
|
@ -67,7 +68,7 @@ public class QueryPhase implements SearchPhase {
|
|||
throw new SearchParseException(context, "No query specified in search request");
|
||||
}
|
||||
if (context.queryBoost() != 1.0f) {
|
||||
context.query(new FunctionScoreQuery(context.query(), new BoostScoreFunction(context.queryBoost())));
|
||||
context.parsedQuery(new ParsedQuery(new FunctionScoreQuery(context.query(), new BoostScoreFunction(context.queryBoost())), context.parsedQuery()));
|
||||
}
|
||||
facetsPhase.preProcess(context);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.elasticsearch.index.cache.IndexCache;
|
|||
import org.elasticsearch.index.engine.robin.RobinIndexEngine;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.query.IndexQueryParser;
|
||||
import org.elasticsearch.index.query.ParsedQuery;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
|
@ -375,6 +376,17 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(prefixFilter.getPrefix(), equalTo(new Term("name.first", "sh")));
|
||||
}
|
||||
|
||||
@Test public void testPrefixNamedFilteredQuery() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/prefix-filter-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
PrefixFilter prefixFilter = (PrefixFilter) filteredQuery.getFilter();
|
||||
assertThat(prefixFilter.getPrefix(), equalTo(new Term("name.first", "sh")));
|
||||
}
|
||||
|
||||
@Test public void testPrefixQueryBoostQueryBuilder() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
Query parsedQuery = queryParser.parse(prefixQuery("name.first", "sh").boost(2.0f)).query();
|
||||
|
@ -483,6 +495,22 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(rangeFilter.includesMax(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test public void testRangeNamedFilteredQuery() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/range-filter-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
Filter filter = ((FilteredQuery) parsedQuery.query()).getFilter();
|
||||
assertThat(filter, instanceOf(NumericRangeFilter.class));
|
||||
NumericRangeFilter rangeFilter = (NumericRangeFilter) filter;
|
||||
assertThat(rangeFilter.getField(), equalTo("age"));
|
||||
assertThat(rangeFilter.getMin().intValue(), equalTo(23));
|
||||
assertThat(rangeFilter.getMax().intValue(), equalTo(54));
|
||||
assertThat(rangeFilter.includesMin(), equalTo(true));
|
||||
assertThat(rangeFilter.includesMax(), equalTo(false));
|
||||
}
|
||||
|
||||
@Test public void testBoolFilteredQuery() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/bool-filter.json");
|
||||
|
@ -519,6 +547,20 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(((TermFilter) andFilter.filters().get(1)).getTerm(), equalTo(new Term("name.first", "shay4")));
|
||||
}
|
||||
|
||||
@Test public void testAndNamedFilteredQuery() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/and-filter-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
|
||||
AndFilter andFilter = (AndFilter) filteredQuery.getFilter();
|
||||
assertThat(andFilter.filters().size(), equalTo(2));
|
||||
assertThat(((TermFilter) andFilter.filters().get(0)).getTerm(), equalTo(new Term("name.first", "shay1")));
|
||||
assertThat(((TermFilter) andFilter.filters().get(1)).getTerm(), equalTo(new Term("name.first", "shay4")));
|
||||
}
|
||||
|
||||
@Test public void testAndFilteredQuery2() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/and-filter2.json");
|
||||
|
@ -695,6 +737,31 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(((TermFilter) filteredQuery.getFilter()).getTerm(), equalTo(new Term("name.last", "banon")));
|
||||
}
|
||||
|
||||
@Test public void testTermFilterQuery() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/term-filter.json");
|
||||
Query parsedQuery = queryParser.parse(query).query();
|
||||
assertThat(parsedQuery, instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery;
|
||||
assertThat(filteredQuery.getFilter(), instanceOf(TermFilter.class));
|
||||
TermFilter termFilter = (TermFilter) filteredQuery.getFilter();
|
||||
assertThat(termFilter.getTerm().field(), equalTo("name.last"));
|
||||
assertThat(termFilter.getTerm().text(), equalTo("banon"));
|
||||
}
|
||||
|
||||
@Test public void testTermNamedFilterQuery() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/term-filter-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
assertThat(filteredQuery.getFilter(), instanceOf(TermFilter.class));
|
||||
TermFilter termFilter = (TermFilter) filteredQuery.getFilter();
|
||||
assertThat(termFilter.getTerm().field(), equalTo("name.last"));
|
||||
assertThat(termFilter.getTerm().text(), equalTo("banon"));
|
||||
}
|
||||
|
||||
@Test public void testTermsFilterQueryBuilder() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), termsFilter("name.last", "banon", "kimchy"))).query();
|
||||
|
@ -725,6 +792,22 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(terms.iterator().next().text(), equalTo("banon"));
|
||||
}
|
||||
|
||||
@Test public void testTermsWithNameFilterQuery() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/terms-filter-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
assertThat(filteredQuery.getFilter(), instanceOf(TermsFilter.class));
|
||||
TermsFilter termsFilter = (TermsFilter) filteredQuery.getFilter();
|
||||
Field field = TermsFilter.class.getDeclaredField("terms");
|
||||
field.setAccessible(true);
|
||||
Set<Term> terms = (Set<Term>) field.get(termsFilter);
|
||||
assertThat(terms.size(), equalTo(2));
|
||||
assertThat(terms.iterator().next().text(), equalTo("banon"));
|
||||
}
|
||||
|
||||
@Test public void testConstantScoreQueryBuilder() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
Query parsedQuery = queryParser.parse(constantScoreQuery(termFilter("name.last", "banon"))).query();
|
||||
|
@ -908,6 +991,21 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(((TermQuery) wrappedQuery).getTerm(), equalTo(new Term("name.last", "banon")));
|
||||
}
|
||||
|
||||
@Test public void testFQueryFilter() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/fquery-filter.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
QueryWrapperFilter queryWrapperFilter = (QueryWrapperFilter) filteredQuery.getFilter();
|
||||
Field field = QueryWrapperFilter.class.getDeclaredField("query");
|
||||
field.setAccessible(true);
|
||||
Query wrappedQuery = (Query) field.get(queryWrapperFilter);
|
||||
assertThat(wrappedQuery, instanceOf(TermQuery.class));
|
||||
assertThat(((TermQuery) wrappedQuery).getTerm(), equalTo(new Term("name.last", "banon")));
|
||||
}
|
||||
|
||||
@Test public void testMoreLikeThisBuilder() throws Exception {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
Query parsedQuery = queryParser.parse(moreLikeThisQuery("name.first", "name.last").likeText("something").minTermFreq(1).maxQueryTerms(12)).query();
|
||||
|
@ -985,6 +1083,21 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(mltQuery.getMaxQueryTerms(), equalTo(12));
|
||||
}
|
||||
|
||||
@Test public void testGeoDistanceFilterNamed() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_distance-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
GeoDistanceFilter filter = (GeoDistanceFilter) filteredQuery.getFilter();
|
||||
assertThat(filter.latFieldName(), equalTo("location.lat"));
|
||||
assertThat(filter.lonFieldName(), equalTo("location.lon"));
|
||||
assertThat(filter.lat(), closeTo(40, 0.00001));
|
||||
assertThat(filter.lon(), closeTo(-70, 0.00001));
|
||||
assertThat(filter.distance(), closeTo(12, 0.00001));
|
||||
}
|
||||
|
||||
@Test public void testGeoDistanceFilter1() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_distance1.json");
|
||||
|
@ -1041,6 +1154,23 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(filter.distance(), closeTo(12, 0.00001));
|
||||
}
|
||||
|
||||
@Test public void testGeoBoundingBoxFilterNamed() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_boundingbox-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
GeoBoundingBoxFilter filter = (GeoBoundingBoxFilter) filteredQuery.getFilter();
|
||||
assertThat(filter.latFieldName(), equalTo("location.lat"));
|
||||
assertThat(filter.lonFieldName(), equalTo("location.lon"));
|
||||
assertThat(filter.topLeft().lat, closeTo(40, 0.00001));
|
||||
assertThat(filter.topLeft().lon, closeTo(-70, 0.00001));
|
||||
assertThat(filter.bottomRight().lat, closeTo(30, 0.00001));
|
||||
assertThat(filter.bottomRight().lon, closeTo(-80, 0.00001));
|
||||
}
|
||||
|
||||
|
||||
@Test public void testGeoBoundingBoxFilter1() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_boundingbox1.json");
|
||||
|
@ -1101,6 +1231,25 @@ public class SimpleIndexQueryParserTests {
|
|||
assertThat(filter.bottomRight().lon, closeTo(-80, 0.00001));
|
||||
}
|
||||
|
||||
@Test public void testGeoPolygonNamedFilter() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_polygon-named.json");
|
||||
ParsedQuery parsedQuery = queryParser.parse(query);
|
||||
assertThat(parsedQuery.namedFilters().containsKey("test"), equalTo(true));
|
||||
assertThat(parsedQuery.query(), instanceOf(FilteredQuery.class));
|
||||
FilteredQuery filteredQuery = (FilteredQuery) parsedQuery.query();
|
||||
GeoPolygonFilter filter = (GeoPolygonFilter) filteredQuery.getFilter();
|
||||
assertThat(filter.latFieldName(), equalTo("location.lat"));
|
||||
assertThat(filter.lonFieldName(), equalTo("location.lon"));
|
||||
assertThat(filter.points().length, equalTo(3));
|
||||
assertThat(filter.points()[0].lat, closeTo(40, 0.00001));
|
||||
assertThat(filter.points()[0].lon, closeTo(-70, 0.00001));
|
||||
assertThat(filter.points()[1].lat, closeTo(30, 0.00001));
|
||||
assertThat(filter.points()[1].lon, closeTo(-80, 0.00001));
|
||||
assertThat(filter.points()[2].lat, closeTo(20, 0.00001));
|
||||
assertThat(filter.points()[2].lon, closeTo(-90, 0.00001));
|
||||
}
|
||||
|
||||
@Test public void testGeoPolygonFilter1() throws IOException {
|
||||
IndexQueryParser queryParser = newQueryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_polygon1.json");
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"and" : {
|
||||
"filters" : [
|
||||
{
|
||||
"term" : { "name.first" : "shay1" }
|
||||
},
|
||||
{
|
||||
"term" : { "name.first" : "shay4" }
|
||||
}
|
||||
],
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"fquery" : {
|
||||
"query" : {
|
||||
"term" : { "name.last" : "banon" }
|
||||
},
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"match_all" : {}
|
||||
},
|
||||
"filter" : {
|
||||
"geo_bounding_box" : {
|
||||
"person.location" : {
|
||||
"top_left" : [40, -70],
|
||||
"bottom_right" : [30, -80]
|
||||
},
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"match_all" : {}
|
||||
},
|
||||
"filter" : {
|
||||
"geo_distance" : {
|
||||
"distance" : "12mi",
|
||||
"person.location" : {
|
||||
"lat" : 40,
|
||||
"lon" : -70
|
||||
},
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"match_all" : {}
|
||||
},
|
||||
"filter" : {
|
||||
"geo_polygon" : {
|
||||
"person.location" : {
|
||||
"points" : [
|
||||
[40, -70],
|
||||
[30, -80],
|
||||
[20, -90]
|
||||
]
|
||||
},
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"prefix" : {
|
||||
"name.first" : "sh",
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
filtered : {
|
||||
query : {
|
||||
term : { "name.first" : "shay" }
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
filter : {
|
||||
prefix : { "name.first" : "sh" }
|
||||
"filter" : {
|
||||
"prefix" : { "name.first" : "sh" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"range" : {
|
||||
"age" : { "from" : "23", "to" : "54", "include_lower" : true, "include_upper": false},
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"term" : {
|
||||
"name.last" : "banon",
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"term" : {
|
||||
"name.last" : "banon"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
"filter" : {
|
||||
"terms" : {
|
||||
"name.last" : ["banon", "kimchy"],
|
||||
"_name" : "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
filtered : {
|
||||
query : {
|
||||
term : { "name.first" : "shay" }
|
||||
"filtered" : {
|
||||
"query" : {
|
||||
"term" : { "name.first" : "shay" }
|
||||
},
|
||||
filter : {
|
||||
terms : {
|
||||
"filter" : {
|
||||
"terms" : {
|
||||
"name.last" : ["banon", "kimchy"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Licensed to Elastic Search and Shay Banon under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. Elastic Search licenses this
|
||||
* file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.test.integration.search.matchedfilters;
|
||||
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.test.integration.AbstractNodesTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.*;
|
||||
import static org.elasticsearch.index.query.xcontent.FilterBuilders.*;
|
||||
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* @author kimchy (shay.banon)
|
||||
*/
|
||||
public class MatchedFiltersTests extends AbstractNodesTests {
|
||||
|
||||
private Client client;
|
||||
|
||||
@BeforeClass public void createNodes() throws Exception {
|
||||
startNode("server1");
|
||||
startNode("server2");
|
||||
client = getClient();
|
||||
}
|
||||
|
||||
@AfterClass public void closeNodes() {
|
||||
client.close();
|
||||
closeAllNodes();
|
||||
}
|
||||
|
||||
protected Client getClient() {
|
||||
return client("server1");
|
||||
}
|
||||
|
||||
@Test public void simpleMatchedFilter() throws Exception {
|
||||
try {
|
||||
client.admin().indices().prepareDelete("test").execute().actionGet();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
client.admin().indices().prepareCreate("test").execute().actionGet();
|
||||
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
|
||||
.field("name", "test1")
|
||||
.field("number", 1)
|
||||
.endObject()).execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject()
|
||||
.field("name", "test2")
|
||||
.field("number", 2)
|
||||
.endObject()).execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareFlush().execute().actionGet();
|
||||
|
||||
client.prepareIndex("test", "type1", "3").setSource(jsonBuilder().startObject()
|
||||
.field("name", "test3")
|
||||
.field("number", 3)
|
||||
.endObject()).execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareRefresh().execute().actionGet();
|
||||
|
||||
SearchResponse searchResponse = client.prepareSearch()
|
||||
.setQuery(filtered(matchAllQuery(), orFilter(rangeFilter("number").lte(2).filterName("test1"), rangeFilter("number").gt(2).filterName("test2"))))
|
||||
.execute().actionGet();
|
||||
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(3l));
|
||||
for (SearchHit hit : searchResponse.hits()) {
|
||||
if (hit.id().equals("1") || hit.id().equals("2")) {
|
||||
assertThat(hit.matchedFilters().length, equalTo(1));
|
||||
assertThat(hit.matchedFilters(), hasItemInArray("test1"));
|
||||
} else if (hit.id().equals("3")) {
|
||||
assertThat(hit.matchedFilters().length, equalTo(1));
|
||||
assertThat(hit.matchedFilters(), hasItemInArray("test2"));
|
||||
} else {
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue