com.browseengine.bobo.geosearch.query.GeoScorerTest.java Source code

Java tutorial

Introduction

Here is the source code for com.browseengine.bobo.geosearch.query.GeoScorerTest.java

Source

/**
 * This software is licensed to you under the Apache License, Version 2.0 (the
 * "Apache License").
 *
 * LinkedIn's contributions are made under the Apache License. If you contribute
 * to the Software, the contributions will be deemed to have been made under the
 * Apache License, unless you expressly indicate otherwise. Please do not make any
 * contributions that would be inconsistent with the Apache License.
 *
 * You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, this software
 * distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache
 * License for the specific language governing permissions and limitations for the
 * software governed under the Apache License.
 *
 *  2012 LinkedIn Corp. All Rights Reserved.  
 */

package com.browseengine.bobo.geosearch.query;

import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.Weight;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.browseengine.bobo.geosearch.IGeoConverter;
import com.browseengine.bobo.geosearch.bo.CartesianGeoRecord;
import com.browseengine.bobo.geosearch.bo.GeoSearchConfig;
import com.browseengine.bobo.geosearch.bo.LatitudeLongitudeDocId;
import com.browseengine.bobo.geosearch.impl.CartesianGeoRecordComparator;
import com.browseengine.bobo.geosearch.impl.CartesianGeoRecordSerializer;
import com.browseengine.bobo.geosearch.impl.GeoConverter;
import com.browseengine.bobo.geosearch.impl.GeoRecordBTree;
import com.browseengine.bobo.geosearch.index.impl.GeoIndexReader;
import com.browseengine.bobo.geosearch.index.impl.GeoSegmentReader;

/**
 * @author Ken McCracken
 *
 */
public class GeoScorerTest {

    private IGeoConverter geoConverter;
    double centroidLongitude;
    double centroidLatitude;
    Float rangeInKm;
    private GeoQuery geoQuery;
    private Searcher searcher;
    private Weight geoWeight;
    private GeoIndexReader geoIndexReader;
    private Scorer scorer;
    private List<GeoSegmentReader<CartesianGeoRecord>> geoSubReaders;
    private GeoSegmentReader<CartesianGeoRecord> geoSegmentReader;

    private List<LatitudeLongitudeDocId> indexedDocuments;

    private LatitudeLongitudeDocId MISS_FAR_WEST;
    private LatitudeLongitudeDocId HIT0_CLOSE;
    private LatitudeLongitudeDocId MISS_FAR_SOUTH;
    private LatitudeLongitudeDocId HIT_EXACT;
    private LatitudeLongitudeDocId HIT1_CLOSE;
    private LatitudeLongitudeDocId HIT2_CLOSE;
    private LatitudeLongitudeDocId MISS_NOT_BY_MUCH;

    private int docid;
    private float score;

    private int maxDoc2;
    private TreeSet<CartesianGeoRecord> treeSet2;
    private GeoRecordBTree geoRecordBTree2;
    private GeoSegmentReader<CartesianGeoRecord> geoSegmentReader2;

    private static class MyGeoSegmentReader extends GeoSegmentReader<CartesianGeoRecord> {
        private final GeoRecordBTree tree;

        public MyGeoSegmentReader(GeoRecordBTree tree, int maxDoc) {
            super(tree.getArrayLength(), maxDoc, new CartesianGeoRecordSerializer(),
                    new CartesianGeoRecordComparator());
            this.tree = tree;
        }

        /**
         * Delegates to the GeoRecordBTree from the constructor.
         * 
         * {@inheritDoc}
         */
        @Override
        protected CartesianGeoRecord getValueAtIndex(int index) {
            return tree.getValueAtIndex(index);
        }
    }

    TreeSet<CartesianGeoRecord> treeSet;

    private List<LatitudeLongitudeDocId> indexedDocuments3;

    @Before
    public void setUp() throws Exception {
        geoConverter = new GeoConverter();

        centroidLongitude = -71.61f;
        centroidLatitude = 42.42f;
        rangeInKm = 5f * 1.609344f;

        docid = 0;
        indexedDocuments = new ArrayList<LatitudeLongitudeDocId>();
        MISS_FAR_WEST = new LatitudeLongitudeDocId(centroidLatitude, centroidLongitude - 20, docid++);
        indexedDocuments.add(MISS_FAR_WEST);
        HIT0_CLOSE = new LatitudeLongitudeDocId(centroidLatitude - 0.00001f, centroidLongitude + 0.0001f, docid++);
        indexedDocuments.add(HIT0_CLOSE);
        MISS_FAR_SOUTH = new LatitudeLongitudeDocId(centroidLatitude - 35, centroidLongitude, docid++);
        indexedDocuments.add(MISS_FAR_SOUTH);
        HIT_EXACT = new LatitudeLongitudeDocId(centroidLatitude, centroidLongitude, docid++);
        indexedDocuments.add(HIT_EXACT);
        HIT1_CLOSE = new LatitudeLongitudeDocId(centroidLatitude + 0.00033f, centroidLongitude - 0.00023f, docid++);
        indexedDocuments.add(HIT1_CLOSE);
        HIT2_CLOSE = new LatitudeLongitudeDocId(centroidLatitude + 0.00011f, centroidLongitude - 0.00034f, docid++);
        indexedDocuments.add(HIT2_CLOSE);

        indexedDocuments3 = new ArrayList<LatitudeLongitudeDocId>();

        geoSubReaders = new ArrayList<GeoSegmentReader<CartesianGeoRecord>>();
    }

    private LatitudeLongitudeDocId HIT3_CLOSE;
    private LatitudeLongitudeDocId MISS_EAST;
    private LatitudeLongitudeDocId SF_CENTER;
    private LatitudeLongitudeDocId HIT4_CLOSE;
    private LatitudeLongitudeDocId SF_OTHER1;
    private LatitudeLongitudeDocId SF_OTHER2;
    private Set<CartesianGeoRecord> treeSet3;
    private GeoRecordBTree geoRecordBTree3;
    private GeoSegmentReader<CartesianGeoRecord> geoSegmentReader3;
    private int maxDoc3;

    private void setupThirdSegment() throws Exception {
        // docids are within the segment because segments can get re-ordered and stacked 
        // arbitrarily from the perspective outside the segments.gen and segments file.
        docid = 0;
        HIT3_CLOSE = new LatitudeLongitudeDocId(centroidLatitude + 0.00007f, centroidLongitude + 0.00009f, docid++);
        indexedDocuments3.add(HIT3_CLOSE);
        MISS_EAST = new LatitudeLongitudeDocId(centroidLatitude + 10.00004f, centroidLongitude + 10.1001f, docid++);
        indexedDocuments3.add(MISS_EAST);
        SF_CENTER = new LatitudeLongitudeDocId(37.91f, -122.50f, docid++);
        indexedDocuments3.add(SF_CENTER);
        HIT4_CLOSE = new LatitudeLongitudeDocId(centroidLatitude + 0.00002f, centroidLongitude + 0.00016f, docid++);
        indexedDocuments3.add(HIT4_CLOSE);
        SF_OTHER1 = new LatitudeLongitudeDocId(SF_CENTER.latitude + 0.00007f, SF_CENTER.longitude + 0.00003f,
                docid++);
        indexedDocuments3.add(SF_OTHER1);
        SF_OTHER2 = new LatitudeLongitudeDocId(SF_CENTER.latitude + 0.00007f, SF_CENTER.longitude - 0.00003f,
                docid++);
        indexedDocuments3.add(SF_OTHER2);

        treeSet3 = new TreeSet<CartesianGeoRecord>(new CartesianGeoRecordComparator());
        for (LatitudeLongitudeDocId raw : indexedDocuments3) {
            CartesianGeoRecord geoRecord = geoConverter.toCartesianGeoRecord(raw,
                    CartesianGeoRecord.DEFAULT_FILTER_BYTE);
            treeSet3.add(geoRecord);
        }
        geoRecordBTree3 = new GeoRecordBTree(treeSet3);
        maxDoc3 = treeSet3.size();
        geoSegmentReader3 = new MyGeoSegmentReader(geoRecordBTree3, maxDoc3);
        geoSubReaders.add(geoSegmentReader3);
    }

    private void addMissNotByMuch() {
        MISS_NOT_BY_MUCH = new LatitudeLongitudeDocId(centroidLatitude + 0.09f, centroidLongitude - 0.08f, docid++);
        indexedDocuments.add(MISS_NOT_BY_MUCH);
    }

    private void setupOneTreeAndReaders() throws Exception {
        setupOneTree();
        setupQuerySearcherReaderScorer();
    }

    int maxDoc;

    private void setupOneTree() throws Exception {

        treeSet = new TreeSet<CartesianGeoRecord>(new CartesianGeoRecordComparator());
        for (LatitudeLongitudeDocId raw : indexedDocuments) {
            CartesianGeoRecord geoRecord = geoConverter.toCartesianGeoRecord(raw,
                    CartesianGeoRecord.DEFAULT_FILTER_BYTE);
            treeSet.add(geoRecord);
        }

        GeoRecordBTree geoRecordBTree = new GeoRecordBTree(treeSet);
        maxDoc = indexedDocuments.size();
        geoSegmentReader = new MyGeoSegmentReader(geoRecordBTree, maxDoc);

        geoSubReaders.add(geoSegmentReader);
    }

    private void setupQuerySearcherReaderScorer() throws Exception {
        searcher = null;

        Directory directory = buildEmptyDirectory();

        geoIndexReader = new GeoIndexReader(directory, new GeoSearchConfig()) {
            @Override
            public List<GeoSegmentReader<CartesianGeoRecord>> getGeoSegmentReaders() {
                return geoSubReaders;
            }

            @Override
            public IndexReader[] getSequentialSubReaders() {
                return null;
            }
        };

        geoQuery = new GeoQuery(centroidLatitude, centroidLongitude, rangeInKm);
        geoWeight = geoQuery.createWeight(searcher);
        boolean scoreDocsInOrder = true;
        boolean topScorer = true;

        scorer = geoWeight.scorer(geoIndexReader, scoreDocsInOrder, topScorer);

    }

    private Directory buildEmptyDirectory() throws IOException {
        RAMDirectory directory = new RAMDirectory();

        Version version = Version.LUCENE_CURRENT;
        Analyzer analyzer = new StandardAnalyzer(version);
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(version, analyzer);
        IndexWriter writer = new IndexWriter(directory, indexWriterConfig);

        writer.close();

        return directory;
    }

    @After
    public void tearDown() throws Exception {
    }

    private void setupOneTreeAndEmptySecondAndReaders() throws Exception {
        setupOneTree();
        addEmptySecondSegment();
        setupQuerySearcherReaderScorer();

    }

    private void setupOneTree_empty2nd_3rd_AndReaders() throws Exception {
        setupOneTree();
        addEmptySecondSegment();
        setupThirdSegment();

        setupQuerySearcherReaderScorer();
    }

    private void addEmptySecondSegment() throws Exception {
        treeSet2 = new TreeSet<CartesianGeoRecord>(new CartesianGeoRecordComparator());
        maxDoc2 = 6;
        geoRecordBTree2 = new GeoRecordBTree(treeSet2);
        geoSegmentReader2 = new MyGeoSegmentReader(geoRecordBTree2, maxDoc2);
        geoSubReaders.add(geoSegmentReader2);
    }

    @Test
    public void test_advance_and_score6() throws Exception {
        setupOneTreeAndReaders();

        verifyAdvanceAndScore();
    }

    @Test
    public void test_advance_and_score6_empty2nd() throws Exception {
        setupOneTreeAndEmptySecondAndReaders();

        verifyAdvanceAndScore();
    }

    @Test
    public void test_advance_and_score6_empty2nd_3rd() throws Exception {
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyAdvanceAndScore_3segments();
    }

    @Test
    public void test_advance_and_score6_empty2nd_3rd_jump() throws Exception {
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyAdvanceAndScore_3segments_jumpTo3rd();
    }

    @Test
    public void test_advance_and_score7() throws Exception {
        addMissNotByMuch();
        setupOneTreeAndReaders();

        verifyAdvanceAndScore();
    }

    @Test
    public void test_advance_and_score7_empty2nd() throws Exception {
        addMissNotByMuch();
        setupOneTreeAndEmptySecondAndReaders();

        verifyAdvanceAndScore();
    }

    @Test
    public void test_advance_and_score7_empty2nd_3rd() throws Exception {
        addMissNotByMuch();
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyAdvanceAndScore_3segments();
    }

    @Test
    public void test_advance_and_score7_empty2nd_3rd_jump() throws Exception {
        addMissNotByMuch();
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyAdvanceAndScore_3segments_jumpTo3rd();
    }

    @Test
    public void test_nextDoc_and_score6() throws Exception {
        setupOneTreeAndReaders();

        verifyNextDocAndScore();
    }

    @Test
    public void test_nextDoc_and_score6_empty2nd() throws Exception {
        setupOneTreeAndEmptySecondAndReaders();

        verifyNextDocAndScore();
    }

    @Test
    public void test_nextDoc_and_score6_empty2nd_3rd() throws Exception {
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyNextDocAndScore3segments();
    }

    @Test
    public void test_nextDoc_and_score7() throws Exception {
        addMissNotByMuch();
        setupOneTreeAndReaders();

        verifyNextDocAndScore();
    }

    @Test
    public void test_nextDoc_and_score7_empty2nd() throws Exception {
        addMissNotByMuch();
        setupOneTreeAndEmptySecondAndReaders();

        verifyNextDocAndScore();
    }

    @Test
    public void test_nextDoc_and_score7_empty2nd_3rd() throws Exception {
        addMissNotByMuch();
        setupOneTree_empty2nd_3rd_AndReaders();

        verifyNextDocAndScore3segments();
    }

    @Test
    public void test_noSegments() throws Exception {
        setupQuerySearcherReaderScorer();
        verifyEndNextDoc();
    }

    @Test
    public void test_sf_nextDoc_and_score7_empty2nd_3rd() throws Exception {
        addMissNotByMuch();
        setupOneTree_empty2nd_3rd_AndReaders();
        centroidLongitude = SF_CENTER.longitude + 0.0001f;
        centroidLatitude = SF_CENTER.latitude - 0.00002f;
        setupQuerySearcherReaderScorer();

        verifyNextDocAndScoreSF();
    }

    private void verifyAdvanceAndScore() throws Exception {
        verifyAdvanceAndScore_1segment();
        verifyEndAdvance(maxDoc);
    }

    private void verifyAdvanceAndScore_3segments_jumpTo3rd() throws Exception {
        verifyAdvanceAndScore_segment3();
        verifyEndAdvance(maxDoc + maxDoc2 + maxDoc3);
    }

    private void verifyAdvanceAndScore_3segments() throws Exception {
        verifyAdvanceAndScore_1segment();
        verifyAdvanceAndScore_segment3();
        verifyEndAdvance(maxDoc + maxDoc2 + maxDoc3);
    }

    private void verifyAdvanceAndScore_segment3() throws Exception {
        float expectedScoreLowerBound = 0.00001f;
        float expectedScoreUpperBound = 1f;

        int advanceTo = maxDoc + maxDoc2 + SF_CENTER.docid;

        int expectedDocid = maxDoc + maxDoc2 + HIT4_CLOSE.docid;
        docid = scorer.advance(advanceTo);
        assertTrue("expectedDocid " + expectedDocid + ", got actual docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

    }

    private void verifyAdvanceAndScore_1segment() throws Exception {
        // advance to second hit docid=3

        docid = scorer.advance(3);

        int expectedDocid = HIT_EXACT.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        float expectedScoreLowerBound = 0.00001f;
        float expectedScoreUpperBound = 1f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // third hit should be docid=4
        docid = scorer.advance(4);

        expectedDocid = HIT1_CLOSE.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        expectedScoreLowerBound = 0.00001f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);
    }

    private void verifyEndAdvance(int advanceTo) throws IOException {
        int expectedDocid;
        for (int i = 0; i < 10; i++) {
            // no more hits
            docid = scorer.advance(advanceTo);

            expectedDocid = DocIdSetIterator.NO_MORE_DOCS;
            assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);
        }

    }

    private void verifyNextDocAndScore() throws Exception {
        verifyNextDocAndScore_1segment();
        verifyEndNextDoc();

    }

    private void verifyNextDocAndScore3segments() throws Exception {
        verifyNextDocAndScore_1segment();

        int expectedDocid;
        float expectedScoreLowerBound = 0.00001f;
        float expectedScoreUpperBound = 1f;

        // next hit should be on segment 3
        docid = scorer.nextDoc();

        expectedDocid = HIT3_CLOSE.docid + maxDoc + maxDoc2;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // next hit is HIT4.
        docid = scorer.nextDoc();

        expectedDocid = HIT4_CLOSE.docid + maxDoc + maxDoc2;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // no more hits
        verifyEndNextDoc();
    }

    private void verifyNextDocAndScore_1segment() throws Exception {
        // first hit should be docid=1
        docid = scorer.nextDoc();

        int expectedDocid = HIT0_CLOSE.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        float expectedScoreLowerBound = 0.00001f;
        float expectedScoreUpperBound = 1f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // second hit should be docid=3
        docid = scorer.nextDoc();

        expectedDocid = HIT_EXACT.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        expectedScoreLowerBound = 0.99f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // third hit should be docid=4
        docid = scorer.nextDoc();

        expectedDocid = HIT1_CLOSE.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        expectedScoreLowerBound = 0.00001f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // fourth hit should be docid=5
        docid = scorer.nextDoc();

        expectedDocid = HIT2_CLOSE.docid;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        expectedScoreLowerBound = 0.00001f;
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

    }

    private void verifyNextDocAndScoreSF() throws Exception {
        int expectedDocid;
        float expectedScoreLowerBound = 0.00001f;
        float expectedScoreUpperBound = 1f;

        // next hit should be SF_CENTER
        docid = scorer.nextDoc();
        expectedDocid = SF_CENTER.docid + maxDoc + maxDoc2;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // next hit should be SF_OTHER1
        docid = scorer.nextDoc();
        expectedDocid = SF_OTHER1.docid + maxDoc + maxDoc2;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        // next hit should be SF_OTHER2
        docid = scorer.nextDoc();
        expectedDocid = SF_OTHER2.docid + maxDoc + maxDoc2;
        assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);

        score = scorer.score();
        assertTrue(
                "docid " + docid + ", expectedScoreLowerBound " + expectedScoreLowerBound
                        + ", expectedScoreUpperBound " + expectedScoreUpperBound + ", actual score " + score,
                expectedScoreLowerBound <= score && score <= expectedScoreUpperBound);

        verifyEndNextDoc();
    }

    private void verifyEndNextDoc() throws Exception {
        int expectedDocid;

        for (int i = 0; i < 10; i++) {
            // no more hits
            docid = scorer.nextDoc();

            expectedDocid = DocIdSetIterator.NO_MORE_DOCS;
            assertTrue("expectedDocid " + expectedDocid + ", got docid " + docid, expectedDocid == docid);
        }

    }

}