org.apache.lucene.facet.DrillDownQuery.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.facet.DrillDownQuery.java

Source

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

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

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;
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.
 * <p>
 * <b>NOTE:</b> if you choose to create your own {@link Query} by calling
 * {@link #term}, it is recommended to wrap it in a {@link BoostQuery}
 * with a boost of {@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 Query baseQuery;
    private final List<BooleanQuery.Builder> dimQueries = new ArrayList<>();
    private final Map<String, Integer> drillDownDims = new LinkedHashMap<>();

    /** Used by clone() and DrillSideways */
    DrillDownQuery(FacetsConfig config, Query baseQuery, List<BooleanQuery.Builder> dimQueries,
            Map<String, Integer> drillDownDims) {
        this.baseQuery = baseQuery;
        this.dimQueries.addAll(dimQueries);
        this.drillDownDims.putAll(drillDownDims);
        this.config = config;
    }

    /** Used by DrillSideways */
    DrillDownQuery(FacetsConfig config, Query filter, DrillDownQuery other) {
        this.baseQuery = new BooleanQuery.Builder()
                .add(other.baseQuery == null ? new MatchAllDocsQuery() : other.baseQuery, Occur.MUST)
                .add(filter, Occur.FILTER).build();
        this.dimQueries.addAll(other.dimQueries);
        this.drillDownDims.putAll(other.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) {
        this.baseQuery = baseQuery;
        this.config = config;
    }

    /** Adds one dimension of drill downs; if you pass the same
     *  dimension more than once it is OR'd with the previous
     *  constraints on that dimension, and all dimensions are
     *  AND'd against each other and the base query. */
    public void add(String dim, String... path) {
        String indexedField = config.getDimConfig(dim).indexFieldName;
        add(dim, new TermQuery(term(indexedField, dim, path)));
    }

    /** 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) {
        assert drillDownDims.size() == dimQueries.size();
        if (drillDownDims.containsKey(dim) == false) {
            drillDownDims.put(dim, drillDownDims.size());
            BooleanQuery.Builder builder = new BooleanQuery.Builder();
            dimQueries.add(builder);
        }
        final int index = drillDownDims.get(dim);
        dimQueries.get(index).add(subQuery, Occur.SHOULD);
    }

    @Override
    public DrillDownQuery clone() {
        return new DrillDownQuery(config, baseQuery, dimQueries, drillDownDims);
    }

    @Override
    public int hashCode() {
        return classHash() + Objects.hash(baseQuery, dimQueries);
    }

    @Override
    public boolean equals(Object other) {
        return sameClassAs(other) && equalsTo(getClass().cast(other));
    }

    private boolean equalsTo(DrillDownQuery other) {
        return Objects.equals(baseQuery, other.baseQuery) && dimQueries.equals(other.dimQueries);
    }

    @Override
    public Query rewrite(IndexReader r) throws IOException {
        BooleanQuery rewritten = getBooleanQuery();
        if (rewritten.clauses().isEmpty()) {
            return new MatchAllDocsQuery();
        }
        return rewritten;
    }

    @Override
    public String toString(String field) {
        return getBooleanQuery().toString(field);
    }

    @Override
    public void visit(QueryVisitor visitor) {
        visitor.visitLeaf(this);
    }

    private BooleanQuery getBooleanQuery() {
        BooleanQuery.Builder bq = new BooleanQuery.Builder();
        if (baseQuery != null) {
            bq.add(baseQuery, Occur.MUST);
        }
        for (BooleanQuery.Builder builder : dimQueries) {
            bq.add(builder.build(), Occur.FILTER);
        }
        return bq.build();
    }

    Query getBaseQuery() {
        return baseQuery;
    }

    Query[] getDrillDownQueries() {
        Query[] dimQueries = new Query[this.dimQueries.size()];
        for (int i = 0; i < dimQueries.length; ++i) {
            dimQueries[i] = this.dimQueries.get(i).build();
        }
        return dimQueries;
    }

    Map<String, Integer> getDims() {
        return drillDownDims;
    }
}