/** * 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.solr.search; import org.apache.lucene.analysis.core.SimpleAnalyzer; 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.search.*; import org.apache.lucene.store.Directory; import org.apache.lucene.util.OpenBitSet; import org.apache.solr.util.AbstractSolrTestCase; import java.io.IOException; import java.util.*; public class TestSort extends AbstractSolrTestCase { public String getSchemaFile() { return null; } public String getSolrConfigFile() { return null; } Random r = new Random(); int ndocs = 77; int iter = 100; int qiter = 1000; int commitCount = ndocs/5 + 1; int maxval = ndocs*2; static class MyDoc { int doc; String val; } public void testSort() throws Exception { Random random = newRandom(); Directory dir = newDirectory(random); Document smallDoc = new Document(); // Field id = new Field("id","0", Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS); Field f = new Field("f","0", Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS); smallDoc.add(f); Document emptyDoc = new Document(); for (int iterCnt = 0; iterCnt<iter; iterCnt++) { IndexWriter iw = new IndexWriter(dir, new SimpleAnalyzer(), true, IndexWriter.MaxFieldLength.UNLIMITED); final MyDoc[] mydocs = new MyDoc[ndocs]; int commitCountdown = commitCount; for (int i=0; i< ndocs; i++) { Document doc; MyDoc mydoc = new MyDoc(); mydoc.doc = i; mydocs[i] = mydoc; if (r.nextInt(3)==0) { doc = emptyDoc; mydoc.val = null; } else { mydoc.val = Integer.toString(r.nextInt(maxval)); f.setValue(mydoc.val); doc = smallDoc; } iw.addDocument(doc); if (--commitCountdown <= 0) { commitCountdown = commitCount; iw.commit(); } } iw.close(); /*** Arrays.sort(mydocs, new Comparator<MyDoc>() { public int compare(MyDoc o1, MyDoc o2) { String v1 = o1.val==null ? "zzz" : o1.val; String v2 = o2.val==null ? "zzz" : o2.val; int cmp = v1.compareTo(v2); cmp = cmp==0 ? o1.doc-o2.doc : cmp; return cmp; } }); ***/ IndexSearcher searcher = new IndexSearcher(dir, true); // System.out.println("segments="+searcher.getIndexReader().getSequentialSubReaders().length); assertTrue(searcher.getIndexReader().getSequentialSubReaders().length > 1); for (int i=0; i<qiter; i++) { Filter filt = new Filter() { @Override public DocIdSet getDocIdSet(IndexReader reader) throws IOException { return randSet(reader.maxDoc()); } }; int top = r.nextInt((ndocs>>3)+1)+1; final boolean sortMissingLast = r.nextBoolean(); final boolean reverse = !sortMissingLast; List<SortField> sfields = new ArrayList<SortField>(); if (r.nextBoolean()) sfields.add( new SortField(null, SortField.SCORE)); // hit both use-cases of sort-missing-last sfields.add( Sorting.getStringSortField("f", reverse, sortMissingLast, !sortMissingLast) ); if (r.nextBoolean()) sfields.add( new SortField(null, SortField.SCORE)); Sort sort = new Sort(sfields.toArray(new SortField[sfields.size()])); // final String nullRep = sortMissingLast ? "zzz" : ""; final String nullRep = "zzz"; boolean trackScores = r.nextBoolean(); boolean trackMaxScores = r.nextBoolean(); boolean scoreInOrder = r.nextBoolean(); final TopFieldCollector topCollector = TopFieldCollector.create(sort, top, true, trackScores, trackMaxScores, scoreInOrder); final List<MyDoc> collectedDocs = new ArrayList<MyDoc>(); // delegate and collect docs ourselves Collector myCollector = new Collector() { int docBase; @Override public void setScorer(Scorer scorer) throws IOException { topCollector.setScorer(scorer); } @Override public void collect(int doc) throws IOException { topCollector.collect(doc); collectedDocs.add(mydocs[doc + docBase]); } @Override public void setNextReader(IndexReader reader, int docBase) throws IOException { topCollector.setNextReader(reader,docBase); this.docBase = docBase; } @Override public boolean acceptsDocsOutOfOrder() { return topCollector.acceptsDocsOutOfOrder(); } }; searcher.search(new MatchAllDocsQuery(), filt, myCollector); Collections.sort(collectedDocs, new Comparator<MyDoc>() { public int compare(MyDoc o1, MyDoc o2) { String v1 = o1.val==null ? nullRep : o1.val; String v2 = o2.val==null ? nullRep : o2.val; int cmp = v1.compareTo(v2); if (reverse) cmp = -cmp; cmp = cmp==0 ? o1.doc-o2.doc : cmp; return cmp; } }); TopDocs topDocs = topCollector.topDocs(); ScoreDoc[] sdocs = topDocs.scoreDocs; for (int j=0; j<sdocs.length; j++) { int id = sdocs[j].doc; if (id != collectedDocs.get(j).doc) { log.error("Error at pos " + j); } assertEquals(id, collectedDocs.get(j).doc); } } searcher.close(); } dir.close(); } public DocIdSet randSet(int sz) { OpenBitSet obs = new OpenBitSet(sz); int n = r.nextInt(sz); for (int i=0; i<n; i++) { obs.fastSet(r.nextInt(sz)); } return obs; } }