blob: 91b7a08ef57d3ea856c7e8d7ec166c9f133db7a0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.lucene.monitor;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;
/**
* Class to analyze and extract terms from a lucene query, to be used by
* a {@link Presearcher} in indexing.
*/
class QueryAnalyzer {
private final BiFunction<Query, TermWeightor, QueryTree> unknownQueryMapper;
QueryAnalyzer(List<CustomQueryHandler> queryTreeBuilders) {
this.unknownQueryMapper = buildMapper(queryTreeBuilders);
}
QueryAnalyzer() {
this.unknownQueryMapper = (q, w) -> null;
}
private static BiFunction<Query, TermWeightor, QueryTree> buildMapper(List<CustomQueryHandler> mappers) {
return (q, w) -> {
for (CustomQueryHandler mapper : mappers) {
QueryTree qt = mapper.handleQuery(q, w);
if (qt != null) {
return qt;
}
}
return null;
};
}
/**
* Create a {@link QueryTree} from a passed in Query or Filter
*
* @param luceneQuery the query to analyze
* @return a QueryTree describing the analyzed query
*/
QueryTree buildTree(Query luceneQuery, TermWeightor weightor) {
QueryBuilder builder = new QueryBuilder();
luceneQuery.visit(builder);
return builder.apply(weightor);
}
private class QueryBuilder extends QueryVisitor implements Function<TermWeightor, QueryTree> {
final List<Function<TermWeightor, QueryTree>> children = new ArrayList<>();
@Override
public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) {
if (occur == BooleanClause.Occur.MUST || occur == BooleanClause.Occur.FILTER) {
QueryBuilder n = new QueryBuilder();
children.add(n);
return n;
}
if (occur == BooleanClause.Occur.MUST_NOT) {
// Check if we're in a pure negative disjunction
if (parent instanceof BooleanQuery) {
BooleanQuery bq = (BooleanQuery) parent;
long positiveCount = bq.clauses().stream()
.filter(c -> c.getOccur() != BooleanClause.Occur.MUST_NOT)
.count();
if (positiveCount == 0) {
children.add(w -> QueryTree.anyTerm("PURE NEGATIVE QUERY[" + parent + "]"));
}
}
return QueryVisitor.EMPTY_VISITOR;
}
// It's a disjunction clause. If the parent has MUST or FILTER clauses, we can
// ignore it
if (parent instanceof BooleanQuery) {
BooleanQuery bq = (BooleanQuery) parent;
long requiredCount = bq.clauses().stream()
.filter(c -> c.getOccur() == BooleanClause.Occur.MUST || c.getOccur() == BooleanClause.Occur.FILTER)
.count();
if (requiredCount > 0) {
return QueryVisitor.EMPTY_VISITOR;
}
}
Disjunction n = new Disjunction();
children.add(n);
return n;
}
@Override
public void consumeTerms(Query query, Term... terms) {
for (Term term : terms) {
children.add(w -> QueryTree.term(term, w));
}
}
@Override
public void visitLeaf(Query query) {
children.add(w -> {
QueryTree q = unknownQueryMapper.apply(query, w);
if (q == null) {
return QueryTree.anyTerm(query.toString());
}
return q;
});
}
@Override
public QueryTree apply(TermWeightor termWeightor) {
return QueryTree.conjunction(children, termWeightor);
}
}
private class Disjunction extends QueryBuilder {
@Override
public QueryTree apply(TermWeightor termWeightor) {
return QueryTree.disjunction(children, termWeightor);
}
}
}