All Field: Automatically detect when field level boosting is used, and optimize when its not, closes #2189.

This commit is contained in:
Shay Banon 2012-08-20 15:07:32 +02:00
parent e1fe89389c
commit 9aae62b4a6
6 changed files with 111 additions and 9 deletions

View File

@ -79,6 +79,10 @@ public class AllEntries extends Reader {
entries.add(entry); entries.add(entry);
} }
public boolean customBoost() {
return customBoost;
}
public void clear() { public void clear() {
this.entries.clear(); this.entries.clear();
this.current = null; this.current = null;

View File

@ -69,6 +69,9 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
private boolean enabled = Defaults.ENABLED; private boolean enabled = Defaults.ENABLED;
// an internal flag, automatically set if we encounter boosting
boolean autoBoost = false;
public Builder() { public Builder() {
super(Defaults.NAME); super(Defaults.NAME);
builder = this; builder = this;
@ -103,7 +106,7 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
@Override @Override
public AllFieldMapper build(BuilderContext context) { public AllFieldMapper build(BuilderContext context) {
return new AllFieldMapper(name, store, termVector, omitNorms, indexOptions, return new AllFieldMapper(name, store, termVector, omitNorms, indexOptions,
indexAnalyzer, searchAnalyzer, enabled); indexAnalyzer, searchAnalyzer, enabled, autoBoost);
} }
} }
@ -117,6 +120,8 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
Object fieldNode = entry.getValue(); Object fieldNode = entry.getValue();
if (fieldName.equals("enabled")) { if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode)); builder.enabled(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("auto_boost")) {
builder.autoBoost = nodeBooleanValue(fieldNode);
} }
} }
return builder; return builder;
@ -125,16 +130,24 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
private boolean enabled; private boolean enabled;
// The autoBoost flag is automatically set based on indexed docs on the mappings
// if a doc is indexed with a specific boost value and part of _all, it is automatically
// set to true. This allows to optimize (automatically, which we like) for the common case
// where fields don't usually have boost associated with them, and we don't need to use the
// special SpanTermQuery to look at payloads
private volatile boolean autoBoost;
public AllFieldMapper() { public AllFieldMapper() {
this(Defaults.NAME, Defaults.STORE, Defaults.TERM_VECTOR, Defaults.OMIT_NORMS, Defaults.INDEX_OPTIONS, null, null, Defaults.ENABLED); this(Defaults.NAME, Defaults.STORE, Defaults.TERM_VECTOR, Defaults.OMIT_NORMS, Defaults.INDEX_OPTIONS, null, null, Defaults.ENABLED, false);
} }
protected AllFieldMapper(String name, Field.Store store, Field.TermVector termVector, boolean omitNorms, IndexOptions indexOptions, protected AllFieldMapper(String name, Field.Store store, Field.TermVector termVector, boolean omitNorms, IndexOptions indexOptions,
NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer, boolean enabled) { NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer, boolean enabled, boolean autoBoost) {
super(new Names(name, name, name, name), Field.Index.ANALYZED, store, termVector, 1.0f, omitNorms, indexOptions, indexAnalyzer, super(new Names(name, name, name, name), Field.Index.ANALYZED, store, termVector, 1.0f, omitNorms, indexOptions, indexAnalyzer,
searchAnalyzer); searchAnalyzer);
this.enabled = enabled; this.enabled = enabled;
this.autoBoost = autoBoost;
} }
public boolean enabled() { public boolean enabled() {
@ -143,16 +156,19 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
@Override @Override
public Query queryStringTermQuery(Term term) { public Query queryStringTermQuery(Term term) {
if (!autoBoost) {
return new TermQuery(term);
}
if (indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) { if (indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) {
return new AllTermQuery(term); return new AllTermQuery(term);
} }
return new TermQuery(term); return new TermQuery(term);
} }
@Override @Override
public Query fieldQuery(String value, QueryParseContext context) { public Query fieldQuery(String value, QueryParseContext context) {
return queryStringTermQuery(names().createIndexNameTerm(value)); return queryStringTermQuery(names().createIndexNameTerm(value));
} }
@Override @Override
@ -186,6 +202,13 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
// reset the entries // reset the entries
context.allEntries().reset(); context.allEntries().reset();
// if the autoBoost flag is not set, and we indexed a doc with custom boost, make
// sure to update the flag, and notify mappings on change
if (!autoBoost && context.allEntries().customBoost()) {
autoBoost = true;
context.setMappingsModified();
}
Analyzer analyzer = findAnalyzer(context); Analyzer analyzer = findAnalyzer(context);
return new AllField(names.indexName(), store, termVector, context.allEntries(), analyzer); return new AllField(names.indexName(), store, termVector, context.allEntries(), analyzer);
} }
@ -240,6 +263,9 @@ public class AllFieldMapper extends AbstractFieldMapper<Void> implements Interna
if (enabled != Defaults.ENABLED) { if (enabled != Defaults.ENABLED) {
builder.field("enabled", enabled); builder.field("enabled", enabled);
} }
if (autoBoost != false) {
builder.field("auto_boost", autoBoost);
}
if (store != Defaults.STORE) { if (store != Defaults.STORE) {
builder.field("store", store.name().toLowerCase()); builder.field("store", store.name().toLowerCase());
} }

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.lucene.all.AllTermQuery;
import org.elasticsearch.common.lucene.all.AllTokenStream; import org.elasticsearch.common.lucene.all.AllTokenStream;
import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.FieldMappers;
import org.elasticsearch.test.unit.index.mapper.MapperTests; import org.elasticsearch.test.unit.index.mapper.MapperTests;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -60,7 +59,23 @@ public class SimpleAllMapperTests {
FieldMapper mapper = docMapper.mappers().smartNameFieldMapper("_all"); FieldMapper mapper = docMapper.mappers().smartNameFieldMapper("_all");
assertThat(mapper.queryStringTermQuery(new Term("_all", "foobar")), Matchers.instanceOf(AllTermQuery.class)); assertThat(mapper.queryStringTermQuery(new Term("_all", "foobar")), Matchers.instanceOf(AllTermQuery.class));
} }
@Test
public void testAllMappersNoBoost() throws Exception {
String mapping = copyToStringFromClasspath("/org/elasticsearch/test/unit/index/mapper/all/noboost-mapping.json");
DocumentMapper docMapper = MapperTests.newParser().parse(mapping);
byte[] json = copyToBytesFromClasspath("/org/elasticsearch/test/unit/index/mapper/all/test1.json");
Document doc = docMapper.parse(new BytesArray(json)).rootDoc();
AllField field = (AllField) doc.getFieldable("_all");
AllEntries allEntries = ((AllTokenStream) field.tokenStreamValue()).allEntries();
assertThat(allEntries.fields().size(), equalTo(3));
assertThat(allEntries.fields().contains("address.last.location"), equalTo(true));
assertThat(allEntries.fields().contains("name.last"), equalTo(true));
assertThat(allEntries.fields().contains("simple1"), equalTo(true));
FieldMapper mapper = docMapper.mappers().smartNameFieldMapper("_all");
assertThat(mapper.queryStringTermQuery(new Term("_all", "foobar")), Matchers.instanceOf(TermQuery.class));
}
@Test @Test
public void testAllMappersTermQuery() throws Exception { public void testAllMappersTermQuery() throws Exception {
String mapping = copyToStringFromClasspath("/org/elasticsearch/test/unit/index/mapper/all/mapping_omit_positions_on_all.json"); String mapping = copyToStringFromClasspath("/org/elasticsearch/test/unit/index/mapper/all/mapping_omit_positions_on_all.json");

View File

@ -15,7 +15,8 @@
}, },
"last":{ "last":{
"type":"string", "type":"string",
"index":"not_analyzed" "index":"not_analyzed",
"boost":2.0
} }
} }
}, },

View File

@ -0,0 +1,55 @@
{
"person":{
"_all":{
"enabled":true
},
"properties":{
"name":{
"type":"object",
"dynamic":false,
"properties":{
"first":{
"type":"string",
"store":"yes",
"include_in_all":false
},
"last":{
"type":"string",
"index":"not_analyzed"
}
}
},
"address":{
"type":"object",
"include_in_all":false,
"properties":{
"first":{
"properties":{
"location":{
"type":"string",
"store":"yes",
"index_name":"firstLocation"
}
}
},
"last":{
"properties":{
"location":{
"type":"string",
"include_in_all":true
}
}
}
}
},
"simple1":{
"type":"long",
"include_in_all":true
},
"simple2":{
"type":"long",
"include_in_all":false
}
}
}
}

View File

@ -16,7 +16,8 @@
}, },
"last":{ "last":{
"type":"string", "type":"string",
"index":"not_analyzed" "index":"not_analyzed",
"boost":2.0
} }
} }
}, },