earch API: Automatically identify "script" fields on the field elements in search, close #357.

This commit is contained in:
kimchy 2010-09-06 11:51:31 +03:00
parent 243b7455e8
commit f270fc00d2
6 changed files with 55 additions and 32 deletions

View File

@ -135,7 +135,7 @@ public class FetchPhase implements SearchPhase {
hitField.values().add(value); hitField.values().add(value);
} }
if (context.scriptFields() != null) { if (context.hasScriptFields()) {
sameDocCache.clear(); sameDocCache.clear();
int readerIndex = context.searcher().readerIndex(docId); int readerIndex = context.searcher().readerIndex(docId);
IndexReader subReader = context.searcher().subReaders()[readerIndex]; IndexReader subReader = context.searcher().subReaders()[readerIndex];
@ -203,12 +203,12 @@ public class FetchPhase implements SearchPhase {
} }
private FieldSelector buildFieldSelectors(SearchContext context) { private FieldSelector buildFieldSelectors(SearchContext context) {
if (context.scriptFields() != null && context.fieldNames() == null) { if (context.hasScriptFields() && !context.hasFieldNames()) {
// we ask for script fields, and no field names, don't load the source // we ask for script fields, and no field names, don't load the source
return UidFieldSelector.INSTANCE; return UidFieldSelector.INSTANCE;
} }
if (context.fieldNames() == null) { if (!context.hasFieldNames()) {
return new UidAndSourceFieldSelector(); return new UidAndSourceFieldSelector();
} }

View File

@ -19,13 +19,12 @@
package org.elasticsearch.search.fetch; package org.elasticsearch.search.fetch;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.field.function.script.ScriptFieldsFunction;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.fetch.script.ScriptFieldsContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.util.ArrayList;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
@ -34,17 +33,28 @@ public class FieldsParseElement implements SearchParseElement {
@Override public void parse(XContentParser parser, SearchContext context) throws Exception { @Override public void parse(XContentParser parser, SearchContext context) throws Exception {
XContentParser.Token token = parser.currentToken(); XContentParser.Token token = parser.currentToken();
if (token == XContentParser.Token.START_ARRAY) { if (token == XContentParser.Token.START_ARRAY) {
ArrayList<String> fieldNames = new ArrayList<String>(); boolean added = false;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
fieldNames.add(parser.text()); added = true;
String name = parser.text();
if (name.contains("_source.") || name.contains("doc[")) {
// script field to load from source
context.scriptFields().add(new ScriptFieldsContext.ScriptField(name, new ScriptFieldsFunction(name, context.scriptService(), context.mapperService(), context.fieldDataCache()), null));
} else {
context.fieldNames().add(name);
}
} }
if (fieldNames.isEmpty()) { if (!added) {
context.fieldNames(ImmutableList.<String>of()); context.emptyFieldNames();
} else {
context.fieldNames(fieldNames);
} }
} else if (token == XContentParser.Token.VALUE_STRING) { } else if (token == XContentParser.Token.VALUE_STRING) {
context.fieldNames(ImmutableList.of(parser.text())); String name = parser.text();
if (name.contains("_source.") || name.contains("doc[")) {
// script field to load from source
context.scriptFields().add(new ScriptFieldsContext.ScriptField(name, new ScriptFieldsFunction(name, context.scriptService(), context.mapperService(), context.fieldDataCache()), null));
} else {
context.fieldNames().add(name);
}
} }
} }
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search.fetch.script; package org.elasticsearch.search.fetch.script;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.index.field.function.script.ScriptFieldsFunction; import org.elasticsearch.index.field.function.script.ScriptFieldsFunction;
import java.util.List; import java.util.List;
@ -53,10 +54,13 @@ public class ScriptFieldsContext {
} }
} }
private List<ScriptField> fields; private List<ScriptField> fields = Lists.newArrayList();
public ScriptFieldsContext(List<ScriptField> fields) { public ScriptFieldsContext() {
this.fields = fields; }
public void add(ScriptField field) {
this.fields.add(field);
} }
public List<ScriptField> fields() { public List<ScriptField> fields() {

View File

@ -19,14 +19,11 @@
package org.elasticsearch.search.fetch.script; package org.elasticsearch.search.fetch.script;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.field.function.script.ScriptFieldsFunction; import org.elasticsearch.index.field.function.script.ScriptFieldsFunction;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -48,7 +45,6 @@ public class ScriptFieldsParseElement implements SearchParseElement {
@Override public void parse(XContentParser parser, SearchContext context) throws Exception { @Override public void parse(XContentParser parser, SearchContext context) throws Exception {
XContentParser.Token token; XContentParser.Token token;
String currentFieldName = null; String currentFieldName = null;
List<ScriptFieldsContext.ScriptField> scriptFields = Lists.newArrayList();
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();
@ -65,11 +61,8 @@ public class ScriptFieldsParseElement implements SearchParseElement {
script = parser.text(); script = parser.text();
} }
} }
scriptFields.add(new ScriptFieldsContext.ScriptField(fieldName, context.scriptFields().add(new ScriptFieldsContext.ScriptField(fieldName, new ScriptFieldsFunction(script, context.scriptService(), context.mapperService(), context.fieldDataCache()), params));
new ScriptFieldsFunction(script, context.scriptService(), context.mapperService(), context.fieldDataCache()),
params == null ? new HashMap<String, Object>() : params));
} }
} }
context.scriptFields(new ScriptFieldsContext(scriptFields));
} }
} }

View File

@ -22,6 +22,8 @@ package org.elasticsearch.search.internal;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort; import org.apache.lucene.search.Sort;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasable;
import org.elasticsearch.common.timer.Timeout; import org.elasticsearch.common.timer.Timeout;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
@ -198,12 +200,15 @@ public class SearchContext implements Releasable {
this.highlight = highlight; this.highlight = highlight;
} }
public ScriptFieldsContext scriptFields() { public boolean hasScriptFields() {
return this.scriptFields; return scriptFields != null;
} }
public void scriptFields(ScriptFieldsContext scriptFields) { public ScriptFieldsContext scriptFields() {
this.scriptFields = scriptFields; if (scriptFields == null) {
scriptFields = new ScriptFieldsContext();
}
return this.scriptFields;
} }
public ContextIndexSearcher searcher() { public ContextIndexSearcher searcher() {
@ -322,13 +327,19 @@ public class SearchContext implements Releasable {
return this; return this;
} }
public boolean hasFieldNames() {
return fieldNames != null;
}
public List<String> fieldNames() { public List<String> fieldNames() {
if (fieldNames == null) {
fieldNames = Lists.newArrayList();
}
return fieldNames; return fieldNames;
} }
public SearchContext fieldNames(List<String> fieldNames) { public void emptyFieldNames() {
this.fieldNames = fieldNames; this.fieldNames = ImmutableList.of();
return this;
} }
public boolean explain() { public boolean explain() {

View File

@ -126,13 +126,18 @@ public class ScriptFieldSearchTests extends AbstractNodesTests {
SearchResponse response = client.prepareSearch() SearchResponse response = client.prepareSearch()
.setQuery(matchAllQuery()) .setQuery(matchAllQuery())
.addField("_source.obj1") // we also automatically detect _source in fields
.addScriptField("s_obj1", "_source.obj1") .addScriptField("s_obj1", "_source.obj1")
.addScriptField("s_obj1_test", "_source.obj1.test") .addScriptField("s_obj1_test", "_source.obj1.test")
.addScriptField("s_obj2", "_source.obj2") .addScriptField("s_obj2", "_source.obj2")
.addScriptField("s_obj2_arr2", "_source.obj2.arr2") .addScriptField("s_obj2_arr2", "_source.obj2.arr2")
.execute().actionGet(); .execute().actionGet();
Map<String, Object> sObj1 = (Map<String, Object>) response.hits().getAt(0).field("s_obj1").value(); Map<String, Object> sObj1 = (Map<String, Object>) response.hits().getAt(0).field("_source.obj1").value();
assertThat(sObj1.get("test").toString(), equalTo("something"));
assertThat(response.hits().getAt(0).field("s_obj1_test").value().toString(), equalTo("something"));
sObj1 = (Map<String, Object>) response.hits().getAt(0).field("s_obj1").value();
assertThat(sObj1.get("test").toString(), equalTo("something")); assertThat(sObj1.get("test").toString(), equalTo("something"));
assertThat(response.hits().getAt(0).field("s_obj1_test").value().toString(), equalTo("something")); assertThat(response.hits().getAt(0).field("s_obj1_test").value().toString(), equalTo("something"));