| /* |
| * 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(); |
| } |