diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java index 58b91fadc8a..9c18eefca96 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentFieldMappers.java @@ -111,6 +111,17 @@ public class DocumentFieldMappers implements Iterable { return fullNameFieldMappers.get(fullName); } + /** + * Tries to find first based on {@link #fullName(String)}, then by {@link #indexName(String)}. + */ + public FieldMappers smartName(String name) { + FieldMappers fieldMappers = fullName(name); + if (fieldMappers != null) { + return fieldMappers; + } + return indexName(name); + } + /** * A smart analyzer used for indexing that takes into account specific analyzers configured * per {@link FieldMapper}. diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 18385634556..e972c05e10d 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -20,11 +20,12 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.document.Fieldable; import org.elasticsearch.util.Nullable; import org.elasticsearch.util.concurrent.ThreadSafe; /** - * @author kimchy (Shay Banon) + * @author kimchy (shay.banon) */ @ThreadSafe public interface DocumentMapper { @@ -73,12 +74,20 @@ public interface DocumentMapper { /** * Parses the source into a parsed document. - *

+ * *

Validates that the source has the provided id and type. Note, most times * we will already have the id and the type even though they exist in the source as well. */ ParsedDocument parse(@Nullable String type, @Nullable String id, byte[] source) throws MapperParsingException; + /** + * Parses the source into a parsed document. + * + *

Validates that the source has the provided id and type. Note, most times + * we will already have the id and the type even though they exist in the source as well. + */ + ParsedDocument parse(@Nullable String type, @Nullable String id, byte[] source, @Nullable ParseListener listener) throws MapperParsingException; + /** * Parses the source into the parsed document. */ @@ -120,4 +129,25 @@ public interface DocumentMapper { return this; } } + + /** + * A listener to be called during the parse process. + */ + public static interface ParseListener { + + public static final ParseListener EMPTY = new ParseListenerAdapter(); + + /** + * Called before a field is added to the document. Return true to include + * it in the document. + */ + boolean beforeFieldAdded(FieldMapper fieldMapper, Fieldable fieldable, ParseContext parseContent); + } + + public static class ParseListenerAdapter implements ParseListener { + + @Override public boolean beforeFieldAdded(FieldMapper fieldMapper, Fieldable fieldable, Object parseContext) { + return true; + } + } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/FieldMapperListener.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/FieldMapperListener.java index 4b11926f282..134d40d528e 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/FieldMapperListener.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/FieldMapperListener.java @@ -20,7 +20,7 @@ package org.elasticsearch.index.mapper; /** - * @author kimchy (Shay Banon) + * @author kimchy (shay.banon) */ public interface FieldMapperListener { diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java index e3db1a768ce..e792fcd2812 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -249,11 +249,7 @@ public class MapperService extends AbstractIndexComponent implements Iterable implements FieldMapper, JsonMapper { field.setOmitNorms(omitNorms); field.setOmitTermFreqAndPositions(omitTermFreqAndPositions); field.setBoost(boost); - jsonContext.doc().add(field); + if (jsonContext.listener().beforeFieldAdded(this, field, jsonContext)) { + jsonContext.doc().add(field); + } } protected abstract Field parseCreateField(JsonParseContext jsonContext) throws IOException; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonParseContext.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonParseContext.java index 00876ab4311..bcf8fa9f720 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonParseContext.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonParseContext.java @@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper.json; import org.apache.lucene.document.Document; import org.codehaus.jackson.JsonParser; +import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.util.concurrent.NotThreadSafe; /** @@ -43,6 +44,8 @@ public class JsonParseContext { private String id; + private DocumentMapper.ParseListener listener; + private String uid; private StringBuilder stringBuilder = new StringBuilder(); @@ -56,7 +59,7 @@ public class JsonParseContext { this.path = path; } - public void reset(JsonParser jsonParser, Document document, String type, byte[] source) { + public void reset(JsonParser jsonParser, Document document, String type, byte[] source, DocumentMapper.ParseListener listener) { this.jsonParser = jsonParser; this.document = document; this.type = type; @@ -64,6 +67,7 @@ public class JsonParseContext { this.path.reset(); this.parsedIdState = ParsedIdState.NO; this.mappersAdded = false; + this.listener = listener; } public boolean mappersAdded() { @@ -90,6 +94,10 @@ public class JsonParseContext { return this.jsonParser; } + public DocumentMapper.ParseListener listener() { + return this.listener; + } + public Document doc() { return this.document; } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/BoolJsonQueryParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/BoolJsonQueryParser.java index b2a4bf8a441..f28794a67d8 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/BoolJsonQueryParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/BoolJsonQueryParser.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.util.List; import static com.google.common.collect.Lists.*; +import static org.elasticsearch.index.query.support.QueryParsers.*; /** * @author kimchy (Shay Banon) @@ -110,6 +111,6 @@ public class BoolJsonQueryParser extends AbstractIndexComponent implements JsonQ if (minimumNumberShouldMatch != -1) { query.setMinimumNumberShouldMatch(minimumNumberShouldMatch); } - return query; + return fixNegativeQueryIfNeeded(query); } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryBuilders.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryBuilders.java index 720dc3e61e3..05b05274d5e 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryBuilders.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/JsonQueryBuilders.java @@ -116,11 +116,11 @@ public abstract class JsonQueryBuilders { return new ConstantScoreQueryJsonQueryBuilder(filterBuilder); } - public static MoreLikeThisJsonQueryBuilder moreLikeThis(String... fields) { + public static MoreLikeThisJsonQueryBuilder moreLikeThisQuery(String... fields) { return new MoreLikeThisJsonQueryBuilder(fields); } - public static MoreLikeThisFieldJsonQueryBuilder moreLikeThisField(String name) { + public static MoreLikeThisFieldJsonQueryBuilder moreLikeThisFieldQuery(String name) { return new MoreLikeThisFieldJsonQueryBuilder(name); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/QueryStringJsonQueryParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/QueryStringJsonQueryParser.java index 9ecb56a6094..e92059422a2 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/QueryStringJsonQueryParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/json/QueryStringJsonQueryParser.java @@ -37,6 +37,8 @@ import org.elasticsearch.util.settings.Settings; import java.io.IOException; +import static org.elasticsearch.index.query.support.QueryParsers.*; + /** * @author kimchy (Shay Banon) */ @@ -154,7 +156,7 @@ public class QueryStringJsonQueryParser extends AbstractIndexComponent implement try { Query query = queryParser.parse(queryString); query.setBoost(boost); - return query; + return fixNegativeQueryIfNeeded(query); } catch (ParseException e) { throw new QueryParsingException(index, "Failed to parse query [" + queryString + "]", e); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/support/QueryParsers.java b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/support/QueryParsers.java index 3b1e9707379..8f3d77f0348 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/support/QueryParsers.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/index/query/support/QueryParsers.java @@ -26,6 +26,8 @@ import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.util.Nullable; import org.elasticsearch.util.lucene.search.TermFilter; +import java.util.List; + /** * @author kimchy (Shay Banon) */ @@ -35,6 +37,29 @@ public final class QueryParsers { } + public static boolean isNegativeQuery(Query q) { + if (!(q instanceof BooleanQuery)) { + return false; + } + List clauses = ((BooleanQuery) q).clauses(); + if (clauses.isEmpty()) { + return false; + } + for (BooleanClause clause : clauses) { + if (!clause.isProhibited()) return false; + } + return true; + } + + public static Query fixNegativeQueryIfNeeded(Query q) { + if (isNegativeQuery(q)) { + BooleanQuery newBq = (BooleanQuery) q.clone(); + newBq.add(new MatchAllDocsQuery(), BooleanClause.Occur.MUST); + return newBq; + } + return q; + } + public static Query wrapSmartNameQuery(Query query, @Nullable MapperService.SmartNameFieldMappers smartFieldMappers, @Nullable FilterCache filterCache) { if (smartFieldMappers == null) { diff --git a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java index 584ea2099ae..3bdf72e6e7a 100644 --- a/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java +++ b/modules/elasticsearch/src/test/java/org/elasticsearch/index/query/json/SimpleJsonIndexQueryParserTests.java @@ -603,7 +603,7 @@ public class SimpleJsonIndexQueryParserTests { @Test public void testMoreLikeThisBuilder() throws Exception { IndexQueryParser queryParser = newQueryParser(); - Query parsedQuery = queryParser.parse(moreLikeThis("name.first", "name.last").likeText("something").minTermFrequency(1).maxQueryTerms(12)); + Query parsedQuery = queryParser.parse(moreLikeThisQuery("name.first", "name.last").likeText("something").minTermFrequency(1).maxQueryTerms(12)); assertThat(parsedQuery, instanceOf(MoreLikeThisQuery.class)); MoreLikeThisQuery mltQuery = (MoreLikeThisQuery) parsedQuery; assertThat(mltQuery.getMoreLikeFields()[0], equalTo("name.first")); @@ -627,7 +627,7 @@ public class SimpleJsonIndexQueryParserTests { @Test public void testMoreLikeThisFieldBuilder() throws Exception { IndexQueryParser queryParser = newQueryParser(); - Query parsedQuery = queryParser.parse(moreLikeThisField("name.first").likeText("something").minTermFrequency(1).maxQueryTerms(12)); + Query parsedQuery = queryParser.parse(moreLikeThisFieldQuery("name.first").likeText("something").minTermFrequency(1).maxQueryTerms(12)); assertThat(parsedQuery, instanceOf(MoreLikeThisQuery.class)); MoreLikeThisQuery mltQuery = (MoreLikeThisQuery) parsedQuery; assertThat(mltQuery.getMoreLikeFields()[0], equalTo("name.first"));