Skip to content

Commit bd6b89f

Browse files
committed
Query DSL: Allow to control if filters should be cached, closes #181.
1 parent 1882460 commit bd6b89f

35 files changed

+423
-86
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
* Licensed to Elastic Search 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. Elastic Search 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.apache.lucene.search;
21+
22+
import org.apache.lucene.index.IndexReader;
23+
import org.apache.lucene.index.Term;
24+
25+
import java.io.IOException;
26+
import java.util.Set;
27+
28+
/**
29+
* @author kimchy (shay.banon)
30+
*/
31+
// LUCENE MONITOR: Against ConstantScoreQuery, basically added logic in the doc iterator to take deletions into account
32+
// So it can basically be cached safely even with a reader that changes deletions but remain with teh same cache key
33+
// See more: https://issues.apache.org/jira/browse/LUCENE-2468
34+
public class DeletionAwareConstantScoreQuery extends Query {
35+
protected final Filter filter;
36+
protected final boolean deletionAware;
37+
38+
public DeletionAwareConstantScoreQuery(Filter filter) {
39+
this(filter, false);
40+
}
41+
42+
public DeletionAwareConstantScoreQuery(Filter filter, boolean deletionAware) {
43+
this.filter = filter;
44+
this.deletionAware = deletionAware;
45+
}
46+
47+
/**
48+
* Returns the encapsulated filter
49+
*/
50+
public Filter getFilter() {
51+
return filter;
52+
}
53+
54+
@Override
55+
public Query rewrite(IndexReader reader) throws IOException {
56+
return this;
57+
}
58+
59+
@Override
60+
public void extractTerms(Set<Term> terms) {
61+
// OK to not add any terms when used for MultiSearcher,
62+
// but may not be OK for highlighting
63+
}
64+
65+
protected class ConstantWeight extends Weight {
66+
private Similarity similarity;
67+
private float queryNorm;
68+
private float queryWeight;
69+
70+
public ConstantWeight(Searcher searcher) {
71+
this.similarity = getSimilarity(searcher);
72+
}
73+
74+
@Override
75+
public Query getQuery() {
76+
return DeletionAwareConstantScoreQuery.this;
77+
}
78+
79+
@Override
80+
public float getValue() {
81+
return queryWeight;
82+
}
83+
84+
@Override
85+
public float sumOfSquaredWeights() throws IOException {
86+
queryWeight = getBoost();
87+
return queryWeight * queryWeight;
88+
}
89+
90+
@Override
91+
public void normalize(float norm) {
92+
this.queryNorm = norm;
93+
queryWeight *= this.queryNorm;
94+
}
95+
96+
@Override
97+
public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder, boolean topScorer) throws IOException {
98+
return new ConstantScorer(similarity, reader, this);
99+
}
100+
101+
@Override
102+
public Explanation explain(IndexReader reader, int doc) throws IOException {
103+
104+
ConstantScorer cs = new ConstantScorer(similarity, reader, this);
105+
boolean exists = cs.docIdSetIterator.advance(doc) == doc;
106+
107+
ComplexExplanation result = new ComplexExplanation();
108+
109+
if (exists) {
110+
result.setDescription("ConstantScoreQuery(" + filter
111+
+ "), product of:");
112+
result.setValue(queryWeight);
113+
result.setMatch(Boolean.TRUE);
114+
result.addDetail(new Explanation(getBoost(), "boost"));
115+
result.addDetail(new Explanation(queryNorm, "queryNorm"));
116+
} else {
117+
result.setDescription("ConstantScoreQuery(" + filter
118+
+ ") doesn't match id " + doc);
119+
result.setValue(0);
120+
result.setMatch(Boolean.FALSE);
121+
}
122+
return result;
123+
}
124+
}
125+
126+
protected class ConstantScorer extends Scorer {
127+
final IndexReader reader;
128+
final DocIdSetIterator docIdSetIterator;
129+
final float theScore;
130+
131+
public ConstantScorer(Similarity similarity, IndexReader reader, Weight w) throws IOException {
132+
super(similarity);
133+
this.reader = reader;
134+
theScore = w.getValue();
135+
DocIdSet docIdSet = filter.getDocIdSet(reader);
136+
if (docIdSet == null) {
137+
docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
138+
} else {
139+
DocIdSetIterator iter = docIdSet.iterator();
140+
if (iter == null) {
141+
docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
142+
} else {
143+
docIdSetIterator = iter;
144+
}
145+
}
146+
}
147+
148+
@Override
149+
public int nextDoc() throws IOException {
150+
if (deletionAware) {
151+
int nextDoc;
152+
while ((nextDoc = docIdSetIterator.nextDoc()) != NO_MORE_DOCS) {
153+
if (!reader.isDeleted(nextDoc)) {
154+
return nextDoc;
155+
}
156+
}
157+
return nextDoc;
158+
} else {
159+
return docIdSetIterator.nextDoc();
160+
}
161+
}
162+
163+
@Override
164+
public int docID() {
165+
return docIdSetIterator.docID();
166+
}
167+
168+
@Override
169+
public float score() throws IOException {
170+
return theScore;
171+
}
172+
173+
@Override
174+
public int advance(int target) throws IOException {
175+
if (deletionAware) {
176+
int doc = docIdSetIterator.advance(target);
177+
if (doc == NO_MORE_DOCS) {
178+
return doc;
179+
}
180+
if (!reader.isDeleted(doc)) {
181+
return doc;
182+
}
183+
while ((doc = nextDoc()) < target) {
184+
if (!reader.isDeleted(doc)) {
185+
return doc;
186+
}
187+
}
188+
return doc;
189+
} else {
190+
return docIdSetIterator.advance(target);
191+
}
192+
}
193+
}
194+
195+
@Override
196+
public Weight createWeight(Searcher searcher) {
197+
return new DeletionAwareConstantScoreQuery.ConstantWeight(searcher);
198+
}
199+
200+
/**
201+
* Prints a user-readable version of this query.
202+
*/
203+
@Override
204+
public String toString(String field) {
205+
return "ConstantScore(" + filter.toString()
206+
+ (getBoost() == 1.0 ? ")" : "^" + getBoost());
207+
}
208+
209+
/**
210+
* Returns true if <code>o</code> is equal to this.
211+
*/
212+
@Override
213+
public boolean equals(Object o) {
214+
if (this == o) return true;
215+
if (!(o instanceof ConstantScoreQuery)) return false;
216+
ConstantScoreQuery other = (ConstantScoreQuery) o;
217+
return this.getBoost() == other.getBoost() && filter.equals(other.filter);
218+
}
219+
220+
/**
221+
* Returns a hash code value for this object.
222+
*/
223+
@Override
224+
public int hashCode() {
225+
// Simple add is OK since no existing filter hashcode has a float component.
226+
return filter.hashCode() + Float.floatToIntBits(getBoost());
227+
}
228+
229+
}
230+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Licensed to Elastic Search 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. Elastic Search 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.apache.lucene.search;
21+
22+
/**
23+
* @author kimchy (shay.banon)
24+
*/
25+
public class OpenFilterClause extends FilterClause {
26+
27+
public OpenFilterClause(Filter filter, BooleanClause.Occur occur) {
28+
super(filter, occur);
29+
}
30+
31+
public void setFilter(Filter filter) {
32+
this.filter = filter;
33+
}
34+
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/FilterCache.java

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public interface FilterCache extends IndexComponent, CloseableComponent {
3232

3333
Filter cache(Filter filterToCache);
3434

35+
boolean isCached(Filter filter);
36+
3537
void clear();
3638

3739
/**

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/none/NoneFilterCache.java

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public class NoneFilterCache extends AbstractIndexComponent implements FilterCac
4949
return filterToCache;
5050
}
5151

52+
@Override public boolean isCached(Filter filter) {
53+
return false;
54+
}
55+
5256
@Override public void clear() {
5357
// nothing to do here
5458
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/cache/filter/support/AbstractConcurrentMapFilterCache.java

+4
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ protected AbstractConcurrentMapFilterCache(Index index, @IndexSettings Settings
8181
return new FilterCacheFilterWrapper(filterToCache);
8282
}
8383

84+
@Override public boolean isCached(Filter filter) {
85+
return filter instanceof FilterCacheFilterWrapper;
86+
}
87+
8488
protected ConcurrentMap<Filter, DocIdSet> buildFilterMap() {
8589
return newConcurrentMap();
8690
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/engine/IndexEngine.java

+6
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,11 @@
2929
*/
3030
public interface IndexEngine extends IndexComponent {
3131

32+
/**
33+
* Are readers cloned on deletion? If this is the case, then some specific
34+
* needs to be taken.
35+
*/
36+
boolean readerClonedOnDeletion();
37+
3238
void close();
3339
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/engine/robin/RobinIndexEngine.java

+13
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,28 @@
2626
import org.elasticsearch.util.inject.Inject;
2727
import org.elasticsearch.util.settings.Settings;
2828

29+
import static org.elasticsearch.util.settings.ImmutableSettings.Builder.*;
30+
2931
/**
3032
* @author kimchy (shay.banon)
3133
*/
3234
public class RobinIndexEngine extends AbstractIndexComponent implements IndexEngine {
3335

36+
public RobinIndexEngine(Index index) {
37+
this(index, EMPTY_SETTINGS);
38+
}
39+
3440
@Inject public RobinIndexEngine(Index index, @IndexSettings Settings indexSettings) {
3541
super(index, indexSettings);
3642
}
3743

44+
/**
45+
* With NRT, readers are cloned on deletions... .
46+
*/
47+
@Override public boolean readerClonedOnDeletion() {
48+
return true;
49+
}
50+
3851
@Override public void close() {
3952
}
4053
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/query/IndexQueryParserService.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.elasticsearch.index.Index;
2424
import org.elasticsearch.index.analysis.AnalysisService;
2525
import org.elasticsearch.index.cache.IndexCache;
26+
import org.elasticsearch.index.engine.IndexEngine;
2627
import org.elasticsearch.index.mapper.MapperService;
2728
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
2829
import org.elasticsearch.index.settings.IndexSettings;
@@ -51,13 +52,13 @@ public static final class Defaults {
5152

5253
private final Map<String, IndexQueryParser> indexQueryParsers;
5354

54-
public IndexQueryParserService(Index index, MapperService mapperService, IndexCache indexCache, AnalysisService analysisService) {
55-
this(index, ImmutableSettings.Builder.EMPTY_SETTINGS, mapperService, indexCache, analysisService, null, null);
55+
public IndexQueryParserService(Index index, MapperService mapperService, IndexCache indexCache, IndexEngine indexEngine, AnalysisService analysisService) {
56+
this(index, ImmutableSettings.Builder.EMPTY_SETTINGS, mapperService, indexCache, indexEngine, analysisService, null, null);
5657
}
5758

5859
@Inject public IndexQueryParserService(Index index, @IndexSettings Settings indexSettings,
5960
MapperService mapperService, IndexCache indexCache,
60-
AnalysisService analysisService,
61+
IndexEngine indexEngine, AnalysisService analysisService,
6162
@Nullable SimilarityService similarityService,
6263
@Nullable Map<String, IndexQueryParserFactory> indexQueryParsersFactories) {
6364
super(index, indexSettings);
@@ -76,7 +77,7 @@ public IndexQueryParserService(Index index, MapperService mapperService, IndexCa
7677
}
7778
}
7879
if (!qparsers.containsKey(Defaults.DEFAULT)) {
79-
IndexQueryParser defaultQueryParser = new XContentIndexQueryParser(index, indexSettings, mapperService, indexCache, analysisService, similarityService, null, null, Defaults.DEFAULT, null);
80+
IndexQueryParser defaultQueryParser = new XContentIndexQueryParser(index, indexSettings, mapperService, indexCache, indexEngine, analysisService, similarityService, null, null, Defaults.DEFAULT, null);
8081
qparsers.put(Defaults.DEFAULT, defaultQueryParser);
8182
}
8283

0 commit comments

Comments
 (0)