/** * Copyright 2014 National University of Ireland, Galway. * * This file is part of the SIREn project. Project and contact information: * * https://github.com/rdelbru/SIREn * * Licensed 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.sindice.siren.search.node; import java.io.IOException; import java.util.Collection; import org.apache.lucene.search.Collector; import org.apache.lucene.search.Scorer; /** * The {@link Scorer} class that defines the interface for iterating * over an ordered list of documents matching a {@link NodeQuery}. */ class LuceneProxyNodeScorer extends Scorer { private int lastDoc = -1; private float score; private int freq; private final NodeScorer scorer; public LuceneProxyNodeScorer(final NodeScorer scorer) { super(scorer.getWeight()); this.scorer = scorer; } /** * Scores and collects all matching documents. * * @param collector * The collector to which all matching documents are passed through. */ @Override public void score(final Collector collector) throws IOException { collector.setScorer(this); while (this.nextDoc() != NO_MORE_DOCS) { collector.collect(this.docID()); } } /** * Expert: Collects matching documents in a range. Hook for optimization. Note * that {@link #nextDoc()} must be called once before this method is * called for the first time. * * @param collector * The collector to which all matching documents are passed through. * @param max * Do not score documents past this. * @return true if more matching documents may remain. */ @Override public boolean score(final Collector collector, final int max, final int firstDocID) throws IOException { // firstDocID is ignored since nextDocument() sets 'currentDoc' collector.setScorer(this); while (this.docID() < max) { collector.collect(this.docID()); if (this.nextDoc() == NO_MORE_DOCS) { return false; } } return true; } @Override public int docID() { return scorer.doc(); } @Override public int advance(final int target) throws IOException { if (scorer.skipToCandidate(target)) { do { if (scorer.nextNode()) { return this.docID(); } } while (scorer.nextCandidateDocument()); } return NO_MORE_DOCS; } @Override public int nextDoc() throws IOException { while (scorer.nextCandidateDocument()) { if (scorer.nextNode()) { // check if there is at least 1 node that matches the query return this.docID(); } } return NO_MORE_DOCS; } @Override public float score() throws IOException { this.computeScoreAndFreq(); return score; } /** * Returns number of matches for the current document. This returns a float * (not int) because SloppyPhraseScorer discounts its freq according to how * "sloppy" the match was. * <p> * Only valid after calling {@link #nextDoc()} or {@link #advance(int)} */ @Override public float freq() throws IOException { this.computeScoreAndFreq(); return freq; } @Override public Collection<ChildScorer> getChildren() { return scorer.getChildren(); } /** * Compute the score and the frequency of the current document * @throws IOException */ private void computeScoreAndFreq() throws IOException { final int doc = this.docID(); if (doc != lastDoc) { lastDoc = doc; score = 0; freq = 0; do { // nextNode() was already called in nextDoc() or in advance() score += scorer.scoreInNode(); freq += scorer.freqInNode(); } while (scorer.nextNode()); } } }