Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse aliases at search time and never cache parsed alias filters #11930

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 0 additions & 58 deletions core/src/main/java/org/elasticsearch/index/aliases/IndexAlias.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.AbstractIndexComponent;
Expand All @@ -38,71 +39,50 @@
import org.elasticsearch.indices.InvalidAliasNameException;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

/**
*
*/
public class IndexAliasesService extends AbstractIndexComponent implements Iterable<IndexAlias> {
public class IndexAliasesService extends AbstractIndexComponent {

private final IndexQueryParserService indexQueryParser;
private final Map<String, IndexAlias> aliases = ConcurrentCollections.newConcurrentMapWithAggressiveConcurrency();
private volatile ImmutableOpenMap<String, AliasMetaData> aliases = ImmutableOpenMap.of();

@Inject
public IndexAliasesService(Index index, @IndexSettings Settings indexSettings, IndexQueryParserService indexQueryParser) {
super(index, indexSettings);
this.indexQueryParser = indexQueryParser;
}

public boolean hasAlias(String alias) {
return aliases.containsKey(alias);
}

public IndexAlias alias(String alias) {
return aliases.get(alias);
}

public IndexAlias create(String alias, @Nullable CompressedXContent filter) {
return new IndexAlias(alias, filter, parse(alias, filter));
}

public void add(String alias, @Nullable CompressedXContent filter) {
add(new IndexAlias(alias, filter, parse(alias, filter)));
}

public void addAll(Map<String, IndexAlias> aliases) {
this.aliases.putAll(aliases);
}

/**
* Returns the filter associated with listed filtering aliases.
* <p/>
* <p>The list of filtering aliases should be obtained by calling MetaData.filteringAliases.
* Returns <tt>null</tt> if no filtering is required.</p>
*/
public Query aliasFilter(String... aliases) {
if (aliases == null || aliases.length == 0) {
public Query aliasFilter(String... aliasNames) {
if (aliasNames == null || aliasNames.length == 0) {
return null;
}
if (aliases.length == 1) {
IndexAlias indexAlias = alias(aliases[0]);
if (indexAlias == null) {
if (aliasNames.length == 1) {
AliasMetaData alias = this.aliases.get(aliasNames[0]);
if (alias == null) {
// This shouldn't happen unless alias disappeared after filteringAliases was called.
throw new InvalidAliasNameException(index, aliases[0], "Unknown alias name was passed to alias Filter");
throw new InvalidAliasNameException(index, aliasNames[0], "Unknown alias name was passed to alias Filter");
}
return indexAlias.parsedFilter();
return parse(alias);
} else {
// we need to bench here a bit, to see maybe it makes sense to use OrFilter
BooleanQuery combined = new BooleanQuery();
for (String alias : aliases) {
IndexAlias indexAlias = alias(alias);
if (indexAlias == null) {
for (String aliasName : aliasNames) {
AliasMetaData alias = this.aliases.get(aliasName);
if (alias == null) {
// This shouldn't happen unless alias disappeared after filteringAliases was called.
throw new InvalidAliasNameException(index, aliases[0], "Unknown alias name was passed to alias Filter");
throw new InvalidAliasNameException(index, aliasNames[0], "Unknown alias name was passed to alias Filter");
}
if (indexAlias.parsedFilter() != null) {
combined.add(indexAlias.parsedFilter(), BooleanClause.Occur.SHOULD);
Query parsedFilter = parse(alias);
if (parsedFilter != null) {
combined.add(parsedFilter, BooleanClause.Occur.SHOULD);
} else {
// The filter might be null only if filter was removed after filteringAliases was called
return null;
Expand All @@ -112,31 +92,36 @@ public Query aliasFilter(String... aliases) {
}
}

private void add(IndexAlias indexAlias) {
aliases.put(indexAlias.alias(), indexAlias);
public void setAliases(ImmutableOpenMap<String, AliasMetaData> aliases) {
this.aliases = aliases;
}

public void remove(String alias) {
aliases.remove(alias);
}

private Query parse(String alias, CompressedXContent filter) {
if (filter == null) {
Query parse(AliasMetaData alias) {
if (alias.filter() == null) {
return null;
}
try {
byte[] filterSource = filter.uncompressed();
byte[] filterSource = alias.filter().uncompressed();
try (XContentParser parser = XContentFactory.xContent(filterSource).createParser(filterSource)) {
ParsedQuery parsedFilter = indexQueryParser.parseInnerFilter(parser);
return parsedFilter == null ? null : parsedFilter.query();
}
} catch (IOException ex) {
throw new AliasFilterParsingException(index, alias, "Invalid alias filter", ex);
throw new AliasFilterParsingException(index, alias.getAlias(), "Invalid alias filter", ex);
}
}

@Override
public Iterator<IndexAlias> iterator() {
return aliases.values().iterator();
// Used by tests:
void add(String alias, @Nullable CompressedXContent filter) {
AliasMetaData aliasMetaData = AliasMetaData.builder(alias).filter(filter).build();
aliases = ImmutableOpenMap.builder(aliases).fPut(alias, aliasMetaData).build();
}

boolean hasAlias(String alias) {
return aliases.containsKey(alias);
}

void remove(String alias) {
aliases = ImmutableOpenMap.builder(aliases).fRemove(alias).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package org.elasticsearch.index.mapper.core;

import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType.NumericType;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Terms;
Expand Down Expand Up @@ -208,8 +207,37 @@ public LateParsingQuery(Object lowerTerm, Object upperTerm, boolean includeLower

@Override
public Query rewrite(IndexReader reader) throws IOException {
Query query = innerRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser);
return query.rewrite(reader);
return innerRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser);
}

// Even though we only cache rewritten queries it is good to let all queries implement hashCode() and equals():
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;

LateParsingQuery that = (LateParsingQuery) o;

if (includeLower != that.includeLower) return false;
if (includeUpper != that.includeUpper) return false;
if (lowerTerm != null ? !lowerTerm.equals(that.lowerTerm) : that.lowerTerm != null) return false;
if (upperTerm != null ? !upperTerm.equals(that.upperTerm) : that.upperTerm != null) return false;
if (timeZone != null ? !timeZone.equals(that.timeZone) : that.timeZone != null) return false;
return !(forcedDateParser != null ? !forcedDateParser.equals(that.forcedDateParser) : that.forcedDateParser != null);

}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (lowerTerm != null ? lowerTerm.hashCode() : 0);
result = 31 * result + (upperTerm != null ? upperTerm.hashCode() : 0);
result = 31 * result + (includeLower ? 1 : 0);
result = 31 * result + (includeUpper ? 1 : 0);
result = 31 * result + (timeZone != null ? timeZone.hashCode() : 0);
result = 31 * result + (forcedDateParser != null ? forcedDateParser.hashCode() : 0);
return result;
}

@Override
Expand Down Expand Up @@ -384,12 +412,7 @@ public FieldStats stats(Terms terms, int maxDoc) throws IOException {
}

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);
}
return new LateParsingQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, forcedDateParser);
}

private Query innerRangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable DateMathParser forcedDateParser) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,6 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
}

public static Query joinUtilHelper(String parentType, ParentChildIndexFieldData parentChildIndexFieldData, Query toQuery, ScoreType scoreType, Query innerQuery, int minChildren, int maxChildren) throws IOException {
SearchContext searchContext = SearchContext.current();
if (searchContext == null) {
throw new IllegalStateException("Search context is required to be set");
}

String joinField = ParentFieldMapper.joinField(parentType);
ScoreMode scoreMode;
// TODO: move entirely over from ScoreType to org.apache.lucene.join.ScoreMode, when we drop the 1.x parent child code.
switch (scoreType) {
Expand All @@ -225,15 +219,73 @@ public static Query joinUtilHelper(String parentType, ParentChildIndexFieldData
default:
throw new UnsupportedOperationException("score type [" + scoreType + "] not supported");
}
IndexReader indexReader = searchContext.searcher().getIndexReader();
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
IndexParentChildFieldData indexParentChildFieldData = parentChildIndexFieldData.loadGlobal(indexReader);
MultiDocValues.OrdinalMap ordinalMap = ParentChildIndexFieldData.getOrdinalMap(indexParentChildFieldData, parentType);

// 0 in pre 2.x p/c impl means unbounded
if (maxChildren == 0) {
maxChildren = Integer.MAX_VALUE;
}
return JoinUtil.createJoinQuery(joinField, innerQuery, toQuery, indexSearcher, scoreMode, ordinalMap, minChildren, maxChildren);
return new LateParsingQuery(toQuery, innerQuery, minChildren, maxChildren, parentType, scoreMode, parentChildIndexFieldData);
}

final static class LateParsingQuery extends Query {

private final Query toQuery;
private final Query innerQuery;
private final int minChildren;
private final int maxChildren;
private final String parentType;
private final ScoreMode scoreMode;
private final ParentChildIndexFieldData parentChildIndexFieldData;
private final Object identity = new Object();

LateParsingQuery(Query toQuery, Query innerQuery, int minChildren, int maxChildren, String parentType, ScoreMode scoreMode, ParentChildIndexFieldData parentChildIndexFieldData) {
this.toQuery = toQuery;
this.innerQuery = innerQuery;
this.minChildren = minChildren;
this.maxChildren = maxChildren;
this.parentType = parentType;
this.scoreMode = scoreMode;
this.parentChildIndexFieldData = parentChildIndexFieldData;
}

@Override
public Query rewrite(IndexReader reader) throws IOException {
SearchContext searchContext = SearchContext.current();
if (searchContext == null) {
throw new IllegalArgumentException("Search context is required to be set");
}

String joinField = ParentFieldMapper.joinField(parentType);
IndexReader indexReader = searchContext.searcher().getIndexReader();
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
IndexParentChildFieldData indexParentChildFieldData = parentChildIndexFieldData.loadGlobal(indexReader);
MultiDocValues.OrdinalMap ordinalMap = ParentChildIndexFieldData.getOrdinalMap(indexParentChildFieldData, parentType);
return JoinUtil.createJoinQuery(joinField, innerQuery, toQuery, indexSearcher, scoreMode, ordinalMap, minChildren, maxChildren);
}

// Even though we only cache rewritten queries it is good to let all queries implement hashCode() and equals():

// We can't check for actually equality here, since we need to IndexReader for this, but
// that isn't available on all cases during query parse time, so instead rely on identity:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;

LateParsingQuery that = (LateParsingQuery) o;
return identity.equals(that.identity);
}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + identity.hashCode();
return result;
}

@Override
public String toString(String s) {
return "LateParsingQuery {parentType=" + parentType + "}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -388,4 +388,5 @@ public boolean isDeprecatedSetting(String setting) {
public Version indexVersionCreated() {
return indexVersionCreated;
}

}
Loading