blob: 93053f6d678800df95ca0cb50da056798590bde1 [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.Collections;
import java.util.HashSet;
import java.util.Set;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.DisjunctionMaxQuery;
import org.apache.lucene.search.Query;
/**
* Split a disjunction query into its consituent parts, so that they can be indexed
* and run separately in the Monitor.
*/
public class QueryDecomposer {
/**
* Split a query up into individual parts that can be indexed and run separately
*
* @param q the query
* @return a collection of subqueries
*/
public Set<Query> decompose(Query q) {
if (q instanceof BooleanQuery)
return decomposeBoolean((BooleanQuery) q);
if (q instanceof DisjunctionMaxQuery) {
Set<Query> subqueries = new HashSet<>();
for (Query subq : ((DisjunctionMaxQuery) q).getDisjuncts()) {
subqueries.addAll(decompose(subq));
}
return subqueries;
}
if (q instanceof BoostQuery) {
return decomposeBoostQuery((BoostQuery) q);
}
return Collections.singleton(q);
}
public Set<Query> decomposeBoostQuery(BoostQuery q) {
if (q.getBoost() == 1.0)
return decompose(q.getQuery());
Set<Query> boostedDecomposedQueries = new HashSet<>();
for (Query subq : decompose(q.getQuery())) {
boostedDecomposedQueries.add(new BoostQuery(subq, q.getBoost()));
}
return boostedDecomposedQueries;
}
/**
* Decompose a {@link org.apache.lucene.search.BooleanQuery}
*
* @param q the boolean query
* @return a collection of subqueries
*/
public Set<Query> decomposeBoolean(BooleanQuery q) {
if (q.getMinimumNumberShouldMatch() > 1)
return Collections.singleton(q);
Set<Query> subqueries = new HashSet<>();
Set<Query> exclusions = new HashSet<>();
Set<Query> mandatory = new HashSet<>();
for (BooleanClause clause : q) {
if (clause.getOccur() == BooleanClause.Occur.MUST || clause.getOccur() == BooleanClause.Occur.FILTER)
mandatory.add(clause.getQuery());
else if (clause.getOccur() == BooleanClause.Occur.MUST_NOT)
exclusions.add(clause.getQuery());
else {
subqueries.addAll(decompose(clause.getQuery()));
}
}
// More than one MUST clause, or a single MUST clause with disjunctions
if (mandatory.size() > 1 || (mandatory.size() == 1 && subqueries.size() > 0))
return Collections.singleton(q);
// If we only have a single MUST clause and no SHOULD clauses, then we can
// decompose the MUST clause instead
if (mandatory.size() == 1) {
subqueries.addAll(decompose(mandatory.iterator().next()));
}
if (exclusions.size() == 0)
return subqueries;
// If there are exclusions, then we need to add them to all the decomposed
// queries
Set<Query> rewrittenSubqueries = new HashSet<>(subqueries.size());
for (Query subquery : subqueries) {
BooleanQuery.Builder bq = new BooleanQuery.Builder();
bq.add(subquery, BooleanClause.Occur.MUST);
for (Query ex : exclusions) {
bq.add(ex, BooleanClause.Occur.MUST_NOT);
}
rewrittenSubqueries.add(bq.build());
}
return rewrittenSubqueries;
}
}