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

import java.io.IOException;
import java.util.Objects;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.DoubleValues;
import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.LongValues;
import org.apache.lucene.search.LongValuesSource;

Class exposing static helper methods for generating DoubleValuesSource instances over some IndexReader statistics
/** * Class exposing static helper methods for generating DoubleValuesSource instances * over some IndexReader statistics */
public final class IndexReaderFunctions { // non-instantiable class private IndexReaderFunctions() {}
Creates a constant value source returning the docFreq of a given term
See Also:
  • docFreq.docFreq(Term)
/** * Creates a constant value source returning the docFreq of a given term * * @see IndexReader#docFreq(Term) */
public static DoubleValuesSource docFreq(Term term) { return new IndexReaderDoubleValuesSource(r -> (double) r.docFreq(term), "docFreq(" + term.toString() + ")"); }
Creates a constant value source returning the index's maxDoc
See Also:
  • maxDoc.maxDoc()
/** * Creates a constant value source returning the index's maxDoc * * @see IndexReader#maxDoc() */
public static DoubleValuesSource maxDoc() { return new IndexReaderDoubleValuesSource(IndexReader::maxDoc, "maxDoc()"); }
Creates a constant value source returning the index's numDocs
See Also:
  • numDocs.numDocs()
/** * Creates a constant value source returning the index's numDocs * * @see IndexReader#numDocs() */
public static DoubleValuesSource numDocs() { return new IndexReaderDoubleValuesSource(IndexReader::numDocs, "numDocs()"); }
Creates a constant value source returning the number of deleted docs in the index
See Also:
  • numDeletedDocs.numDeletedDocs()
/** * Creates a constant value source returning the number of deleted docs in the index * * @see IndexReader#numDeletedDocs() */
public static DoubleValuesSource numDeletedDocs() { return new IndexReaderDoubleValuesSource(IndexReader::numDeletedDocs, "numDeletedDocs()"); }
Creates a constant value source returning the sumTotalTermFreq for a field
See Also:
  • getSumTotalTermFreq.getSumTotalTermFreq(String)
/** * Creates a constant value source returning the sumTotalTermFreq for a field * * @see IndexReader#getSumTotalTermFreq(String) */
public static LongValuesSource sumTotalTermFreq(String field) { return new SumTotalTermFreqValuesSource(field); } private static class SumTotalTermFreqValuesSource extends LongValuesSource { private final String field; private SumTotalTermFreqValuesSource(String field) { this.field = field; } @Override public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { throw new UnsupportedOperationException("IndexReaderFunction must be rewritten before use"); } @Override public boolean needsScores() { return false; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SumTotalTermFreqValuesSource that = (SumTotalTermFreqValuesSource) o; return Objects.equals(field, that.field); } @Override public int hashCode() { return Objects.hash(field); } @Override public LongValuesSource rewrite(IndexSearcher searcher) throws IOException { return new NoCacheConstantLongValuesSource(searcher.getIndexReader().getSumTotalTermFreq(field), this); } @Override public String toString() { return "sumTotalTermFreq(" + field + ")"; } @Override public boolean isCacheable(LeafReaderContext ctx) { return false; } } private static class NoCacheConstantLongValuesSource extends LongValuesSource { final long value; final LongValuesSource parent; private NoCacheConstantLongValuesSource(long value, LongValuesSource parent) { this.value = value; this.parent = parent; } @Override public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { return new LongValues() { @Override public long longValue() throws IOException { return value; } @Override public boolean advanceExact(int doc) throws IOException { return true; } }; } @Override public boolean needsScores() { return false; } @Override public LongValuesSource rewrite(IndexSearcher reader) throws IOException { return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NoCacheConstantLongValuesSource)) return false; NoCacheConstantLongValuesSource that = (NoCacheConstantLongValuesSource) o; return value == that.value && Objects.equals(parent, that.parent); } @Override public int hashCode() { return Objects.hash(value, parent); } @Override public String toString() { return parent.toString(); } @Override public boolean isCacheable(LeafReaderContext ctx) { return false; } }
Creates a value source that returns the term freq of a given term for each document
See Also:
  • freq.freq()
/** * Creates a value source that returns the term freq of a given term for each document * * @see PostingsEnum#freq() */
public static DoubleValuesSource termFreq(Term term) { return new TermFreqDoubleValuesSource(term); } private static class TermFreqDoubleValuesSource extends DoubleValuesSource { private final Term term; private TermFreqDoubleValuesSource(Term term) { this.term = term; } @Override public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { Terms terms = ctx.reader().terms(term.field()); TermsEnum te = terms == null ? null : terms.iterator(); if (te == null || te.seekExact(term.bytes()) == false) { return DoubleValues.EMPTY; } final PostingsEnum pe = te.postings(null); assert pe != null; return new DoubleValues() { @Override public double doubleValue() throws IOException { return pe.freq(); } @Override public boolean advanceExact(int doc) throws IOException { if (pe.docID() > doc) return false; return pe.docID() == doc || pe.advance(doc) == doc; } }; } @Override public boolean needsScores() { return false; } @Override public DoubleValuesSource rewrite(IndexSearcher searcher) throws IOException { return this; } @Override public String toString() { return "termFreq(" + term.toString() + ")"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TermFreqDoubleValuesSource that = (TermFreqDoubleValuesSource) o; return Objects.equals(term, that.term); } @Override public int hashCode() { return Objects.hash(term); } @Override public boolean isCacheable(LeafReaderContext ctx) { return true; } }
Creates a constant value source returning the totalTermFreq for a given term
See Also:
  • totalTermFreq.totalTermFreq(Term)
/** * Creates a constant value source returning the totalTermFreq for a given term * * @see IndexReader#totalTermFreq(Term) */
public static DoubleValuesSource totalTermFreq(Term term) { return new IndexReaderDoubleValuesSource(r -> r.totalTermFreq(term), "totalTermFreq(" + term.toString() + ")"); }
Creates a constant value source returning the sumDocFreq for a given field
See Also:
  • getSumDocFreq.getSumDocFreq(String)
/** * Creates a constant value source returning the sumDocFreq for a given field * * @see IndexReader#getSumDocFreq(String) */
public static DoubleValuesSource sumDocFreq(String field) { return new IndexReaderDoubleValuesSource(r -> r.getSumDocFreq(field), "sumDocFreq(" + field + ")"); }
Creates a constant value source returning the docCount for a given field
See Also:
  • getDocCount.getDocCount(String)
/** * Creates a constant value source returning the docCount for a given field * * @see IndexReader#getDocCount(String) */
public static DoubleValuesSource docCount(String field) { return new IndexReaderDoubleValuesSource(r -> r.getDocCount(field), "docCount(" + field + ")"); } @FunctionalInterface private interface ReaderFunction { double apply(IndexReader reader) throws IOException; } private static class IndexReaderDoubleValuesSource extends DoubleValuesSource { private final ReaderFunction func; private final String description; private IndexReaderDoubleValuesSource(ReaderFunction func, String description) { this.func = func; this.description = description; } @Override public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { throw new UnsupportedOperationException("IndexReaderFunction must be rewritten before use"); } @Override public boolean needsScores() { return false; } @Override public DoubleValuesSource rewrite(IndexSearcher searcher) throws IOException { return new NoCacheConstantDoubleValuesSource(func.apply(searcher.getIndexReader()), this); } @Override public String toString() { return description; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IndexReaderDoubleValuesSource that = (IndexReaderDoubleValuesSource) o; return Objects.equals(description, that.description) && Objects.equals(func, that.func); } @Override public int hashCode() { return Objects.hash(description, func); } @Override public boolean isCacheable(LeafReaderContext ctx) { return false; } } private static class NoCacheConstantDoubleValuesSource extends DoubleValuesSource { final double value; final DoubleValuesSource parent; private NoCacheConstantDoubleValuesSource(double value, DoubleValuesSource parent) { this.value = value; this.parent = parent; } @Override public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException { return new DoubleValues() { @Override public double doubleValue() throws IOException { return value; } @Override public boolean advanceExact(int doc) throws IOException { return true; } }; } @Override public boolean needsScores() { return false; } @Override public DoubleValuesSource rewrite(IndexSearcher reader) throws IOException { return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof NoCacheConstantDoubleValuesSource)) return false; NoCacheConstantDoubleValuesSource that = (NoCacheConstantDoubleValuesSource) o; return Double.compare(that.value, value) == 0 && Objects.equals(parent, that.parent); } @Override public int hashCode() { return Objects.hash(value, parent); } @Override public String toString() { return parent.toString(); } @Override public boolean isCacheable(LeafReaderContext ctx) { return false; } } }