/* * 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.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.MultiFields; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LineFileDocs; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestUtil; public class TestSameScoresWithThreads extends LuceneTestCase { public void test() throws Exception { final Directory dir = newDirectory(); MockAnalyzer analyzer = new MockAnalyzer(random()); analyzer.setMaxTokenLength(TestUtil.nextInt(random(), 1, IndexWriter.MAX_TERM_LENGTH)); final RandomIndexWriter w = new RandomIndexWriter(random(), dir, analyzer); LineFileDocs docs = new LineFileDocs(random()); int charsToIndex = atLeast(100000); int charsIndexed = 0; //System.out.println("bytesToIndex=" + charsToIndex); while(charsIndexed < charsToIndex) { Document doc = docs.nextDoc(); charsIndexed += doc.get("body").length(); w.addDocument(doc); //System.out.println(" bytes=" + charsIndexed + " add: " + doc); } IndexReader r = w.getReader(); //System.out.println("numDocs=" + r.numDocs()); w.close(); final IndexSearcher s = newSearcher(r); Terms terms = MultiFields.getFields(r).terms("body"); int termCount = 0; TermsEnum termsEnum = terms.iterator(); while(termsEnum.next() != null) { termCount++; } assertTrue(termCount > 0); // Target ~10 terms to search: double chance = 10.0 / termCount; termsEnum = terms.iterator(); final Map<BytesRef,TopDocs> answers = new HashMap<>(); while(termsEnum.next() != null) { if (random().nextDouble() <= chance) { BytesRef term = BytesRef.deepCopyOf(termsEnum.term()); answers.put(term, s.search(new TermQuery(new Term("body", term)), 100)); } } if (!answers.isEmpty()) { final CountDownLatch startingGun = new CountDownLatch(1); int numThreads = TestUtil.nextInt(random(), 2, 5); Thread[] threads = new Thread[numThreads]; for(int threadID=0;threadID<numThreads;threadID++) { Thread thread = new Thread() { @Override public void run() { try { startingGun.await(); for(int i=0;i<20;i++) { List<Map.Entry<BytesRef,TopDocs>> shuffled = new ArrayList<>(answers.entrySet()); Collections.shuffle(shuffled, random()); for(Map.Entry<BytesRef,TopDocs> ent : shuffled) { TopDocs actual = s.search(new TermQuery(new Term("body", ent.getKey())), 100); TopDocs expected = ent.getValue(); assertEquals(expected.totalHits, actual.totalHits); assertEquals("query=" + ent.getKey().utf8ToString(), expected.scoreDocs.length, actual.scoreDocs.length); for(int hit=0;hit<expected.scoreDocs.length;hit++) { assertEquals(expected.scoreDocs[hit].doc, actual.scoreDocs[hit].doc); // Floats really should be identical: assertTrue(expected.scoreDocs[hit].score == actual.scoreDocs[hit].score); } } } } catch (Exception e) { throw new RuntimeException(e); } } }; threads[threadID] = thread; thread.start(); } startingGun.countDown(); for(Thread thread : threads) { thread.join(); } } r.close(); dir.close(); } }