All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.lucene.facet.DrillDownQuery Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
package org.apache.lucene.facet;

/*
 * 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.
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;

/**
 * A {@link Query} for drill-down over facet categories. You
 * should call {@link #add(String, String...)} for every group of categories you
 * want to drill-down over.
 * 

* NOTE: if you choose to create your own {@link Query} by calling * {@link #term}, it is recommended to wrap it with {@link ConstantScoreQuery} * and set the {@link ConstantScoreQuery#setBoost(float) boost} to {@code 0.0f}, * so that it does not affect the scores of the documents. * * @lucene.experimental */ public final class DrillDownQuery extends Query { /** Creates a drill-down term. */ public static Term term(String field, String dim, String... path) { return new Term(field, FacetsConfig.pathToString(dim, path)); } private final FacetsConfig config; private final BooleanQuery query; private final Map drillDownDims = new LinkedHashMap(); /** Used by clone() */ DrillDownQuery(FacetsConfig config, BooleanQuery query, Map drillDownDims) { this.query = query.clone(); this.drillDownDims.putAll(drillDownDims); this.config = config; } /** Used by DrillSideways */ DrillDownQuery(FacetsConfig config, Filter filter, DrillDownQuery other) { query = new BooleanQuery(true); // disable coord BooleanClause[] clauses = other.query.getClauses(); if (clauses.length == other.drillDownDims.size()) { throw new IllegalArgumentException("cannot apply filter unless baseQuery isn't null; pass ConstantScoreQuery instead"); } assert clauses.length == 1+other.drillDownDims.size(): clauses.length + " vs " + (1+other.drillDownDims.size()); drillDownDims.putAll(other.drillDownDims); query.add(new FilteredQuery(clauses[0].getQuery(), filter), Occur.MUST); for(int i=1;i clauses, Map drillDownDims) { query = new BooleanQuery(true); if (baseQuery != null) { query.add(baseQuery, Occur.MUST); } for(Query clause : clauses) { query.add(clause, Occur.MUST); } this.drillDownDims.putAll(drillDownDims); this.config = config; } /** Creates a new {@code DrillDownQuery} without a base query, * to perform a pure browsing query (equivalent to using * {@link MatchAllDocsQuery} as base). */ public DrillDownQuery(FacetsConfig config) { this(config, null); } /** Creates a new {@code DrillDownQuery} over the given base query. Can be * {@code null}, in which case the result {@link Query} from * {@link #rewrite(IndexReader)} will be a pure browsing query, filtering on * the added categories only. */ public DrillDownQuery(FacetsConfig config, Query baseQuery) { query = new BooleanQuery(true); // disable coord if (baseQuery != null) { query.add(baseQuery, Occur.MUST); } this.config = config; } /** Merges (ORs) a new path into an existing AND'd * clause. */ private void merge(String dim, String[] path) { int index = drillDownDims.get(dim); if (query.getClauses().length == drillDownDims.size()+1) { index++; } ConstantScoreQuery q = (ConstantScoreQuery) query.clauses().get(index).getQuery(); if ((q.getQuery() instanceof BooleanQuery) == false) { // App called .add(dim, customQuery) and then tried to // merge a facet label in: throw new RuntimeException("cannot merge with custom Query"); } String indexedField = config.getDimConfig(dim).indexFieldName; BooleanQuery bq = (BooleanQuery) q.getQuery(); bq.add(new TermQuery(term(indexedField, dim, path)), Occur.SHOULD); } /** Adds one dimension of drill downs; if you pass the same * dimension more than once it is OR'd with the previous * cofnstraints on that dimension, and all dimensions are * AND'd against each other and the base query. */ public void add(String dim, String... path) { if (drillDownDims.containsKey(dim)) { merge(dim, path); return; } String indexedField = config.getDimConfig(dim).indexFieldName; BooleanQuery bq = new BooleanQuery(true); // disable coord bq.add(new TermQuery(term(indexedField, dim, path)), Occur.SHOULD); add(dim, bq); } /** Expert: add a custom drill-down subQuery. Use this * when you have a separate way to drill-down on the * dimension than the indexed facet ordinals. */ public void add(String dim, Query subQuery) { if (drillDownDims.containsKey(dim)) { throw new IllegalArgumentException("dimension \"" + dim + "\" already has a drill-down"); } // TODO: we should use FilteredQuery? // So scores of the drill-down query don't have an // effect: final ConstantScoreQuery drillDownQuery = new ConstantScoreQuery(subQuery); drillDownQuery.setBoost(0.0f); query.add(drillDownQuery, Occur.MUST); drillDownDims.put(dim, drillDownDims.size()); } /** Expert: add a custom drill-down Filter, e.g. when * drilling down after range faceting. */ public void add(String dim, Filter subFilter) { if (drillDownDims.containsKey(dim)) { throw new IllegalArgumentException("dimension \"" + dim + "\" already has a drill-down"); } // TODO: we should use FilteredQuery? // So scores of the drill-down query don't have an // effect: final ConstantScoreQuery drillDownQuery = new ConstantScoreQuery(subFilter); drillDownQuery.setBoost(0.0f); query.add(drillDownQuery, Occur.MUST); drillDownDims.put(dim, drillDownDims.size()); } static Filter getFilter(Query query) { if (query instanceof ConstantScoreQuery) { ConstantScoreQuery csq = (ConstantScoreQuery) query; Filter filter = csq.getFilter(); if (filter != null) { return filter; } else { return getFilter(csq.getQuery()); } } else { return null; } } @Override public DrillDownQuery clone() { return new DrillDownQuery(config, query, drillDownDims); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); return prime * result + query.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof DrillDownQuery)) { return false; } DrillDownQuery other = (DrillDownQuery) obj; return query.equals(other.query) && super.equals(other); } @Override public Query rewrite(IndexReader r) throws IOException { if (query.clauses().size() == 0) { return new MatchAllDocsQuery(); } List filters = new ArrayList(); List queries = new ArrayList(); List clauses = query.clauses(); Query baseQuery; int startIndex; if (drillDownDims.size() == query.clauses().size()) { baseQuery = new MatchAllDocsQuery(); startIndex = 0; } else { baseQuery = clauses.get(0).getQuery(); startIndex = 1; } for(int i=startIndex;i getDims() { return drillDownDims; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy