Improved filtering by _parent field

In the _parent field the type and id of the parent are stored as type#id, because of this a term filter on the _parent field with the parent id is always resolved to a terms filter with a type / id combination for each type in the mapping.

This can be improved by automatically use the most optimized filter (either term or terms) based on the number of parent types in the mapping.

Also added support to use the parent type in the term filter for the _parent field. Like this:
```json
{
   "term" : {
        "_parent" : "parent_type#1"
    }
}
```
This will then always automatically use the term filter.

Closes #3454
This commit is contained in:
Martijn van Groningen 2013-08-07 13:20:21 +02:00
parent 5e0b1621b4
commit 73c038fb48
2 changed files with 74 additions and 8 deletions

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.mapper.internal;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.FieldInfo.IndexOptions;
import org.apache.lucene.index.Term;
import org.apache.lucene.queries.TermsFilter;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
@ -31,6 +32,8 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.lucene.search.TermFilter;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.codec.postingsformat.PostingsFormatProvider;
@ -239,13 +242,29 @@ public class ParentFieldMapper extends AbstractFieldMapper<Uid> implements Inter
return super.termFilter(value, context);
}
BytesRef bValue = BytesRefs.toBytesRef(value);
// we use all types, cause we don't know if its exact or not...
BytesRef[] typesValues = new BytesRef[context.mapperService().types().size()];
int i = 0;
for (String type : context.mapperService().types()) {
typesValues[i++] = Uid.createUidAsBytes(type, bValue);
if (Uid.hasDelimiter(bValue)) {
return new TermFilter(new Term(names.indexName(), bValue));
}
List<String> parentTypes = new ArrayList<String>(context.mapperService().types().size());
for (DocumentMapper documentMapper : context.mapperService()) {
if (documentMapper.parentFieldMapper() == null) {
parentTypes.add(documentMapper.type());
}
}
if (parentTypes.isEmpty()) {
return Queries.MATCH_NO_FILTER;
} else if (parentTypes.size() == 1) {
return new TermFilter(new Term(names.indexName(), Uid.createUidAsBytes(parentTypes.get(0), bValue)));
} else {
// we use all types, cause we don't know if its exact or not...
List<BytesRef> typesValues = new ArrayList<BytesRef>(parentTypes.size());
for (String type : context.mapperService().types()) {
typesValues.add(Uid.createUidAsBytes(type, bValue));
}
return new TermsFilter(names.indexName(), typesValues);
}
return new TermsFilter(names.indexName(), typesValues);
}
@Override

View File

@ -46,6 +46,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.facet.FacetBuilders.termsFacet;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.Matchers.*;
@ -1514,7 +1515,6 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
@Test
// See also issue: https://github.com/elasticsearch/elasticsearch/issues/3203
public void testHasChildQueryWithMinimumScore() throws Exception {
client().admin().indices().prepareCreate("test")
.setSettings(
ImmutableSettings.settingsBuilder()
@ -1533,7 +1533,7 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
client().prepareIndex("test", "child", "c3").setSource("c_field", "x").setParent("p2").execute().actionGet();
client().prepareIndex("test", "child", "c4").setSource("c_field", "x").setParent("p2").execute().actionGet();
client().prepareIndex("test", "child", "c5").setSource("c_field", "x").setParent("p2").execute().actionGet();
client().admin().indices().prepareRefresh("test").execute().actionGet();
refresh();
SearchResponse searchResponse = client().prepareSearch("test")
.setQuery(hasChildQuery("child", matchAllQuery()).scoreType("sum"))
@ -1546,4 +1546,51 @@ public class SimpleChildQuerySearchTests extends AbstractSharedClusterTest {
assertThat(searchResponse.getHits().getAt(0).score(), equalTo(3.0f));
}
@Test
public void testParentFieldFilter() throws Exception {
client().admin().indices().prepareCreate("test")
.setSettings(
ImmutableSettings.settingsBuilder()
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 1)
.put("index.refresh_interval", -1)
).execute().actionGet();
client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
client().admin().indices().preparePutMapping("test").setType("child").setSource(jsonBuilder().startObject().startObject("type")
.startObject("_parent").field("type", "parent").endObject()
.endObject().endObject()).execute().actionGet();
SearchResponse response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1")))
.execute().actionGet();
assertHitCount(response, 0l);
client().prepareIndex("test", "parent", "p1").setSource("p_field", "value").execute().actionGet();
client().prepareIndex("test", "child", "c1").setSource("c_field", "value").setParent("p1")
.execute().actionGet();
response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1")))
.execute().actionGet();
assertHitCount(response, 0l);
refresh();
response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1")))
.execute().actionGet();
assertHitCount(response, 1l);
response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "parent#p1")))
.execute().actionGet();
assertHitCount(response, 1l);
client().prepareIndex("test", "parent2", "p1").setSource("p_field", "value")
.setRefresh(true).execute().actionGet();
response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "p1")))
.execute().actionGet();
assertHitCount(response, 1l);
response = client().prepareSearch("test").setQuery(filteredQuery(matchAllQuery(), termFilter("_parent", "parent#p1")))
.execute().actionGet();
assertHitCount(response, 1l);
}
}