Search API: Allow to name filters and return per hit the filters it matched on, closes #364.

This commit is contained in:
kimchy 2010-09-11 12:38:19 +03:00
parent ab2a655a59
commit eccc7d5ef2
56 changed files with 1093 additions and 257 deletions

View File

@ -19,7 +19,9 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.elasticsearch.common.collect.ImmutableMap;
/** /**
* The result of parsing a query. * The result of parsing a query.
@ -30,8 +32,16 @@ public class ParsedQuery {
private final Query query; 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.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() { public Query query() {
return this.query; return this.query;
} }
public ImmutableMap<String, Filter> namedFilters() {
return this.namedFilters;
}
} }

View File

@ -19,7 +19,11 @@
package org.elasticsearch.index.query.support; 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.DocumentMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.xcontent.QueryParseContext; import org.elasticsearch.index.query.xcontent.QueryParseContext;
@ -56,12 +60,6 @@ public final class QueryParsers {
return filter; return filter;
} }
DocumentMapper docMapper = smartFieldMappers.docMapper(); DocumentMapper docMapper = smartFieldMappers.docMapper();
BooleanFilter booleanFilter = new BooleanFilter(); return new AndFilter(ImmutableList.of(parseContext.cacheFilterIfPossible(docMapper.typeFilter()), filter));
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;
} }
} }

View File

@ -36,6 +36,8 @@ public class AndFilterBuilder extends BaseFilterBuilder {
private Boolean cache; private Boolean cache;
private String filterName;
public AndFilterBuilder(XContentFilterBuilder... filters) { public AndFilterBuilder(XContentFilterBuilder... filters) {
for (XContentFilterBuilder filter : filters) { for (XContentFilterBuilder filter : filters) {
this.filters.add(filter); this.filters.add(filter);
@ -58,6 +60,14 @@ public class AndFilterBuilder extends BaseFilterBuilder {
return this; 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 { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(AndFilterParser.NAME); builder.startObject(AndFilterParser.NAME);
builder.startArray("filters"); builder.startArray("filters");
@ -66,7 +76,10 @@ public class AndFilterBuilder extends BaseFilterBuilder {
} }
builder.endArray(); builder.endArray();
if (cache != null) { if (cache != null) {
builder.field("cache", cache); builder.field("_cache", cache);
}
if (filterName != null) {
builder.field("_name", filterName);
} }
builder.endObject(); builder.endObject();
} }

View File

@ -56,6 +56,7 @@ public class AndFilterParser extends AbstractIndexComponent implements XContentF
boolean cache = true; boolean cache = true;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token = parser.currentToken(); XContentParser.Token token = parser.currentToken();
if (token == XContentParser.Token.START_ARRAY) { if (token == XContentParser.Token.START_ARRAY) {
@ -77,8 +78,10 @@ public class AndFilterParser extends AbstractIndexComponent implements XContentF
} }
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("cache".equals(currentFieldName)) { if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); 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 // no need to cache this one
return new AndFilter(filters); AndFilter filter = new AndFilter(filters);
if (filterName != null) {
parseContext.addNamedFilter(filterName, filter);
}
return filter;
} }
} }

View File

@ -34,6 +34,8 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
private ArrayList<Clause> clauses = new ArrayList<Clause>(); private ArrayList<Clause> clauses = new ArrayList<Clause>();
private String filterName;
/** /**
* Adds a filter that <b>must</b> appear in the matching documents. * Adds a filter that <b>must</b> appear in the matching documents.
*/ */
@ -60,6 +62,14 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
return this; 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 { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("bool"); builder.startObject("bool");
for (Clause clause : clauses) { for (Clause clause : clauses) {
@ -74,6 +84,9 @@ public class BoolFilterBuilder extends BaseFilterBuilder {
clause.filterBuilder.toXContent(builder, params); clause.filterBuilder.toXContent(builder, params);
} }
} }
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }

View File

@ -53,6 +53,7 @@ public class BoolFilterParser extends AbstractIndexComponent implements XContent
boolean cache = true; boolean cache = true;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token; XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -81,22 +82,27 @@ public class BoolFilterParser extends AbstractIndexComponent implements XContent
} }
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("cache".equals(currentFieldName)) { if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); cache = parser.booleanValue();
} else if ("_name".equals(currentFieldName)) {
filterName = parser.text();
} }
} }
} }
BooleanFilter booleanFilter = new PublicBooleanFilter(); BooleanFilter filter = new PublicBooleanFilter();
for (OpenFilterClause filterClause : clauses) { for (OpenFilterClause filterClause : clauses) {
if (cache) { if (cache) {
filterClause.setFilter(parseContext.cacheFilterIfPossible(filterClause.getFilter())); 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...) // 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;
} }
} }

View File

@ -68,7 +68,7 @@ public class ConstantScoreQueryParser extends AbstractIndexComponent implements
} else if (token.isValue()) { } else if (token.isValue()) {
if ("boost".equals(currentFieldName)) { if ("boost".equals(currentFieldName)) {
boost = parser.floatValue(); boost = parser.floatValue();
} else if ("cache".equals(currentFieldName)) { } else if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); cache = parser.booleanValue();
} }
} }

View File

@ -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;
}
}

View File

@ -40,6 +40,8 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
private String bottomRightGeohash; private String bottomRightGeohash;
private String filterName;
public GeoBoundingBoxFilterBuilder(String name) { public GeoBoundingBoxFilterBuilder(String name) {
this.name = name; this.name = name;
} }
@ -68,6 +70,14 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
return this; 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 { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(GeoBoundingBoxFilterParser.NAME); builder.startObject(GeoBoundingBoxFilterParser.NAME);
@ -89,6 +99,10 @@ public class GeoBoundingBoxFilterBuilder extends BaseFilterBuilder {
} }
builder.endObject(); builder.endObject();
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -53,20 +53,21 @@ public class GeoBoundingBoxFilterParser extends AbstractIndexComponent implement
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); String latFieldName = null;
assert token == XContentParser.Token.FIELD_NAME; String lonFieldName = null;
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;
GeoBoundingBoxFilter.Point topLeft = new GeoBoundingBoxFilter.Point(); GeoBoundingBoxFilter.Point topLeft = new GeoBoundingBoxFilter.Point();
GeoBoundingBoxFilter.Point bottomRight = new GeoBoundingBoxFilter.Point(); GeoBoundingBoxFilter.Point bottomRight = new GeoBoundingBoxFilter.Point();
String filterName = null;
String currentFieldName = 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) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); 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(); MapperService mapperService = parseContext.mapperService();
FieldMapper mapper = mapperService.smartNameFieldMapper(latFieldName); FieldMapper mapper = mapperService.smartNameFieldMapper(latFieldName);
@ -154,6 +161,10 @@ public class GeoBoundingBoxFilterParser extends AbstractIndexComponent implement
lonFieldName = mapper.names().indexName(); 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;
} }
} }

View File

@ -42,6 +42,8 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
private GeoDistance geoDistance; private GeoDistance geoDistance;
private String filterName;
public GeoDistanceFilterBuilder(String name) { public GeoDistanceFilterBuilder(String name) {
this.name = name; this.name = name;
} }
@ -82,6 +84,11 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
return this; return this;
} }
public GeoDistanceFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(GeoDistanceFilterParser.NAME); builder.startObject(GeoDistanceFilterParser.NAME);
if (geohash != null) { if (geohash != null) {
@ -93,6 +100,9 @@ public class GeoDistanceFilterBuilder extends BaseFilterBuilder {
if (geoDistance != null) { if (geoDistance != null) {
builder.field("distance_type", geoDistance.name().toLowerCase()); builder.field("distance_type", geoDistance.name().toLowerCase());
} }
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -64,6 +64,7 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
XContentParser.Token token; XContentParser.Token token;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
double lat = 0; double lat = 0;
double lon = 0; double lon = 0;
@ -128,6 +129,8 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
lon = values[1]; lon = values[1];
latFieldName = currentFieldName.substring(0, currentFieldName.length() - XContentGeoPointFieldMapper.Names.GEOHASH_SUFFIX.length()) + XContentGeoPointFieldMapper.Names.LAT_SUFFIX; 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; lonFieldName = currentFieldName.substring(0, currentFieldName.length() - XContentGeoPointFieldMapper.Names.GEOHASH_SUFFIX.length()) + XContentGeoPointFieldMapper.Names.LON_SUFFIX;
} else if ("_name".equals(currentFieldName)) {
filterName = parser.text();
} else { } else {
// assume the value is the actual value // assume the value is the actual value
String value = parser.text(); String value = parser.text();
@ -164,6 +167,10 @@ public class GeoDistanceFilterParser extends AbstractIndexComponent implements X
} }
lonFieldName = mapper.names().indexName(); 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;
} }
} }

View File

@ -36,6 +36,8 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
private final List<GeoPolygonFilter.Point> points = Lists.newArrayList(); private final List<GeoPolygonFilter.Point> points = Lists.newArrayList();
private String filterName;
public GeoPolygonFilterBuilder(String name) { public GeoPolygonFilterBuilder(String name) {
this.name = name; this.name = name;
} }
@ -50,6 +52,11 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
return addPoint(values[0], values[1]); 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 { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(GeoPolygonFilterParser.NAME); builder.startObject(GeoPolygonFilterParser.NAME);
@ -61,6 +68,10 @@ public class GeoPolygonFilterBuilder extends BaseFilterBuilder {
builder.endArray(); builder.endArray();
builder.endObject(); builder.endObject();
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -66,19 +66,22 @@ public class GeoPolygonFilterParser extends AbstractIndexComponent implements XC
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); String latFieldName = null;
assert token == XContentParser.Token.FIELD_NAME; String lonFieldName = null;
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;
List<GeoPolygonFilter.Point> points = Lists.newArrayList(); List<GeoPolygonFilter.Point> points = Lists.newArrayList();
String filterName = null;
String currentFieldName = 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) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); 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()) { if (points.isEmpty()) {
throw new QueryParsingException(index, "no points defined for geo_polygon filter"); 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(); 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;
} }
} }

View File

@ -34,6 +34,8 @@ public class NotFilterBuilder extends BaseFilterBuilder {
private Boolean cache; private Boolean cache;
private String filterName;
public NotFilterBuilder(XContentFilterBuilder filter) { public NotFilterBuilder(XContentFilterBuilder filter) {
this.filter = filter; this.filter = filter;
} }
@ -46,12 +48,20 @@ public class NotFilterBuilder extends BaseFilterBuilder {
return this; return this;
} }
public NotFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NotFilterParser.NAME); builder.startObject(NotFilterParser.NAME);
builder.field("filter"); builder.field("filter");
filter.toXContent(builder, params); filter.toXContent(builder, params);
if (cache != null) { if (cache != null) {
builder.field("cache", cache); builder.field("_cache", cache);
}
if (filterName != null) {
builder.field("_name", filterName);
} }
builder.endObject(); builder.endObject();
} }

View File

@ -52,6 +52,7 @@ public class NotFilterParser extends AbstractIndexComponent implements XContentF
Filter filter = null; Filter filter = null;
boolean cache = true; boolean cache = true;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token; XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -62,8 +63,10 @@ public class NotFilterParser extends AbstractIndexComponent implements XContentF
filter = parseContext.parseInnerFilter(); filter = parseContext.parseInnerFilter();
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("cache".equals(currentFieldName)) { if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); 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); filter = parseContext.cacheFilterIfPossible(filter);
} }
// no need to cache this one // no need to cache this one
return new NotFilter(filter); NotFilter notFilter = new NotFilter(filter);
if (filterName != null) {
parseContext.addNamedFilter(filterName, notFilter);
}
return notFilter;
} }
} }

View File

@ -36,6 +36,8 @@ public class OrFilterBuilder extends BaseFilterBuilder {
private Boolean cache; private Boolean cache;
private String filterName;
public OrFilterBuilder(XContentFilterBuilder... filters) { public OrFilterBuilder(XContentFilterBuilder... filters) {
for (XContentFilterBuilder filter : filters) { for (XContentFilterBuilder filter : filters) {
this.filters.add(filter); this.filters.add(filter);
@ -58,6 +60,11 @@ public class OrFilterBuilder extends BaseFilterBuilder {
return this; return this;
} }
public OrFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(OrFilterParser.NAME); builder.startObject(OrFilterParser.NAME);
builder.startArray("filters"); builder.startArray("filters");
@ -66,7 +73,10 @@ public class OrFilterBuilder extends BaseFilterBuilder {
} }
builder.endArray(); builder.endArray();
if (cache != null) { if (cache != null) {
builder.field("cache", cache); builder.field("_cache", cache);
}
if (filterName != null) {
builder.field("_name", filterName);
} }
builder.endObject(); builder.endObject();
} }

View File

@ -56,6 +56,7 @@ public class OrFilterParser extends AbstractIndexComponent implements XContentFi
boolean cache = true; boolean cache = true;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
XContentParser.Token token = parser.currentToken(); XContentParser.Token token = parser.currentToken();
if (token == XContentParser.Token.START_ARRAY) { if (token == XContentParser.Token.START_ARRAY) {
@ -77,8 +78,10 @@ public class OrFilterParser extends AbstractIndexComponent implements XContentFi
} }
} }
} else if (token.isValue()) { } else if (token.isValue()) {
if ("cache".equals(currentFieldName)) { if ("_cache".equals(currentFieldName)) {
cache = parser.booleanValue(); 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 // no need to cache this one
return new OrFilter(filters); OrFilter filter = new OrFilter(filters);
if (filterName != null) {
parseContext.addNamedFilter(filterName, filter);
}
return filter;
} }
} }

View File

@ -35,6 +35,8 @@ public class PrefixFilterBuilder extends BaseFilterBuilder {
private final String prefix; private final String prefix;
private String filterName;
/** /**
* A filter that restricts search results to values that have a matching prefix in a given * A filter that restricts search results to values that have a matching prefix in a given
* field. * field.
@ -47,9 +49,17 @@ public class PrefixFilterBuilder extends BaseFilterBuilder {
this.prefix = prefix; this.prefix = prefix;
} }
public PrefixFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException { @Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(PrefixFilterParser.NAME); builder.startObject(PrefixFilterParser.NAME);
builder.field(name, prefix); builder.field(name, prefix);
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -53,15 +53,27 @@ public class PrefixFilterParser extends AbstractIndexComponent implements XConte
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); String fieldName = null;
assert token == XContentParser.Token.FIELD_NAME; String value = null;
String fieldName = parser.currentName();
parser.nextToken(); String filterName = null;
String value = parser.text(); String currentFieldName = null;
parser.nextToken(); 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) { 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); 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)); Filter filter = new PrefixFilter(new Term(fieldName, value));
return wrapSmartNameFilter(prefixFilter, smartNameFieldMappers, parseContext); filter = wrapSmartNameFilter(filter, smartNameFieldMappers, parseContext);
if (filterName != null) {
parseContext.addNamedFilter(filterName, filter);
}
return filter;
} }
} }

View File

@ -32,6 +32,8 @@ public class QueryFilterBuilder extends BaseFilterBuilder {
private final XContentQueryBuilder queryBuilder; private final XContentQueryBuilder queryBuilder;
private String filterName;
/** /**
* A filter that simply wraps a query. * A filter that simply wraps a query.
* *
@ -41,8 +43,21 @@ public class QueryFilterBuilder extends BaseFilterBuilder {
this.queryBuilder = queryBuilder; this.queryBuilder = queryBuilder;
} }
public QueryFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
if (filterName != null) {
builder.field(QueryFilterParser.NAME); builder.field(QueryFilterParser.NAME);
queryBuilder.toXContent(builder, params); queryBuilder.toXContent(builder, params);
} else {
builder.startObject(FQueryFilterParser.NAME);
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("_name", filterName);
builder.endObject();
}
} }
} }

View File

@ -24,7 +24,6 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.QueryWrapperFilter;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.AbstractIndexComponent; import org.elasticsearch.index.AbstractIndexComponent;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.QueryParsingException; 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 { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();
Query query = parseContext.parseInnerQuery(); Query query = parseContext.parseInnerQuery();
Filter filter = new QueryWrapperFilter(query); return new QueryWrapperFilter(query);
return filter;
} }
} }

View File

@ -22,6 +22,8 @@ package org.elasticsearch.index.query.xcontent;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.Similarity; 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.common.xcontent.XContentParser;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.cache.IndexCache; import org.elasticsearch.index.cache.IndexCache;
@ -35,6 +37,7 @@ import org.elasticsearch.script.ScriptService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
@ -55,6 +58,8 @@ public class QueryParseContext {
private final XContentQueryParserRegistry queryParserRegistry; private final XContentQueryParserRegistry queryParserRegistry;
private final Map<String, Filter> namedFilters = Maps.newHashMap();
private XContentParser parser; private XContentParser parser;
public QueryParseContext(Index index, XContentQueryParserRegistry queryParserRegistry, public QueryParseContext(Index index, XContentQueryParserRegistry queryParserRegistry,
@ -72,6 +77,7 @@ public class QueryParseContext {
public void reset(XContentParser jp) { public void reset(XContentParser jp) {
this.parser = jp; this.parser = jp;
this.namedFilters.clear();
} }
public XContentParser parser() { public XContentParser parser() {
@ -106,6 +112,17 @@ public class QueryParseContext {
return indexCache.filter().cache(filter); 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 { public Query parseInnerQuery() throws IOException, QueryParsingException {
// move to START object // move to START object
XContentParser.Token token; XContentParser.Token token;

View File

@ -40,6 +40,8 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
private boolean includeUpper = true; private boolean includeUpper = true;
private String filterName;
/** /**
* A filter that restricts search results to values that are within the given range. * 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; return this;
} }
public RangeFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(RangeFilterParser.NAME); builder.startObject(RangeFilterParser.NAME);
builder.startObject(name); builder.startObject(name);
builder.field("from", from); builder.field("from", from);
builder.field("to", to); builder.field("to", to);
builder.field("include_lower", includeLower); builder.field("include_lower", includeLower);
builder.field("include_upper", includeUpper); builder.field("include_upper", includeUpper);
builder.endObject(); builder.endObject();
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -52,20 +52,20 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); String fieldName = null;
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 from = null; String from = null;
String to = null; String to = null;
boolean includeLower = true; boolean includeLower = true;
boolean includeUpper = true; boolean includeUpper = true;
String filterName = null;
String currentFieldName = 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) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
@ -93,10 +93,12 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
} }
} }
} }
} else if (token.isValue()) {
// move to the next end object, to close the field name if ("_name".equals(currentFieldName)) {
token = parser.nextToken(); filterName = parser.text();
assert token == XContentParser.Token.END_OBJECT; }
}
}
Filter filter = null; Filter filter = null;
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName); MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
@ -108,6 +110,10 @@ public class RangeFilterParser extends AbstractIndexComponent implements XConten
if (filter == null) { if (filter == null) {
filter = new TermRangeFilter(fieldName, from, to, includeLower, includeUpper); 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;
} }
} }

View File

@ -35,6 +35,8 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
private Map<String, Object> params; private Map<String, Object> params;
private String filterName;
public ScriptFilterBuilder(String script) { public ScriptFilterBuilder(String script) {
this.script = script; this.script = script;
} }
@ -56,12 +58,20 @@ public class ScriptFilterBuilder extends BaseFilterBuilder {
return this; return this;
} }
public ScriptFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException { @Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(ScriptFilterParser.NAME); builder.startObject(ScriptFilterParser.NAME);
builder.field("script", script); builder.field("script", script);
if (this.params != null) { if (this.params != null) {
builder.field("params", this.params); builder.field("params", this.params);
} }
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -62,6 +62,7 @@ public class ScriptFilterParser extends AbstractIndexComponent implements XConte
String script = null; String script = null;
Map<String, Object> params = null; Map<String, Object> params = null;
String filterName = null;
String currentFieldName = null; String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
@ -73,6 +74,8 @@ public class ScriptFilterParser extends AbstractIndexComponent implements XConte
} else if (token.isValue()) { } else if (token.isValue()) {
if ("script".equals(currentFieldName)) { if ("script".equals(currentFieldName)) {
script = parser.text(); 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(); 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 { public static class ScriptFilter extends Filter {

View File

@ -34,6 +34,8 @@ public class TermFilterBuilder extends BaseFilterBuilder {
private final Object value; private final Object value;
private String filterName;
/** /**
* A filter for a field based on a term. * A filter for a field based on a term.
* *
@ -95,9 +97,17 @@ public class TermFilterBuilder extends BaseFilterBuilder {
this.value = value; this.value = value;
} }
public TermFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException { @Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(TermFilterParser.NAME); builder.startObject(TermFilterParser.NAME);
builder.field(name, value); builder.field(name, value);
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -53,15 +53,24 @@ public class TermFilterParser extends AbstractIndexComponent implements XContent
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); String fieldName = null;
assert token == XContentParser.Token.FIELD_NAME; String value = null;
String fieldName = parser.currentName();
String filterName = null;
parser.nextToken(); String currentFieldName = null;
String value = parser.text(); XContentParser.Token token;
// move to the next token (from VALUE) while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
parser.nextToken(); 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) { if (value == null) {
throw new QueryParsingException(index, "No value specified for term filter"); throw new QueryParsingException(index, "No value specified for term filter");
@ -77,6 +86,10 @@ public class TermFilterParser extends AbstractIndexComponent implements XContent
if (filter == null) { if (filter == null) {
filter = new TermFilter(new Term(fieldName, value)); 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;
} }
} }

View File

@ -34,6 +34,8 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
private final Object[] values; private final Object[] values;
private String filterName;
/** /**
* A filer for a field based on several terms matching on any of them. * 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; this.values = values;
} }
public TermsFilterBuilder filterName(String filterName) {
this.filterName = filterName;
return this;
}
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException { @Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(TermsFilterParser.NAME); builder.startObject(TermsFilterParser.NAME);
builder.startArray(name); builder.startArray(name);
@ -118,6 +125,11 @@ public class TermsFilterBuilder extends BaseFilterBuilder {
builder.value(value); builder.value(value);
} }
builder.endArray(); builder.endArray();
if (filterName != null) {
builder.field("_name", filterName);
}
builder.endObject(); builder.endObject();
} }
} }

View File

@ -55,12 +55,18 @@ public class TermsFilterParser extends AbstractIndexComponent implements XConten
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException { @Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
XContentParser.Token token = parser.nextToken(); MapperService.SmartNameFieldMappers smartNameFieldMappers = null;
assert token == XContentParser.Token.FIELD_NAME; TermsFilter termsFilter = new PublicTermsFilter();
String fieldName = parser.currentName(); 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; FieldMapper fieldMapper = null;
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName); smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) { if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) { if (smartNameFieldMappers.hasMapper()) {
fieldMapper = smartNameFieldMappers.mapper(); 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) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
String value = parser.text(); String value = parser.text();
if (value == null) { if (value == null) {
@ -84,9 +84,17 @@ public class TermsFilterParser extends AbstractIndexComponent implements XConten
} }
termsFilter.addTerm(new Term(fieldName, value)); termsFilter.addTerm(new Term(fieldName, value));
} }
parser.nextToken(); } else if (token.isValue()) {
if ("_name".equals(currentFieldName)) {
filterName = parser.text();
return wrapSmartNameFilter(termsFilter, smartNameFieldMappers, parseContext); }
}
}
Filter filter = wrapSmartNameFilter(termsFilter, smartNameFieldMappers, parseContext);
if (filterName != null) {
parseContext.addNamedFilter(filterName, filter);
}
return filter;
} }
} }

View File

@ -210,6 +210,7 @@ public class XContentIndexQueryParser extends AbstractIndexComponent implements
private ParsedQuery parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException { private ParsedQuery parse(QueryParseContext parseContext, XContentParser parser) throws IOException, QueryParsingException {
parseContext.reset(parser); parseContext.reset(parser);
return new ParsedQuery(parseContext.parseInnerQuery()); Query query = parseContext.parseInnerQuery();
return new ParsedQuery(query, parseContext.copyNamedFilters());
} }
} }

View File

@ -90,6 +90,7 @@ public class XContentQueryParserRegistry {
add(filterParsersMap, new GeoBoundingBoxFilterParser(index, indexSettings)); add(filterParsersMap, new GeoBoundingBoxFilterParser(index, indexSettings));
add(filterParsersMap, new GeoPolygonFilterParser(index, indexSettings)); add(filterParsersMap, new GeoPolygonFilterParser(index, indexSettings));
add(filterParsersMap, new QueryFilterParser(index, indexSettings)); add(filterParsersMap, new QueryFilterParser(index, indexSettings));
add(filterParsersMap, new FQueryFilterParser(index, indexSettings));
add(filterParsersMap, new BoolFilterParser(index, indexSettings)); add(filterParsersMap, new BoolFilterParser(index, indexSettings));
add(filterParsersMap, new AndFilterParser(index, indexSettings)); add(filterParsersMap, new AndFilterParser(index, indexSettings));
add(filterParsersMap, new OrFilterParser(index, indexSettings)); add(filterParsersMap, new OrFilterParser(index, indexSettings));

View File

@ -37,7 +37,7 @@ public class SearchContextException extends SearchException {
private static String buildMessage(SearchContext context, String msg) { private static String buildMessage(SearchContext context, String msg) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append('[').append(context.shardTarget().index()).append("][").append(context.shardTarget().shardId()).append("]: "); 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) { if (context.sort() != null) {
sb.append(",sort[").append(context.sort()).append("]"); sb.append(",sort[").append(context.sort()).append("]");
} }

View File

@ -147,6 +147,16 @@ public interface SearchHit extends Streamable, ToXContent, Iterable<SearchHitFie
*/ */
Object[] getSortValues(); 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. * The shard of the search hit.
*/ */

View File

@ -23,8 +23,12 @@ import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.Fieldable; import org.apache.lucene.document.Fieldable;
import org.apache.lucene.index.IndexReader; 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.ImmutableMap;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.docset.DocSet;
import org.elasticsearch.common.thread.ThreadLocals; import org.elasticsearch.common.thread.ThreadLocals;
import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.*;
import org.elasticsearch.search.SearchHitField; import org.elasticsearch.search.SearchHitField;
@ -41,6 +45,7 @@ import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -159,6 +164,27 @@ public class FetchPhase implements SearchPhase {
sameDocCache.clear(); 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); doExplanation(context, docId, searchHit);
} }
context.fetchResult().hits(new InternalSearchHits(hits, context.queryResult().topDocs().totalHits, context.queryResult().topDocs().getMaxScore())); context.fetchResult().hits(new InternalSearchHits(hits, context.queryResult().topDocs().totalHits, context.queryResult().topDocs().getMaxScore()));

View File

@ -80,7 +80,7 @@ public class ContextIndexSearcher extends ExtendedIndexSearcher {
} }
@Override public Query rewrite(Query original) throws IOException { @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... // optimize in case its the top level search query and we already rewrote it...
if (searchContext.queryRewritten()) { if (searchContext.queryRewritten()) {
return searchContext.query(); return searchContext.query();

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.internal;
import org.apache.lucene.search.Explanation; import org.apache.lucene.search.Explanation;
import org.elasticsearch.ElasticSearchParseException; import org.elasticsearch.ElasticSearchParseException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.Unicode; import org.elasticsearch.common.Unicode;
import org.elasticsearch.common.collect.ImmutableMap; import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.compress.lzf.LZFDecoder; import org.elasticsearch.common.compress.lzf.LZFDecoder;
@ -69,6 +70,8 @@ public class InternalSearchHit implements SearchHit {
private Object[] sortValues = EMPTY_SORT_VALUES; private Object[] sortValues = EMPTY_SORT_VALUES;
private String[] matchedFilters = Strings.EMPTY_ARRAY;
private Explanation explanation; private Explanation explanation;
@Nullable private SearchShardTarget shard; @Nullable private SearchShardTarget shard;
@ -248,6 +251,18 @@ public class InternalSearchHit implements SearchHit {
this.shard = target; 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 { @Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(); builder.startObject();
builder.field("_index", shard.index()); builder.field("_index", shard.index());
@ -305,6 +320,13 @@ public class InternalSearchHit implements SearchHit {
} }
builder.endArray(); builder.endArray();
} }
if (matchedFilters.length > 0) {
builder.startArray("matched_filters");
for (String matchedFilter : matchedFilters) {
builder.value(matchedFilter);
}
builder.endArray();
}
if (explanation() != null) { if (explanation() != null) {
builder.field("_explanation"); builder.field("_explanation");
buildExplanation(builder, 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) { if (shardLookupMap != null) {
int lookupId = in.readVInt(); int lookupId = in.readVInt();
if (lookupId > 0) { 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 (shardLookupMap == null) {
if (shard == null) { if (shard == null) {
out.writeBoolean(false); out.writeBoolean(false);

View File

@ -34,6 +34,7 @@ import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.IndexQueryParser; import org.elasticsearch.index.query.IndexQueryParser;
import org.elasticsearch.index.query.IndexQueryParserMissingException; import org.elasticsearch.index.query.IndexQueryParserMissingException;
import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
@ -94,7 +95,7 @@ public class SearchContext implements Releasable {
private String queryParserName; private String queryParserName;
private Query originalQuery; private ParsedQuery originalQuery;
private Query query; private Query query;
@ -272,17 +273,14 @@ public class SearchContext implements Releasable {
return this; return this;
} }
public SearchContext query(Query query) { public SearchContext parsedQuery(ParsedQuery query) {
queryRewritten = false; queryRewritten = false;
this.originalQuery = query; this.originalQuery = query;
this.query = query; this.query = query.query();
return this; return this;
} }
/** public ParsedQuery parsedQuery() {
* The original query to execute, unmodified.
*/
public Query originalQuery() {
return this.originalQuery; return this.originalQuery;
} }

View File

@ -19,7 +19,6 @@
package org.elasticsearch.search.query; package org.elasticsearch.search.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser; import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
@ -35,7 +34,6 @@ public class QueryBinaryParseElement implements SearchParseElement {
XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser(); XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser();
byte[] querySource = parser.binaryValue(); byte[] querySource = parser.binaryValue();
XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource); XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource);
Query query = indexQueryParser.parse(qSourceParser).query(); context.parsedQuery(indexQueryParser.parse(qSourceParser));
context.query(query);
} }
} }

View File

@ -19,7 +19,6 @@
package org.elasticsearch.search.query; package org.elasticsearch.search.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser; import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
@ -32,7 +31,6 @@ public class QueryParseElement implements SearchParseElement {
@Override public void parse(XContentParser parser, SearchContext context) throws Exception { @Override public void parse(XContentParser parser, SearchContext context) throws Exception {
XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser(); XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser();
Query query = indexQueryParser.parse(parser).query(); context.parsedQuery(indexQueryParser.parse(parser));
context.query(query);
} }
} }

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.lucene.search.function.BoostScoreFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.indices.TypeMissingException; import org.elasticsearch.indices.TypeMissingException;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.SearchParseException;
@ -67,7 +68,7 @@ public class QueryPhase implements SearchPhase {
throw new SearchParseException(context, "No query specified in search request"); throw new SearchParseException(context, "No query specified in search request");
} }
if (context.queryBoost() != 1.0f) { 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); facetsPhase.preProcess(context);
} }

View File

@ -36,6 +36,7 @@ import org.elasticsearch.index.cache.IndexCache;
import org.elasticsearch.index.engine.robin.RobinIndexEngine; import org.elasticsearch.index.engine.robin.RobinIndexEngine;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.IndexQueryParser; import org.elasticsearch.index.query.IndexQueryParser;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -375,6 +376,17 @@ public class SimpleIndexQueryParserTests {
assertThat(prefixFilter.getPrefix(), equalTo(new Term("name.first", "sh"))); 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 { @Test public void testPrefixQueryBoostQueryBuilder() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(prefixQuery("name.first", "sh").boost(2.0f)).query(); Query parsedQuery = queryParser.parse(prefixQuery("name.first", "sh").boost(2.0f)).query();
@ -483,6 +495,22 @@ public class SimpleIndexQueryParserTests {
assertThat(rangeFilter.includesMax(), equalTo(false)); 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 { @Test public void testBoolFilteredQuery() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/bool-filter.json"); 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"))); 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 { @Test public void testAndFilteredQuery2() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/and-filter2.json"); 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"))); 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 { @Test public void testTermsFilterQueryBuilder() throws Exception {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(filtered(termQuery("name.first", "shay"), termsFilter("name.last", "banon", "kimchy"))).query(); 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")); 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 { @Test public void testConstantScoreQueryBuilder() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(constantScoreQuery(termFilter("name.last", "banon"))).query(); 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"))); 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 { @Test public void testMoreLikeThisBuilder() throws Exception {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
Query parsedQuery = queryParser.parse(moreLikeThisQuery("name.first", "name.last").likeText("something").minTermFreq(1).maxQueryTerms(12)).query(); 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)); 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 { @Test public void testGeoDistanceFilter1() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_distance1.json"); 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)); 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 { @Test public void testGeoBoundingBoxFilter1() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_boundingbox1.json"); 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)); 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 { @Test public void testGeoPolygonFilter1() throws IOException {
IndexQueryParser queryParser = newQueryParser(); IndexQueryParser queryParser = newQueryParser();
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_polygon1.json"); String query = copyToStringFromClasspath("/org/elasticsearch/index/query/xcontent/geo_polygon1.json");

View File

@ -0,0 +1,20 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"and" : {
"filters" : [
{
"term" : { "name.first" : "shay1" }
},
{
"term" : { "name.first" : "shay4" }
}
],
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,15 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"fquery" : {
"query" : {
"term" : { "name.last" : "banon" }
},
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,16 @@
{
"filtered" : {
"query" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"person.location" : {
"top_left" : [40, -70],
"bottom_right" : [30, -80]
},
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,17 @@
{
"filtered" : {
"query" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "12mi",
"person.location" : {
"lat" : 40,
"lon" : -70
},
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"filtered" : {
"query" : {
"match_all" : {}
},
"filter" : {
"geo_polygon" : {
"person.location" : {
"points" : [
[40, -70],
[30, -80],
[20, -90]
]
},
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"prefix" : {
"name.first" : "sh",
"_name" : "test"
}
}
}
}

View File

@ -1,10 +1,10 @@
{ {
filtered : { "filtered" : {
query : { "query" : {
term : { "name.first" : "shay" } "term" : { "name.first" : "shay" }
}, },
filter : { "filter" : {
prefix : { "name.first" : "sh" } "prefix" : { "name.first" : "sh" }
} }
} }
} }

View File

@ -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"
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"term" : {
"name.last" : "banon",
"_name" : "test"
}
}
}
}

View File

@ -0,0 +1,12 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"term" : {
"name.last" : "banon"
}
}
}
}

View File

@ -0,0 +1,13 @@
{
"filtered" : {
"query" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"terms" : {
"name.last" : ["banon", "kimchy"],
"_name" : "test"
}
}
}
}

View File

@ -1,10 +1,10 @@
{ {
filtered : { "filtered" : {
query : { "query" : {
term : { "name.first" : "shay" } "term" : { "name.first" : "shay" }
}, },
filter : { "filter" : {
terms : { "terms" : {
"name.last" : ["banon", "kimchy"] "name.last" : ["banon", "kimchy"]
} }
} }

View File

@ -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;
}
}
}
}