package org.apache.lucene.search; /** * 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. */ import java.util.ConcurrentModificationException; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; /** * Test Hits searches with interleaved deletions. * * See {@link http://issues.apache.org/jira/browse/LUCENE-1096}. * @deprecated Hits will be removed in Lucene 3.0 */ public class TestSearchHitsWithDeletions extends LuceneTestCase { private static boolean VERBOSE = false; private static final String TEXT_FIELD = "text"; private static final int N = 16100; private static Directory directory; public void setUp() throws Exception { super.setUp(); // Create an index writer. directory = new RAMDirectory(); IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED); for (int i=0; i<N; i++) { writer.addDocument(createDocument(i)); } writer.optimize(); writer.close(); } /** * Deletions during search should not alter previously retrieved hits. */ public void testSearchHitsDeleteAll() throws Exception { doTestSearchHitsDeleteEvery(1,false); } /** * Deletions during search should not alter previously retrieved hits. */ public void testSearchHitsDeleteEvery2ndHit() throws Exception { doTestSearchHitsDeleteEvery(2,false); } /** * Deletions during search should not alter previously retrieved hits. */ public void testSearchHitsDeleteEvery4thHit() throws Exception { doTestSearchHitsDeleteEvery(4,false); } /** * Deletions during search should not alter previously retrieved hits. */ public void testSearchHitsDeleteEvery8thHit() throws Exception { doTestSearchHitsDeleteEvery(8,false); } /** * Deletions during search should not alter previously retrieved hits. */ public void testSearchHitsDeleteEvery90thHit() throws Exception { doTestSearchHitsDeleteEvery(90,false); } /** * Deletions during search should not alter previously retrieved hits, * and deletions that affect total number of hits should throw the * correct exception when trying to fetch "too many". */ public void testSearchHitsDeleteEvery8thHitAndInAdvance() throws Exception { doTestSearchHitsDeleteEvery(8,true); } /** * Verify that ok also with no deletions at all. */ public void testSearchHitsNoDeletes() throws Exception { doTestSearchHitsDeleteEvery(N+100,false); } /** * Deletions that affect total number of hits should throw the * correct exception when trying to fetch "too many". */ public void testSearchHitsDeleteInAdvance() throws Exception { doTestSearchHitsDeleteEvery(N+100,true); } /** * Intermittent deletions during search, should not alter previously retrieved hits. * (Using a debugger to verify that the check in Hits is performed only */ public void testSearchHitsDeleteIntermittent() throws Exception { doTestSearchHitsDeleteEvery(-1,false); } private void doTestSearchHitsDeleteEvery(int k, boolean deleteInFront) throws Exception { boolean intermittent = k<0; log("Test search hits with "+(intermittent ? "intermittent deletions." : "deletions of every "+k+" hit.")); IndexSearcher searcher = new IndexSearcher(directory); IndexReader reader = searcher.getIndexReader(); Query q = new TermQuery(new Term(TEXT_FIELD,"text")); // matching all docs Hits hits = searcher.search(q); log("Got "+hits.length()+" results"); assertEquals("must match all "+N+" docs, not only "+hits.length()+" docs!",N,hits.length()); if (deleteInFront) { log("deleting hits that was not yet retrieved!"); reader.deleteDocument(reader.maxDoc()-1); reader.deleteDocument(reader.maxDoc()-2); reader.deleteDocument(reader.maxDoc()-3); } try { for (int i = 0; i < hits.length(); i++) { int id = hits.id(i); assertEquals("Hit "+i+" has doc id "+hits.id(i)+" instead of "+i,i,hits.id(i)); if ((intermittent && (i==50 || i==250 || i==950)) || //100-yes, 200-no, 400-yes, 800-no, 1600-yes (!intermittent && (k<2 || (i>0 && i%k==0)))) { Document doc = hits.doc(id); log("Deleting hit "+i+" - doc "+doc+" with id "+id); reader.deleteDocument(id); } if (intermittent) { // check internal behavior of Hits (go 50 ahead of getMoreDocs points because the deletions cause to use more of the available hits) if (i==150 || i==450 || i==1650) { assertTrue("Hit "+i+": hits should have checked for deletions in last call to getMoreDocs()",hits.debugCheckedForDeletions); } else if (i==50 || i==250 || i==850) { assertFalse("Hit "+i+": hits should have NOT checked for deletions in last call to getMoreDocs()",hits.debugCheckedForDeletions); } } } } catch (ConcurrentModificationException e) { // this is the only valid exception, and only when deletng in front. assertTrue(e.getMessage()+" not expected unless deleting hits that were not yet seen!",deleteInFront); } searcher.close(); } private static Document createDocument(int id) { Document doc = new Document(); doc.add(new Field(TEXT_FIELD, "text of document"+id, Field.Store.YES, Field.Index.ANALYZED)); return doc; } private static void log (String s) { if (VERBOSE) { System.out.println(s); } } }