| /* |
| * 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.search; |
| |
| |
| import java.io.IOException; |
| import java.util.Objects; |
| |
| /** |
| * Returned by {@link Scorer#twoPhaseIterator()} |
| * to expose an approximation of a {@link DocIdSetIterator}. |
| * When the {@link #approximation()}'s |
| * {@link DocIdSetIterator#nextDoc()} or {@link DocIdSetIterator#advance(int)} |
| * return, {@link #matches()} needs to be checked in order to know whether the |
| * returned doc ID actually matches. |
| * @lucene.experimental |
| */ |
| public abstract class TwoPhaseIterator { |
| |
| protected final DocIdSetIterator approximation; |
| |
| /** Takes the approximation to be returned by {@link #approximation}. Not null. */ |
| protected TwoPhaseIterator(DocIdSetIterator approximation) { |
| this.approximation = Objects.requireNonNull(approximation); |
| } |
| |
| /** Return a {@link DocIdSetIterator} view of the provided |
| * {@link TwoPhaseIterator}. */ |
| public static DocIdSetIterator asDocIdSetIterator(TwoPhaseIterator twoPhaseIterator) { |
| return new TwoPhaseIteratorAsDocIdSetIterator(twoPhaseIterator); |
| } |
| |
| /** |
| * If the given {@link DocIdSetIterator} has been created with |
| * {@link #asDocIdSetIterator}, then this will return the wrapped |
| * {@link TwoPhaseIterator}. Otherwise this returns {@code null}. |
| */ |
| public static TwoPhaseIterator unwrap(DocIdSetIterator iterator) { |
| if (iterator instanceof TwoPhaseIteratorAsDocIdSetIterator) { |
| return ((TwoPhaseIteratorAsDocIdSetIterator) iterator).twoPhaseIterator; |
| } else { |
| return null; |
| } |
| } |
| |
| private static class TwoPhaseIteratorAsDocIdSetIterator extends DocIdSetIterator { |
| |
| final TwoPhaseIterator twoPhaseIterator; |
| final DocIdSetIterator approximation; |
| |
| TwoPhaseIteratorAsDocIdSetIterator(TwoPhaseIterator twoPhaseIterator) { |
| this.twoPhaseIterator = twoPhaseIterator; |
| this.approximation = twoPhaseIterator.approximation; |
| } |
| |
| @Override |
| public int docID() { |
| return approximation.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| return doNext(approximation.nextDoc()); |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| return doNext(approximation.advance(target)); |
| } |
| |
| private int doNext(int doc) throws IOException { |
| for (;; doc = approximation.nextDoc()) { |
| if (doc == NO_MORE_DOCS) { |
| return NO_MORE_DOCS; |
| } else if (twoPhaseIterator.matches()) { |
| return doc; |
| } |
| } |
| } |
| |
| @Override |
| public long cost() { |
| return approximation.cost(); |
| } |
| } |
| |
| /** Return an approximation. The returned {@link DocIdSetIterator} is a |
| * superset of the matching documents, and each match needs to be confirmed |
| * with {@link #matches()} in order to know whether it matches or not. */ |
| public DocIdSetIterator approximation() { |
| return approximation; |
| } |
| |
| /** Return whether the current doc ID that {@link #approximation()} is on matches. This |
| * method should only be called when the iterator is positioned -- ie. not |
| * when {@link DocIdSetIterator#docID()} is {@code -1} or |
| * {@link DocIdSetIterator#NO_MORE_DOCS} -- and at most once. */ |
| public abstract boolean matches() throws IOException; |
| |
| /** An estimate of the expected cost to determine that a single document {@link #matches()}. |
| * This can be called before iterating the documents of {@link #approximation()}. |
| * Returns an expected cost in number of simple operations like addition, multiplication, |
| * comparing two numbers and indexing an array. |
| * The returned value must be positive. |
| */ |
| public abstract float matchCost(); |
| |
| } |