diff --git a/src/main/java/org/elasticsearch/common/lucene/search/NoCacheQuery.java b/src/main/java/org/elasticsearch/common/lucene/search/NoCacheQuery.java new file mode 100644 index 00000000000..c5bec8c5d9b --- /dev/null +++ b/src/main/java/org/elasticsearch/common/lucene/search/NoCacheQuery.java @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.common.lucene.search; + +import org.apache.lucene.search.Query; + +/** + * Queries are never cached directly, but a query can be wrapped in a filter that may end being cached. + * Filters that wrap this query either directly or indirectly will never be cached. + */ +public abstract class NoCacheQuery extends Query { + + @Override + public final String toString(String s) { + return "no_cache(" + innerToString(s) + ")"; + } + + public abstract String innerToString(String s); +} diff --git a/src/main/java/org/elasticsearch/common/lucene/search/ResolvableFilter.java b/src/main/java/org/elasticsearch/common/lucene/search/ResolvableFilter.java new file mode 100644 index 00000000000..4578e6711be --- /dev/null +++ b/src/main/java/org/elasticsearch/common/lucene/search/ResolvableFilter.java @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.common.lucene.search; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSet; +import org.apache.lucene.search.Filter; +import org.apache.lucene.util.Bits; + +import java.io.IOException; + +/** + * A filter implementation that resolves details at the last possible moment between filter parsing and execution. + * For example a date filter based on 'now'. + */ +public abstract class ResolvableFilter extends Filter { + + /** + * @return The actual filter instance to be executed containing the latest details. + */ + public abstract Filter resolve(); + + + + @Override + public DocIdSet getDocIdSet(LeafReaderContext context, Bits acceptDocs) throws IOException { + Filter resolvedFilter = resolve(); + if (resolvedFilter != null) { + return resolvedFilter.getDocIdSet(context, acceptDocs); + } else { + return null; + } + } +} diff --git a/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java b/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java index 68f5e15aaef..fbedc85ab4c 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/DateFieldMapper.java @@ -22,6 +22,7 @@ package org.elasticsearch.index.mapper.core; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Filter; import org.apache.lucene.search.NumericRangeFilter; import org.apache.lucene.search.NumericRangeQuery; @@ -29,6 +30,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.NumericUtils; +import org.apache.lucene.util.ToStringUtils; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.common.Explicit; import org.elasticsearch.common.Nullable; @@ -38,6 +40,8 @@ import org.elasticsearch.common.joda.DateMathParser; import org.elasticsearch.common.joda.FormatDateTimeFormatter; import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.lucene.search.NoCacheFilter; +import org.elasticsearch.common.lucene.search.NoCacheQuery; +import org.elasticsearch.common.lucene.search.ResolvableFilter; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.util.LocaleUtils; @@ -53,6 +57,7 @@ import org.elasticsearch.index.mapper.core.LongFieldMapper.CustomLongNumericFiel import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.search.NumericRangeFieldDataFilter; import org.elasticsearch.index.similarity.SimilarityProvider; +import org.elasticsearch.search.internal.SearchContext; import org.joda.time.DateTimeZone; import java.io.IOException; @@ -300,39 +305,35 @@ public class DateFieldMapper extends NumberFieldMapper { @Override public Query termQuery(Object value, @Nullable QueryParseContext context) { - long lValue = parseToMilliseconds(value, context); + long lValue = parseToMilliseconds(value); return NumericRangeQuery.newLongRange(names.indexName(), precisionStep, lValue, lValue, true, true); } - public long parseToMilliseconds(Object value, @Nullable QueryParseContext context) { - return parseToMilliseconds(value, context, false); + public long parseToMilliseconds(Object value) { + return parseToMilliseconds(value, false, null, dateMathParser); } - public long parseToMilliseconds(Object value, @Nullable QueryParseContext context, boolean includeUpper) { - return parseToMilliseconds(value, context, includeUpper, null, dateMathParser); - } - - public long parseToMilliseconds(Object value, @Nullable QueryParseContext context, boolean includeUpper, @Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser) { + public long parseToMilliseconds(Object value, boolean includeUpper, @Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser) { if (value instanceof Number) { return ((Number) value).longValue(); } - return parseToMilliseconds(convertToString(value), context, includeUpper, zone, forcedDateParser); + return parseToMilliseconds(convertToString(value), includeUpper, zone, forcedDateParser); } - public long parseToMilliseconds(String value, @Nullable QueryParseContext context, boolean includeUpper, @Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser) { - long now = context == null ? System.currentTimeMillis() : context.nowInMillis(); + public long parseToMilliseconds(String value, boolean includeUpper, @Nullable DateTimeZone zone, @Nullable DateMathParser forcedDateParser) { + SearchContext sc = SearchContext.current(); + long now = sc == null ? System.currentTimeMillis() : sc.nowInMillis(); DateMathParser dateParser = dateMathParser; if (forcedDateParser != null) { dateParser = forcedDateParser; } - long time = includeUpper && roundCeil ? dateParser.parseRoundCeil(value, now, zone) : dateParser.parse(value, now, zone); - return time; + return includeUpper && roundCeil ? dateParser.parseRoundCeil(value, now, zone) : dateParser.parse(value, now, zone); } @Override public Filter termFilter(Object value, @Nullable QueryParseContext context) { - final long lValue = parseToMilliseconds(value, context); + final long lValue = parseToMilliseconds(value); return NumericRangeFilter.newLongRange(names.indexName(), precisionStep, lValue, lValue, true, true); } @@ -343,9 +344,18 @@ public class DateFieldMapper extends NumberFieldMapper { } public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable DateMathParser forcedDateParser, @Nullable QueryParseContext context) { + // If the current search context is null we're parsing percolator query or a index alias filter. + if (SearchContext.current() == null) { + return new LateParsingQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser); + } else { + return innerRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser); + } + } + + private Query innerRangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable DateMathParser forcedDateParser) { return NumericRangeQuery.newLongRange(names.indexName(), precisionStep, - lowerTerm == null ? null : parseToMilliseconds(lowerTerm, context, false, timeZone, forcedDateParser == null ? dateMathParser : forcedDateParser), - upperTerm == null ? null : parseToMilliseconds(upperTerm, context, includeUpper, timeZone, forcedDateParser == null ? dateMathParser : forcedDateParser), + lowerTerm == null ? null : parseToMilliseconds(lowerTerm, false, timeZone, forcedDateParser == null ? dateMathParser : forcedDateParser), + upperTerm == null ? null : parseToMilliseconds(upperTerm, includeUpper, timeZone, forcedDateParser == null ? dateMathParser : forcedDateParser), includeLower, includeUpper); } @@ -370,6 +380,16 @@ public class DateFieldMapper extends NumberFieldMapper { * - the String to parse does not have already a timezone defined (ie. `2014-01-01T00:00:00+03:00`) */ public Filter rangeFilter(QueryParseContext parseContext, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable DateMathParser forcedDateParser, @Nullable QueryParseContext context, @Nullable Boolean explicitCaching) { + IndexNumericFieldData fieldData = parseContext != null ? (IndexNumericFieldData) parseContext.getForField(this) : null; + // If the current search context is null we're parsing percolator query or a index alias filter. + if (SearchContext.current() == null) { + return new LateParsingFilter(fieldData, lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser, explicitCaching); + } else { + return innerRangeFilter(fieldData, lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser, explicitCaching); + } + } + + private Filter innerRangeFilter(IndexNumericFieldData fieldData, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable DateMathParser forcedDateParser, @Nullable Boolean explicitCaching) { boolean cache; boolean cacheable = true; Long lowerVal = null; @@ -380,7 +400,7 @@ public class DateFieldMapper extends NumberFieldMapper { } else { String value = convertToString(lowerTerm); cacheable = !hasDateExpressionWithNoRounding(value); - lowerVal = parseToMilliseconds(value, context, false, timeZone, forcedDateParser); + lowerVal = parseToMilliseconds(value, false, timeZone, forcedDateParser); } } if (upperTerm != null) { @@ -389,7 +409,7 @@ public class DateFieldMapper extends NumberFieldMapper { } else { String value = convertToString(upperTerm); cacheable = cacheable && !hasDateExpressionWithNoRounding(value); - upperVal = parseToMilliseconds(value, context, includeUpper, timeZone, forcedDateParser); + upperVal = parseToMilliseconds(value, includeUpper, timeZone, forcedDateParser); } } @@ -404,12 +424,10 @@ public class DateFieldMapper extends NumberFieldMapper { } Filter filter; - if (parseContext != null) { - filter = NumericRangeFieldDataFilter.newLongRange( - (IndexNumericFieldData) parseContext.getForField(this), lowerVal,upperVal, includeLower, includeUpper - ); + if (fieldData != null) { + filter = NumericRangeFieldDataFilter.newLongRange(fieldData, lowerVal,upperVal, includeLower, includeUpper); } else { - filter = NumericRangeFilter.newLongRange( + filter = NumericRangeFilter.newLongRange( names.indexName(), precisionStep, lowerVal, upperVal, includeLower, includeUpper ); } @@ -597,4 +615,70 @@ public class DateFieldMapper extends NumberFieldMapper { } } } + + private final class LateParsingFilter extends ResolvableFilter { + + final IndexNumericFieldData fieldData; + final Object lowerTerm; + final Object upperTerm; + final boolean includeLower; + final boolean includeUpper; + final DateTimeZone timeZone; + final DateMathParser forcedDateParser; + final Boolean explicitCaching; + + public LateParsingFilter(IndexNumericFieldData fieldData, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, DateTimeZone timeZone, DateMathParser forcedDateParser, Boolean explicitCaching) { + this.fieldData = fieldData; + this.lowerTerm = lowerTerm; + this.upperTerm = upperTerm; + this.includeLower = includeLower; + this.includeUpper = includeUpper; + this.timeZone = timeZone; + this.forcedDateParser = forcedDateParser; + this.explicitCaching = explicitCaching; + } + + @Override + public Filter resolve() { + return innerRangeFilter(fieldData, lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser, explicitCaching); + } + } + + public final class LateParsingQuery extends NoCacheQuery { + + final Object lowerTerm; + final Object upperTerm; + final boolean includeLower; + final boolean includeUpper; + final DateTimeZone timeZone; + final DateMathParser forcedDateParser; + + public LateParsingQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, DateTimeZone timeZone, DateMathParser forcedDateParser) { + this.lowerTerm = lowerTerm; + this.upperTerm = upperTerm; + this.includeLower = includeLower; + this.includeUpper = includeUpper; + this.timeZone = timeZone; + this.forcedDateParser = forcedDateParser; + } + + @Override + public Query rewrite(IndexReader reader) throws IOException { + Query query = innerRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser); + return query.rewrite(reader); + } + + @Override + public String innerToString(String s) { + final StringBuilder sb = new StringBuilder(); + return sb.append(names.indexName()).append(':') + .append(includeLower ? '[' : '{') + .append((lowerTerm == null) ? "*" : lowerTerm.toString()) + .append(" TO ") + .append((upperTerm == null) ? "*" : upperTerm.toString()) + .append(includeUpper ? ']' : '}') + .append(ToStringUtils.boost(getBoost())) + .toString(); + } + } } diff --git a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java index a94b86f485e..f49f2e4e3b3 100644 --- a/src/main/java/org/elasticsearch/index/query/QueryParseContext.java +++ b/src/main/java/org/elasticsearch/index/query/QueryParseContext.java @@ -21,17 +21,22 @@ package org.elasticsearch.index.query; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.queryparser.classic.MapperQueryParser; import org.apache.lucene.queryparser.classic.QueryParserSettings; +import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.Filter; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.BitDocIdSetFilter; import org.apache.lucene.search.similarities.Similarity; +import org.apache.lucene.util.Bits; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.lucene.search.NoCacheFilter; +import org.elasticsearch.common.lucene.search.NoCacheQuery; import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.common.lucene.search.ResolvableFilter; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.Index; import org.elasticsearch.index.analysis.AnalysisService; @@ -187,17 +192,37 @@ public class QueryParseContext { return indexQueryParser.bitsetFilterCache.getBitDocIdSetFilter(filter); } - public Filter cacheFilter(Filter filter, @Nullable CacheKeyFilter.Key cacheKey) { + public Filter cacheFilter(Filter filter, @Nullable final CacheKeyFilter.Key cacheKey) { if (filter == null) { return null; } if (this.disableFilterCaching || this.propagateNoCache || filter instanceof NoCacheFilter) { return filter; } - if (cacheKey != null) { - filter = new CacheKeyFilter.Wrapper(filter, cacheKey); + if (filter instanceof ResolvableFilter) { + final ResolvableFilter resolvableFilter = (ResolvableFilter) filter; + // We need to wrap it another filter, because this method is invoked at query parse time, which + // may not be during search execution time. (for example index alias filter and percolator) + return new Filter() { + @Override + public DocIdSet getDocIdSet(LeafReaderContext atomicReaderContext, Bits bits) throws IOException { + Filter filter = resolvableFilter.resolve(); + if (filter == null) { + return null; + } + if (cacheKey != null) { + filter = new CacheKeyFilter.Wrapper(filter, cacheKey); + } + filter = indexQueryParser.indexCache.filter().cache(filter); + return filter.getDocIdSet(atomicReaderContext, bits); + } + }; + } else { + if (cacheKey != null) { + filter = new CacheKeyFilter.Wrapper(filter, cacheKey); + } + return indexQueryParser.indexCache.filter().cache(filter); } - return indexQueryParser.indexCache.filter().cache(filter); } public > IFD getForField(FieldMapper mapper) { @@ -249,6 +274,9 @@ public class QueryParseContext { // if we are at END_OBJECT, move to the next one... parser.nextToken(); } + if (result instanceof NoCacheQuery) { + propagateNoCache = true; + } if (CustomQueryWrappingFilter.shouldUseCustomQueryWrappingFilter(result)) { requireCustomQueryWrappingFilter = true; // If later on, either directly or indirectly this query gets wrapped in a query filter it must never diff --git a/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java b/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java index 199d497f6d1..01cb8637623 100644 --- a/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java +++ b/src/main/java/org/elasticsearch/index/query/functionscore/DecayFunctionParser.java @@ -265,7 +265,7 @@ public abstract class DecayFunctionParser implements ScoreFunctionParser { } long origin = SearchContext.current().nowInMillis(); if (originString != null) { - origin = dateFieldMapper.parseToMilliseconds(originString, parseContext); + origin = dateFieldMapper.parseToMilliseconds(originString); } if (scaleString == null) { diff --git a/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java b/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java index fc61a66cc2c..5bfa17c85bc 100644 --- a/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java +++ b/src/test/java/org/elasticsearch/aliases/IndexAliasesTests.java @@ -38,7 +38,6 @@ import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilders; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryParsingException; -import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.rest.action.admin.indices.alias.delete.AliasesMissingException; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; @@ -58,10 +57,12 @@ import static com.google.common.collect.Sets.newHashSet; import static org.elasticsearch.client.Requests.createIndexRequest; import static org.elasticsearch.client.Requests.indexRequest; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; -import static org.elasticsearch.index.query.FilterBuilders.termFilter; +import static org.elasticsearch.index.query.FilterBuilders.*; +import static org.elasticsearch.index.query.QueryBuilders.rangeQuery; import static org.elasticsearch.test.hamcrest.CollectionAssertions.hasKey; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.hamcrest.Matchers.*; /** @@ -964,6 +965,26 @@ public class IndexAliasesTests extends ElasticsearchIntegrationTest { .addAlias("test", "a", FilterBuilders.matchAllFilter()) // <-- no fail, b/c no field mentioned .get(); } + + @Test + public void testAliasFilterWithNowInRangeFilterAndQuery() throws Exception { + assertAcked(prepareCreate("my-index").addMapping("my-type", "_timestamp", "enabled=true")); + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter1", rangeFilter("_timestamp").cache(randomBoolean()).from("now-1d").to("now"))); + assertAcked(admin().indices().prepareAliases().addAlias("my-index", "filter2", queryFilter(rangeQuery("_timestamp").from("now-1d").to("now")))); + + final int numDocs = scaledRandomIntBetween(5, 52); + for (int i = 1; i <= numDocs; i++) { + client().prepareIndex("my-index", "my-type").setCreate(true).setSource("{}").get(); + if (i % 2 == 0) { + refresh(); + SearchResponse response = client().prepareSearch("filter1").get(); + assertHitCount(response, i); + + response = client().prepareSearch("filter2").get(); + assertHitCount(response, i); + } + } + } private void checkAliases() { GetAliasesResponse getAliasesResponse = admin().indices().prepareGetAliases("alias1").get(); diff --git a/src/test/java/org/elasticsearch/index/mapper/date/SimpleDateMappingTests.java b/src/test/java/org/elasticsearch/index/mapper/date/SimpleDateMappingTests.java index a2510a4901f..31f6e6fa277 100644 --- a/src/test/java/org/elasticsearch/index/mapper/date/SimpleDateMappingTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/date/SimpleDateMappingTests.java @@ -36,7 +36,9 @@ import org.elasticsearch.index.mapper.*; import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.index.mapper.core.LongFieldMapper; import org.elasticsearch.index.mapper.core.StringFieldMapper; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ElasticsearchSingleNodeTest; +import org.elasticsearch.test.TestSearchContext; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.Test; @@ -221,7 +223,13 @@ public class SimpleDateMappingTests extends ElasticsearchSingleNodeTest { .bytes()); assertThat(((LongFieldMapper.CustomLongNumericField) doc.rootDoc().getField("date_field")).numericAsString(), equalTo(Long.toString(new DateTime(TimeValue.timeValueHours(10).millis(), DateTimeZone.UTC).getMillis()))); - Filter filter = defaultMapper.mappers().smartNameFieldMapper("date_field").rangeFilter("10:00:00", "11:00:00", true, true, null); + Filter filter; + try { + SearchContext.setCurrent(new TestSearchContext()); + filter = defaultMapper.mappers().smartNameFieldMapper("date_field").rangeFilter("10:00:00", "11:00:00", true, true, null); + } finally { + SearchContext.removeCurrent(); + } assertThat(filter, instanceOf(NumericRangeFilter.class)); NumericRangeFilter rangeFilter = (NumericRangeFilter) filter; assertThat(rangeFilter.getMax(), equalTo(new DateTime(TimeValue.timeValueHours(11).millis() + 999).getMillis())); // +999 to include the 00-01 minute @@ -245,7 +253,13 @@ public class SimpleDateMappingTests extends ElasticsearchSingleNodeTest { .bytes()); assertThat(((LongFieldMapper.CustomLongNumericField) doc.rootDoc().getField("date_field")).numericAsString(), equalTo(Long.toString(new DateTime(TimeValue.timeValueHours(34).millis(), DateTimeZone.UTC).getMillis()))); - Filter filter = defaultMapper.mappers().smartNameFieldMapper("date_field").rangeFilter("Jan 02 10:00:00", "Jan 02 11:00:00", true, true, null); + Filter filter; + try { + SearchContext.setCurrent(new TestSearchContext()); + filter = defaultMapper.mappers().smartNameFieldMapper("date_field").rangeFilter("Jan 02 10:00:00", "Jan 02 11:00:00", true, true, null); + } finally { + SearchContext.removeCurrent(); + } assertThat(filter, instanceOf(NumericRangeFilter.class)); NumericRangeFilter rangeFilter = (NumericRangeFilter) filter; assertThat(rangeFilter.getMax(), equalTo(new DateTime(TimeValue.timeValueHours(35).millis() + 999).getMillis())); // +999 to include the 00-01 minute diff --git a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterCachingTests.java b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterCachingTests.java index 8c431e773aa..47996ef51fb 100644 --- a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterCachingTests.java +++ b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterCachingTests.java @@ -25,6 +25,7 @@ import org.apache.lucene.search.Query; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.compress.CompressedString; import org.elasticsearch.common.inject.Injector; +import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.lucene.search.AndFilter; import org.elasticsearch.common.lucene.search.CachedFilter; import org.elasticsearch.common.lucene.search.NoCacheFilter; @@ -36,6 +37,7 @@ import org.elasticsearch.index.service.IndexService; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ElasticsearchSingleNodeTest; import org.elasticsearch.test.TestSearchContext; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -70,6 +72,14 @@ public class IndexQueryParserFilterCachingTests extends ElasticsearchSingleNodeT mapperService.merge("child", new CompressedString(childMapping), true); mapperService.documentMapper("person").parse(new BytesArray(copyToBytesFromClasspath("/org/elasticsearch/index/query/data.json"))); queryParser = injector.getInstance(IndexQueryParserService.class); + SearchContext.setCurrent(new TestSearchContext()); + } + + @After + public void removeSearchContext() { + SearchContext current = SearchContext.current(); + SearchContext.removeCurrent(); + Releasables.close(current); } private IndexQueryParserService queryParser() throws IOException { diff --git a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java index c4ffbf87774..b15c63c013a 100644 --- a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java +++ b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeFormatTests.java @@ -27,7 +27,9 @@ import org.elasticsearch.common.compress.CompressedString; import org.elasticsearch.common.inject.Injector; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.service.IndexService; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ElasticsearchSingleNodeTest; +import org.elasticsearch.test.TestSearchContext; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -36,7 +38,8 @@ import java.io.IOException; import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; /** * @@ -73,10 +76,13 @@ public class IndexQueryParserFilterDateRangeFormatTests extends ElasticsearchSin // Test Invalid format query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_filter_format_invalid.json"); try { + SearchContext.setCurrent(new TestSearchContext()); queryParser.parse(query).query(); fail("A Range Filter with a specific format but with an unexpected date should raise a QueryParsingException"); } catch (QueryParsingException e) { // We expect it + } finally { + SearchContext.removeCurrent(); } } @@ -85,7 +91,13 @@ public class IndexQueryParserFilterDateRangeFormatTests extends ElasticsearchSin IndexQueryParserService queryParser = queryParser(); // We test 01/01/2012 from gte and 2030 for lt String query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_format.json"); - Query parsedQuery = queryParser.parse(query).query(); + Query parsedQuery; + try { + SearchContext.setCurrent(new TestSearchContext()); + parsedQuery = queryParser.parse(query).query(); + } finally { + SearchContext.removeCurrent();; + } assertThat(parsedQuery, instanceOf(NumericRangeQuery.class)); // Min value was 01/01/2012 (dd/MM/yyyy) @@ -99,10 +111,13 @@ public class IndexQueryParserFilterDateRangeFormatTests extends ElasticsearchSin // Test Invalid format query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_format_invalid.json"); try { + SearchContext.setCurrent(new TestSearchContext()); queryParser.parse(query).query(); fail("A Range Query with a specific format but with an unexpected date should raise a QueryParsingException"); } catch (QueryParsingException e) { // We expect it + } finally { + SearchContext.removeCurrent(); } } } diff --git a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java index dd774088e5d..95164c3c06e 100644 --- a/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java +++ b/src/test/java/org/elasticsearch/index/query/IndexQueryParserFilterDateRangeTimezoneTests.java @@ -27,7 +27,9 @@ import org.elasticsearch.common.compress.CompressedString; import org.elasticsearch.common.inject.Injector; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.service.IndexService; +import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.ElasticsearchSingleNodeTest; +import org.elasticsearch.test.TestSearchContext; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; @@ -36,9 +38,7 @@ import java.io.IOException; import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath; import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.*; /** * @@ -74,10 +74,13 @@ public class IndexQueryParserFilterDateRangeTimezoneTests extends ElasticsearchS query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_filter_timezone_numeric_field.json"); try { + SearchContext.setCurrent(new TestSearchContext()); queryParser.parse(query).query(); fail("A Range Filter on a numeric field with a TimeZone should raise a QueryParsingException"); } catch (QueryParsingException e) { // We expect it + } finally { + SearchContext.removeCurrent(); } } @@ -87,7 +90,13 @@ public class IndexQueryParserFilterDateRangeTimezoneTests extends ElasticsearchS IndexQueryParserService queryParser = queryParser(); String query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_timezone.json"); - Query parsedQuery = queryParser.parse(query).query(); + Query parsedQuery; + try { + SearchContext.setCurrent(new TestSearchContext()); + parsedQuery = queryParser.parse(query).query(); + } finally { + SearchContext.removeCurrent(); + } assertThat(parsedQuery, instanceOf(NumericRangeQuery.class)); // Min value was 2012-01-01 (UTC) so we need to remove one hour @@ -102,10 +111,13 @@ public class IndexQueryParserFilterDateRangeTimezoneTests extends ElasticsearchS query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_timezone_numeric_field.json"); try { + SearchContext.setCurrent(new TestSearchContext()); queryParser.parse(query).query(); fail("A Range Query on a numeric field with a TimeZone should raise a QueryParsingException"); } catch (QueryParsingException e) { // We expect it + } finally { + SearchContext.removeCurrent(); } } } diff --git a/src/test/java/org/elasticsearch/percolator/PercolatorTests.java b/src/test/java/org/elasticsearch/percolator/PercolatorTests.java index 30180914675..fbd669af830 100644 --- a/src/test/java/org/elasticsearch/percolator/PercolatorTests.java +++ b/src/test/java/org/elasticsearch/percolator/PercolatorTests.java @@ -71,6 +71,7 @@ import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilde import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.smileBuilder; import static org.elasticsearch.common.xcontent.XContentFactory.yamlBuilder; +import static org.elasticsearch.index.query.FilterBuilders.rangeFilter; import static org.elasticsearch.index.query.FilterBuilders.termFilter; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery; @@ -240,7 +241,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest { client().prepareIndex("test", PercolatorService.TYPE_NAME, "test3") .setSource( XContentFactory.jsonBuilder().startObject().field("query", - constantScoreQuery(FilterBuilders.rangeFilter("field1").from(1).to(5).includeLower(true).setExecution("fielddata")) + constantScoreQuery(rangeFilter("field1").from(1).to(5).includeLower(true).setExecution("fielddata")) ).endObject() ) .execute().actionGet(); @@ -263,7 +264,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest { client().prepareIndex("test", PercolatorService.TYPE_NAME, "1") .setSource( XContentFactory.jsonBuilder().startObject().field("query", - constantScoreQuery(FilterBuilders.rangeFilter("field1").from(1).to(5).setExecution("fielddata")) + constantScoreQuery(rangeFilter("field1").from(1).to(5).setExecution("fielddata")) ).endObject() ).get(); @@ -558,8 +559,7 @@ public class PercolatorTests extends ElasticsearchIntegrationTest { @Test public void percolateWithSizeField() throws Exception { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1") -.startObject("_size").field("enabled", true) - .field("store", "yes").endObject() + .startObject("_size").field("enabled", true).field("stored", "yes").endObject() .startObject("properties").startObject("field1").field("type", "string").endObject().endObject() .endObject().endObject().string(); @@ -1839,6 +1839,30 @@ public class PercolatorTests extends ElasticsearchIntegrationTest { } } + @Test + public void testPercolatorQueryWithNowRange() throws Exception { + client().admin().indices().prepareCreate("test") + .addMapping("my-type", "timestamp", "type=date") + .get(); + ensureGreen(); + + client().prepareIndex("test", PercolatorService.TYPE_NAME, "1") + .setSource(jsonBuilder().startObject().field("query", rangeQuery("timestamp").from("now-1d").to("now")).endObject()) + .get(); + client().prepareIndex("test", PercolatorService.TYPE_NAME, "2") + .setSource(jsonBuilder().startObject().field("query", constantScoreQuery(rangeFilter("timestamp").from("now-1d").to("now"))).endObject()) + .get(); + + logger.info("--> Percolate doc with field1=b"); + PercolateResponse response = client().preparePercolate() + .setIndices("test").setDocumentType("my-type") + .setPercolateDoc(docBuilder().setDoc("timestamp", System.currentTimeMillis())) + .get(); + assertMatchCount(response, 2l); + assertThat(response.getMatches(), arrayWithSize(2)); + assertThat(convertFromTextArray(response.getMatches(), "test"), arrayContainingInAnyOrder("1", "2")); + } + void initNestedIndexAndPercolation() throws IOException { XContentBuilder mapping = XContentFactory.jsonBuilder(); mapping.startObject().startObject("properties").startObject("companyname").field("type", "string").endObject()