Skip to content

Commit 03fdc6a

Browse files
committed
Query DSL: Terms filter to allow for terms lookup from another document
closes #2674
1 parent 6978aa2 commit 03fdc6a

File tree

9 files changed

+554
-4
lines changed

9 files changed

+554
-4
lines changed

src/main/java/org/elasticsearch/action/admin/indices/cache/clear/TransportClearIndicesCacheAction.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.elasticsearch.common.settings.Settings;
3535
import org.elasticsearch.index.service.IndexService;
3636
import org.elasticsearch.indices.IndicesService;
37+
import org.elasticsearch.indices.cache.filter.terms.IndicesTermsFilterCache;
3738
import org.elasticsearch.threadpool.ThreadPool;
3839
import org.elasticsearch.transport.TransportService;
3940

@@ -48,12 +49,14 @@
4849
public class TransportClearIndicesCacheAction extends TransportBroadcastOperationAction<ClearIndicesCacheRequest, ClearIndicesCacheResponse, ShardClearIndicesCacheRequest, ShardClearIndicesCacheResponse> {
4950

5051
private final IndicesService indicesService;
52+
private final IndicesTermsFilterCache termsFilterCache;
5153

5254
@Inject
5355
public TransportClearIndicesCacheAction(Settings settings, ThreadPool threadPool, ClusterService clusterService,
54-
TransportService transportService, IndicesService indicesService) {
56+
TransportService transportService, IndicesService indicesService, IndicesTermsFilterCache termsFilterCache) {
5557
super(settings, threadPool, clusterService, transportService);
5658
this.indicesService = indicesService;
59+
this.termsFilterCache = termsFilterCache;
5760
}
5861

5962
@Override
@@ -123,10 +126,12 @@ protected ShardClearIndicesCacheResponse shardOperation(ShardClearIndicesCacheRe
123126
if (request.isFilterCache()) {
124127
clearedAtLeastOne = true;
125128
service.cache().filter().clear("api");
129+
termsFilterCache.clear("api");
126130
}
127131
if (request.getFilterKeys() != null && request.getFilterKeys().length > 0) {
128132
clearedAtLeastOne = true;
129133
service.cache().filter().clear("api", request.getFilterKeys());
134+
termsFilterCache.clear("api", request.getFilterKeys());
130135
}
131136
if (request.isFieldDataCache()) {
132137
clearedAtLeastOne = true;
@@ -150,9 +155,10 @@ protected ShardClearIndicesCacheResponse shardOperation(ShardClearIndicesCacheRe
150155
}
151156
} else {
152157
service.cache().clear("api");
158+
termsFilterCache.clear("api");
153159
}
154160
}
155-
service.cache().invalidateCache();
161+
service.cache().invalidateStatsCache();
156162
}
157163
return new ShardClearIndicesCacheResponse(request.getIndex(), request.getShardId());
158164
}

src/main/java/org/elasticsearch/index/cache/IndexCache.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void setClusterService(@Nullable ClusterService clusterService) {
7575
}
7676
}
7777

78-
public synchronized void invalidateCache() {
78+
public synchronized void invalidateStatsCache() {
7979
FilterCache.EntriesStats filterEntriesStats = filterCache.entriesStats();
8080
latestCacheStats = new CacheStats(filterCache.evictions(), filterEntriesStats.sizeInBytes, filterEntriesStats.count, idCache.sizeInBytes());
8181
latestCacheStatsTimestamp = System.currentTimeMillis();

src/main/java/org/elasticsearch/index/query/FilterBuilders.java

+8
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,14 @@ public static TermsFilterBuilder termsFilter(String name, Iterable values) {
195195
return new TermsFilterBuilder(name, values);
196196
}
197197

198+
/**
199+
* A terms lookup filter for the provided field name. A lookup terms filter can
200+
* extract the terms to filter by from another doc in an index.
201+
*/
202+
public static TermsLookupFilterBuilder termsLookupFilter(String name) {
203+
return new TermsLookupFilterBuilder(name);
204+
}
205+
198206
/**
199207
* A filer for a field based on several terms matching on any of them.
200208
*

src/main/java/org/elasticsearch/index/query/TermsFilterParser.java

+58-1
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@
2727
import org.apache.lucene.util.BytesRef;
2828
import org.elasticsearch.common.inject.Inject;
2929
import org.elasticsearch.common.lucene.BytesRefs;
30-
import org.elasticsearch.common.lucene.search.*;
30+
import org.elasticsearch.common.lucene.search.AndFilter;
31+
import org.elasticsearch.common.lucene.search.OrFilter;
32+
import org.elasticsearch.common.lucene.search.TermFilter;
33+
import org.elasticsearch.common.lucene.search.XBooleanFilter;
3134
import org.elasticsearch.common.xcontent.XContentParser;
3235
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
3336
import org.elasticsearch.index.mapper.FieldMapper;
3437
import org.elasticsearch.index.mapper.MapperService;
38+
import org.elasticsearch.indices.cache.filter.terms.IndicesTermsFilterCache;
39+
import org.elasticsearch.indices.cache.filter.terms.TermsLookup;
3540

3641
import java.io.IOException;
3742
import java.util.List;
@@ -45,6 +50,8 @@ public class TermsFilterParser implements FilterParser {
4550

4651
public static final String NAME = "terms";
4752

53+
private IndicesTermsFilterCache termsFilterCache;
54+
4855
@Inject
4956
public TermsFilterParser() {
5057
}
@@ -54,6 +61,11 @@ public String[] names() {
5461
return new String[]{NAME, "in"};
5562
}
5663

64+
@Inject(optional = true)
65+
public void setIndicesTermsFilterCache(IndicesTermsFilterCache termsFilterCache) {
66+
this.termsFilterCache = termsFilterCache;
67+
}
68+
5769
@Override
5870
public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
5971
XContentParser parser = parseContext.parser();
@@ -62,6 +74,12 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
6274
Boolean cache = null;
6375
String filterName = null;
6476
String currentFieldName = null;
77+
78+
String lookupIndex = parseContext.index().name();
79+
String lookupType = null;
80+
String lookupId = null;
81+
String lookupPath = null;
82+
6583
CacheKeyFilter.Key cacheKey = null;
6684
XContentParser.Token token;
6785
String execution = "plain";
@@ -80,6 +98,34 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
8098
}
8199
terms.add(value);
82100
}
101+
} else if (token == XContentParser.Token.START_OBJECT) {
102+
fieldName = currentFieldName;
103+
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
104+
if (token == XContentParser.Token.FIELD_NAME) {
105+
currentFieldName = parser.currentName();
106+
} else if (token.isValue()) {
107+
if ("index".equals(currentFieldName)) {
108+
lookupIndex = parser.text();
109+
} else if ("type".equals(currentFieldName)) {
110+
lookupType = parser.text();
111+
} else if ("id".equals(currentFieldName)) {
112+
lookupId = parser.text();
113+
} else if ("path".equals(currentFieldName)) {
114+
lookupPath = parser.text();
115+
} else {
116+
throw new QueryParsingException(parseContext.index(), "[terms] filter does not support [" + currentFieldName + "] within lookup element");
117+
}
118+
}
119+
}
120+
if (lookupType == null) {
121+
throw new QueryParsingException(parseContext.index(), "[terms] filter lookup element requires specifying the type");
122+
}
123+
if (lookupId == null) {
124+
throw new QueryParsingException(parseContext.index(), "[terms] filter lookup element requires specifying the id");
125+
}
126+
if (lookupPath == null) {
127+
throw new QueryParsingException(parseContext.index(), "[terms] filter lookup element requires specifying the path");
128+
}
83129
} else if (token.isValue()) {
84130
if ("execution".equals(currentFieldName)) {
85131
execution = parser.text();
@@ -113,6 +159,17 @@ public Filter parse(QueryParseContext parseContext) throws IOException, QueryPar
113159
}
114160
}
115161

162+
if (lookupId != null) {
163+
// external lookup, use it
164+
TermsLookup termsLookup = new TermsLookup(fieldMapper, lookupIndex, lookupType, lookupId, lookupPath);
165+
if (cacheKey == null) {
166+
cacheKey = new CacheKeyFilter.Key(termsLookup.toString());
167+
}
168+
Filter filter = termsFilterCache.lookupTermsFilter(cacheKey, termsLookup);
169+
filter = parseContext.cacheFilter(filter, null); // cacheKey is passed as null, so we don't double cache the key
170+
return filter;
171+
}
172+
116173
try {
117174
Filter filter;
118175
if ("plain".equals(execution)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed to ElasticSearch and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. ElasticSearch licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.index.query;
21+
22+
import org.elasticsearch.common.xcontent.XContentBuilder;
23+
24+
import java.io.IOException;
25+
26+
/**
27+
* A filer for a field based on several terms matching on any of them.
28+
*/
29+
public class TermsLookupFilterBuilder extends BaseFilterBuilder {
30+
31+
private final String name;
32+
private String lookupIndex;
33+
private String lookupType;
34+
private String lookupId;
35+
private String lookupPath;
36+
37+
private String cacheKey;
38+
private String filterName;
39+
40+
public TermsLookupFilterBuilder(String name) {
41+
this.name = name;
42+
}
43+
44+
/**
45+
* Sets the filter name for the filter that can be used when searching for matched_filters per hit.
46+
*/
47+
public TermsLookupFilterBuilder filterName(String filterName) {
48+
this.filterName = filterName;
49+
return this;
50+
}
51+
52+
/**
53+
* Sets the index name to lookup the terms from.
54+
*/
55+
public TermsLookupFilterBuilder lookupIndex(String lookupIndex) {
56+
this.lookupIndex = lookupIndex;
57+
return this;
58+
}
59+
60+
/**
61+
* Sets the index type to lookup the terms from.
62+
*/
63+
public TermsLookupFilterBuilder lookupType(String lookupType) {
64+
this.lookupType = lookupType;
65+
return this;
66+
}
67+
68+
/**
69+
* Sets the doc id to lookup the terms from.
70+
*/
71+
public TermsLookupFilterBuilder lookupId(String lookupId) {
72+
this.lookupId = lookupId;
73+
return this;
74+
}
75+
76+
/**
77+
* Sets the path within the document to lookup the terms from.
78+
*/
79+
public TermsLookupFilterBuilder lookupPath(String lookupPath) {
80+
this.lookupPath = lookupPath;
81+
return this;
82+
}
83+
84+
public TermsLookupFilterBuilder cacheKey(String cacheKey) {
85+
this.cacheKey = cacheKey;
86+
return this;
87+
}
88+
89+
@Override
90+
public void doXContent(XContentBuilder builder, Params params) throws IOException {
91+
builder.startObject(TermsFilterParser.NAME);
92+
93+
builder.startObject(name);
94+
if (lookupIndex != null) {
95+
builder.field("index", lookupIndex);
96+
}
97+
builder.field("type", lookupType);
98+
builder.field("id", lookupId);
99+
builder.field("path", lookupPath);
100+
builder.endObject();
101+
102+
if (filterName != null) {
103+
builder.field("_name", filterName);
104+
}
105+
if (cacheKey != null) {
106+
builder.field("_cache_key", cacheKey);
107+
}
108+
109+
builder.endObject();
110+
}
111+
}

src/main/java/org/elasticsearch/indices/IndicesModule.java

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.common.settings.Settings;
2727
import org.elasticsearch.indices.analysis.IndicesAnalysisModule;
2828
import org.elasticsearch.indices.cache.filter.IndicesFilterCache;
29+
import org.elasticsearch.indices.cache.filter.terms.IndicesTermsFilterCache;
2930
import org.elasticsearch.indices.cluster.IndicesClusterStateService;
3031
import org.elasticsearch.indices.memory.IndexingMemoryController;
3132
import org.elasticsearch.indices.query.IndicesQueriesModule;
@@ -68,6 +69,7 @@ protected void configure() {
6869
bind(IndicesClusterStateService.class).asEagerSingleton();
6970
bind(IndexingMemoryController.class).asEagerSingleton();
7071
bind(IndicesFilterCache.class).asEagerSingleton();
72+
bind(IndicesTermsFilterCache.class).asEagerSingleton();
7173
bind(TransportNodesListShardStoreMetaData.class).asEagerSingleton();
7274
bind(IndicesTTLService.class).asEagerSingleton();
7375
bind(IndicesWarmer.class).to(InternalIndicesWarmer.class).asEagerSingleton();

0 commit comments

Comments
 (0)