Query DSL: Ids Filter / Query - allow to execute it with no type defined / several types, closes #969.
This commit is contained in:
parent
c90820e03b
commit
d1d631794d
|
@ -54,6 +54,7 @@ import java.io.InputStreamReader;
|
|||
import java.io.Reader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -234,6 +235,10 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
|
|||
return mappers.containsKey(mappingType);
|
||||
}
|
||||
|
||||
public Collection<String> types() {
|
||||
return mappers.keySet();
|
||||
}
|
||||
|
||||
public DocumentMapper documentMapper(String type) {
|
||||
return mappers.get(type);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.index.query.xcontent;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
|
||||
/**
|
||||
* A static factory for simple "import static" usage.
|
||||
*
|
||||
|
@ -34,12 +36,12 @@ public abstract class FilterBuilders {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new ids filter with the provided doc/mapping type.
|
||||
* Creates a new ids filter with the provided doc/mapping types.
|
||||
*
|
||||
* @param type The type
|
||||
* @param types The types to match the ids against.
|
||||
*/
|
||||
public static IdsFilterBuilder idsFilter(String type) {
|
||||
return new IdsFilterBuilder(type);
|
||||
public static IdsFilterBuilder idsFilter(@Nullable String... types) {
|
||||
return new IdsFilterBuilder(types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,7 +31,7 @@ import java.util.List;
|
|||
*/
|
||||
public class IdsFilterBuilder extends BaseFilterBuilder {
|
||||
|
||||
private String type;
|
||||
private final List<String> types;
|
||||
|
||||
private List<String> values = new ArrayList<String>();
|
||||
|
||||
|
@ -40,8 +40,8 @@ public class IdsFilterBuilder extends BaseFilterBuilder {
|
|||
/**
|
||||
* Create an ids filter based on the type.
|
||||
*/
|
||||
public IdsFilterBuilder(String type) {
|
||||
this.type = type;
|
||||
public IdsFilterBuilder(String... types) {
|
||||
this.types = types == null ? null : Arrays.asList(types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +69,17 @@ public class IdsFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
@Override public void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(IdsFilterParser.NAME);
|
||||
builder.field("type", type);
|
||||
if (types != null) {
|
||||
if (types.size() == 1) {
|
||||
builder.field("type", types.get(0));
|
||||
} else {
|
||||
builder.startArray("types");
|
||||
for (Object type : types) {
|
||||
builder.value(type);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
}
|
||||
builder.startArray("values");
|
||||
for (Object value : values) {
|
||||
builder.value(value);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
package org.elasticsearch.index.query.xcontent;
|
||||
|
||||
import org.apache.lucene.search.Filter;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.collect.Iterables;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -31,6 +33,7 @@ import org.elasticsearch.index.settings.IndexSettings;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class IdsFilterParser extends AbstractIndexComponent implements XContentFilterParser {
|
||||
|
@ -49,7 +52,7 @@ public class IdsFilterParser extends AbstractIndexComponent implements XContentF
|
|||
XContentParser parser = parseContext.parser();
|
||||
|
||||
List<String> ids = new ArrayList<String>();
|
||||
String type = null;
|
||||
Collection<String> types = null;
|
||||
String filterName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
@ -65,24 +68,36 @@ public class IdsFilterParser extends AbstractIndexComponent implements XContentF
|
|||
}
|
||||
ids.add(value);
|
||||
}
|
||||
} else if ("types".equals(currentFieldName) || "type".equals(currentFieldName)) {
|
||||
types = new ArrayList<String>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
String value = parser.textOrNull();
|
||||
if (value == null) {
|
||||
throw new QueryParsingException(index, "No type specified for term filter");
|
||||
}
|
||||
types.add(value);
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("type".equals(currentFieldName) || "_type".equals(currentFieldName)) {
|
||||
type = parser.text();
|
||||
types = ImmutableList.of(parser.text());
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
filterName = parser.text();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
throw new QueryParsingException(index, "[ids] filter, no type provided");
|
||||
}
|
||||
if (ids.size() == 0) {
|
||||
throw new QueryParsingException(index, "[ids] filter, no ids values provided");
|
||||
}
|
||||
|
||||
UidFilter filter = new UidFilter(type, ids, parseContext.indexCache().bloomCache());
|
||||
if (types == null || types.isEmpty()) {
|
||||
types = parseContext.mapperService().types();
|
||||
} else if (types.size() == 1 && Iterables.getFirst(types, null).equals("_all")) {
|
||||
types = parseContext.mapperService().types();
|
||||
}
|
||||
|
||||
UidFilter filter = new UidFilter(types, ids, parseContext.indexCache().bloomCache());
|
||||
if (filterName != null) {
|
||||
parseContext.addNamedFilter(filterName, filter);
|
||||
}
|
||||
|
|
|
@ -31,14 +31,14 @@ import java.util.List;
|
|||
*/
|
||||
public class IdsQueryBuilder extends BaseQueryBuilder {
|
||||
|
||||
private String type;
|
||||
private final List<String> types;
|
||||
|
||||
private List<String> values = new ArrayList<String>();
|
||||
|
||||
private float boost = -1;
|
||||
|
||||
public IdsQueryBuilder(String type) {
|
||||
this.type = type;
|
||||
public IdsQueryBuilder(String... types) {
|
||||
this.types = types == null ? null : Arrays.asList(types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,7 +67,17 @@ public class IdsQueryBuilder extends BaseQueryBuilder {
|
|||
|
||||
@Override protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(IdsQueryParser.NAME);
|
||||
builder.field("type", type);
|
||||
if (types != null) {
|
||||
if (types.size() == 1) {
|
||||
builder.field("type", types.get(0));
|
||||
} else {
|
||||
builder.startArray("types");
|
||||
for (Object type : types) {
|
||||
builder.value(type);
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
}
|
||||
builder.startArray("values");
|
||||
for (Object value : values) {
|
||||
builder.value(value);
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.index.query.xcontent;
|
|||
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.collect.Iterables;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -32,6 +34,7 @@ import org.elasticsearch.index.settings.IndexSettings;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -53,7 +56,7 @@ public class IdsQueryParser extends AbstractIndexComponent implements XContentQu
|
|||
XContentParser parser = parseContext.parser();
|
||||
|
||||
List<String> ids = new ArrayList<String>();
|
||||
String type = null;
|
||||
Collection<String> types = null;
|
||||
String currentFieldName = null;
|
||||
float boost = 1.0f;
|
||||
XContentParser.Token token;
|
||||
|
@ -69,24 +72,36 @@ public class IdsQueryParser extends AbstractIndexComponent implements XContentQu
|
|||
}
|
||||
ids.add(value);
|
||||
}
|
||||
} else if ("types".equals(currentFieldName) || "type".equals(currentFieldName)) {
|
||||
types = new ArrayList<String>();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
String value = parser.textOrNull();
|
||||
if (value == null) {
|
||||
throw new QueryParsingException(index, "No type specified for term filter");
|
||||
}
|
||||
types.add(value);
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("type".equals(currentFieldName) || "_type".equals(currentFieldName)) {
|
||||
type = parser.text();
|
||||
types = ImmutableList.of(parser.text());
|
||||
} else if ("boost".equals(currentFieldName)) {
|
||||
boost = parser.floatValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null) {
|
||||
throw new QueryParsingException(index, "[ids] query, no type provided");
|
||||
}
|
||||
if (ids.size() == 0) {
|
||||
throw new QueryParsingException(index, "[ids] query, no ids values provided");
|
||||
}
|
||||
|
||||
UidFilter filter = new UidFilter(type, ids, parseContext.indexCache().bloomCache());
|
||||
if (types == null || types.isEmpty()) {
|
||||
types = parseContext.mapperService().types();
|
||||
} else if (types.size() == 1 && Iterables.getFirst(types, null).equals("_all")) {
|
||||
types = parseContext.mapperService().types();
|
||||
}
|
||||
|
||||
UidFilter filter = new UidFilter(types, ids, parseContext.indexCache().bloomCache());
|
||||
// no need for constant score filter, since we don't cache the filter, and it always takes deletes into account
|
||||
ConstantScoreQuery query = new ConstantScoreQuery(filter);
|
||||
query.setBoost(boost);
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.elasticsearch.index.query.xcontent;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
|
||||
/**
|
||||
* A static factory for simple "import static" usage.
|
||||
*
|
||||
|
@ -73,12 +75,12 @@ public abstract class QueryBuilders {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a query that will match only specific ids within a type.
|
||||
* Constructs a query that will match only specific ids within types.
|
||||
*
|
||||
* @param type The mapping/doc type
|
||||
* @param types The mapping/doc type
|
||||
*/
|
||||
public static IdsQueryBuilder idsQuery(String type) {
|
||||
return new IdsQueryBuilder(type);
|
||||
public static IdsQueryBuilder idsQuery(@Nullable String... types) {
|
||||
return new IdsQueryBuilder(types);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,22 +33,24 @@ import org.elasticsearch.index.mapper.Uid;
|
|||
import org.elasticsearch.index.mapper.UidFieldMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class UidFilter extends Filter {
|
||||
|
||||
private final Term[] uids;
|
||||
private final List<Term> uids;
|
||||
|
||||
private final BloomCache bloomCache;
|
||||
|
||||
public UidFilter(String type, List<String> ids, BloomCache bloomCache) {
|
||||
public UidFilter(Collection<String> types, List<String> ids, BloomCache bloomCache) {
|
||||
this.bloomCache = bloomCache;
|
||||
uids = new Term[ids.size()];
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
uids[i] = new Term(UidFieldMapper.NAME, Uid.createUid(type, ids.get(i)));
|
||||
this.uids = new ArrayList<Term>(types.size() * ids.size());
|
||||
for (String type : types) {
|
||||
for (String id : ids) {
|
||||
uids.add(new Term(UidFieldMapper.NAME, Uid.createUid(type, id)));
|
||||
}
|
||||
}
|
||||
Arrays.sort(uids);
|
||||
}
|
||||
|
||||
// TODO Optimizations
|
||||
|
@ -86,15 +88,11 @@ public class UidFilter extends Filter {
|
|||
@Override public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
UidFilter uidFilter = (UidFilter) o;
|
||||
|
||||
if (!Arrays.equals(uids, uidFilter.uids)) return false;
|
||||
|
||||
return true;
|
||||
return !uids.equals(uidFilter.uids);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return uids != null ? Arrays.hashCode(uids) : 0;
|
||||
return uids.hashCode();
|
||||
}
|
||||
}
|
|
@ -174,11 +174,23 @@ public class SimpleQueryTests extends AbstractNodesTests {
|
|||
assertThat(searchResponse.hits().getAt(0).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.hits().getAt(1).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
|
||||
// no type
|
||||
searchResponse = client.prepareSearch().setQuery(constantScoreQuery(idsFilter().ids("1", "3"))).execute().actionGet();
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
|
||||
assertThat(searchResponse.hits().getAt(0).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.hits().getAt(1).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
|
||||
searchResponse = client.prepareSearch().setQuery(idsQuery("type1").ids("1", "3")).execute().actionGet();
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
|
||||
assertThat(searchResponse.hits().getAt(0).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.hits().getAt(1).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
|
||||
// no type
|
||||
searchResponse = client.prepareSearch().setQuery(idsQuery().ids("1", "3")).execute().actionGet();
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
|
||||
assertThat(searchResponse.hits().getAt(0).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
assertThat(searchResponse.hits().getAt(1).id(), anyOf(equalTo("1"), equalTo("3")));
|
||||
|
||||
searchResponse = client.prepareSearch().setQuery(idsQuery("type1").ids("7", "10")).execute().actionGet();
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(0l));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue