~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/core/src/test/org/apache/solr/search/TestSort.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 
 * contributor license agreements.  See the NOTICE file distributed with
4
 
 * this work for additional information regarding copyright ownership.
5
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 
 * (the "License"); you may not use this file except in compliance with
7
 
 * the License.  You may obtain a copy of the License at
8
 
 *
9
 
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 
 *
11
 
 * Unless required by applicable law or agreed to in writing, software
12
 
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 
 * See the License for the specific language governing permissions and
15
 
 * limitations under the License.
16
 
 */
17
 
 
18
 
package org.apache.solr.search;
19
 
 
20
 
import org.apache.lucene.analysis.SimpleAnalyzer;
21
 
import org.apache.lucene.document.Document;
22
 
import org.apache.lucene.document.Field;
23
 
import org.apache.lucene.index.IndexReader;
24
 
import org.apache.lucene.index.IndexReader;
25
 
import org.apache.lucene.index.IndexWriter;
26
 
import org.apache.lucene.index.IndexWriterConfig;
27
 
import org.apache.lucene.search.*;
28
 
import org.apache.lucene.search.SortField;
29
 
import org.apache.lucene.store.Directory;
30
 
import org.apache.lucene.store.RAMDirectory;
31
 
import org.apache.lucene.util.OpenBitSet;
32
 
import org.apache.lucene.util._TestUtil;
33
 
 
34
 
import org.apache.solr.request.SolrQueryRequest;
35
 
 
36
 
import org.apache.solr.SolrTestCaseJ4;
37
 
 
38
 
import org.junit.BeforeClass;
39
 
 
40
 
import java.io.IOException;
41
 
import java.util.*;
42
 
 
43
 
public class TestSort extends SolrTestCaseJ4 {
44
 
  @BeforeClass
45
 
  public static void beforeClass() throws Exception {
46
 
    initCore("solrconfig.xml","schema-minimal.xml");
47
 
  }
48
 
 
49
 
  final Random r = random;
50
 
 
51
 
  static class MyDoc {
52
 
    int doc;
53
 
    String val;
54
 
    String val2;
55
 
 
56
 
    @Override
57
 
    public String toString() {
58
 
      return "{id=" +doc + " val1="+val + " val2="+val2 + "}";
59
 
    }
60
 
  }
61
 
 
62
 
  public void testRandomFieldNameSorts() throws Exception {
63
 
    SolrQueryRequest req = lrf.makeRequest("q", "*:*");
64
 
 
65
 
    final int iters = atLeast(5000);
66
 
    int numberOfOddities = 0;
67
 
 
68
 
    for (int i = 0; i < iters; i++) {
69
 
      final StringBuilder input = new StringBuilder();
70
 
      final String[] names = new String[_TestUtil.nextInt(r,1,10)];
71
 
      final boolean[] reverse = new boolean[names.length];
72
 
      for (int j = 0; j < names.length; j++) {
73
 
        names[j] = _TestUtil.randomRealisticUnicodeString(r, 1, 20);
74
 
 
75
 
        // reduce the likelyhood that the random str is a valid query or func 
76
 
        names[j] = names[j].replaceFirst("\\{","\\{\\{");
77
 
        names[j] = names[j].replaceFirst("\\(","\\(\\(");
78
 
        names[j] = names[j].replaceFirst("(\\\"|\\')","$1$1");
79
 
        names[j] = names[j].replaceFirst("(\\d)","$1x");
80
 
 
81
 
        // eliminate pesky problem chars
82
 
        names[j] = names[j].replaceAll("\\p{Cntrl}|\\p{javaWhitespace}","");
83
 
 
84
 
        if (0 == names[j].length()) {
85
 
          numberOfOddities++;
86
 
          // screw it, i'm taking my toys and going home
87
 
          names[j] = "last_ditch_i_give_up";
88
 
        }
89
 
        reverse[j] = r.nextBoolean();
90
 
 
91
 
        input.append(r.nextBoolean() ? " " : "");
92
 
        input.append(names[j]);
93
 
        input.append(" ");
94
 
        input.append(reverse[j] ? "desc," : "asc,");
95
 
      }
96
 
      input.deleteCharAt(input.length()-1);
97
 
      SortField[] sorts = null;
98
 
      try {
99
 
        sorts = QueryParsing.parseSort(input.toString(), req).getSort();
100
 
      } catch (RuntimeException e) {
101
 
        throw new RuntimeException("Failed to parse sort: " + input, e);
102
 
      }
103
 
      assertEquals("parsed sorts had unexpected size", 
104
 
                   names.length, sorts.length);
105
 
      for (int j = 0; j < names.length; j++) {
106
 
        assertEquals("sorts["+j+"] had unexpected reverse: " + input,
107
 
                     reverse[j], sorts[j].getReverse());
108
 
 
109
 
        final int type = sorts[j].getType();
110
 
 
111
 
        if (SortField.SCORE == type) {
112
 
          numberOfOddities++;
113
 
          assertEquals("sorts["+j+"] is (unexpectedly) type score : " + input,
114
 
                       "score", names[j]);
115
 
        } else if (SortField.DOC == type) {
116
 
          numberOfOddities++;
117
 
          assertEquals("sorts["+j+"] is (unexpectedly) type doc : " + input,
118
 
                       "_docid_", names[j]);
119
 
        } else if (SortField.CUSTOM == type) {
120
 
          numberOfOddities++;
121
 
 
122
 
          // our orig string better be parsable as a func/query
123
 
          QParser qp = 
124
 
            QParser.getParser(names[j], FunctionQParserPlugin.NAME, req);
125
 
          try { 
126
 
            Query q = qp.getQuery();
127
 
            assertNotNull("sorts["+j+"] had type " + type + 
128
 
                          " but parsed to null func/query: " + input, q);
129
 
          } catch (Exception e) {
130
 
            assertNull("sorts["+j+"] had type " + type + 
131
 
                       " but errored parsing as func/query: " + input, e);
132
 
          }
133
 
        } else {
134
 
          assertEquals("sorts["+j+"] had unexpected field: " + input,
135
 
                       names[j], sorts[j].getField());
136
 
        }
137
 
      }
138
 
    }
139
 
 
140
 
    assertTrue("Over 0.2% oddities in test: " +
141
 
               numberOfOddities + "/" + iters +
142
 
               " have func/query parsing semenatics gotten broader?",
143
 
               numberOfOddities < 0.002 * iters);
144
 
  }
145
 
 
146
 
 
147
 
 
148
 
  public void testSort() throws Exception {
149
 
    int iter =100;
150
 
    int qiter = 1000;
151
 
 
152
 
    Directory dir = new RAMDirectory();
153
 
    Field f = new Field("f","0", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
154
 
    Field f2 = new Field("f2","0", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS);
155
 
 
156
 
    for (int iterCnt = 0; iterCnt<iter; iterCnt++) {
157
 
      int ndocs = random.nextInt(100)+1;
158
 
      int maxval = random.nextInt(ndocs)+random.nextInt(ndocs)+1;
159
 
 
160
 
      IndexWriter iw = new IndexWriter(
161
 
          dir,
162
 
          new IndexWriterConfig(TEST_VERSION_CURRENT, new SimpleAnalyzer(TEST_VERSION_CURRENT)).
163
 
              setOpenMode(IndexWriterConfig.OpenMode.CREATE));
164
 
 
165
 
      int v1EmptyPercent = random.nextInt(40)+random.nextInt(40);
166
 
      int v2EmptyPercent = random.nextInt(40)+random.nextInt(40);
167
 
 
168
 
      for (int i=0; i< ndocs; i++) {
169
 
        Document document = new Document();
170
 
        if (r.nextInt(100) < v1EmptyPercent) {
171
 
          String val = Integer.toString(r.nextInt(maxval));
172
 
          f.setValue(val);
173
 
          document.add(f);
174
 
        }
175
 
        if (r.nextInt(100) < v2EmptyPercent) {
176
 
          String val2 = Integer.toString(r.nextInt(maxval));
177
 
          f2.setValue(val2);
178
 
          document.add(f2);
179
 
        }
180
 
 
181
 
        iw.addDocument(document);
182
 
        // duplicate a certain percent
183
 
        if (r.nextInt(100) < 10) {
184
 
          iw.addDocument(document);
185
 
        }
186
 
        if (random.nextInt(100) < 10) {
187
 
          iw.commit();
188
 
        }
189
 
      }
190
 
      iw.close();
191
 
 
192
 
 
193
 
      IndexReader reader = IndexReader.open(dir);
194
 
      IndexSearcher searcher = new IndexSearcher(reader);
195
 
 
196
 
      // build our model
197
 
      int maxDoc = searcher.maxDoc();
198
 
      final MyDoc[] mydocs = new MyDoc[maxDoc];
199
 
      for (int i=0; i<maxDoc; i++) {
200
 
        if (searcher.getIndexReader().isDeleted(i)) continue;
201
 
        Document doc = searcher.doc(i);
202
 
        MyDoc mydoc = new MyDoc();
203
 
        mydocs[i] = mydoc;
204
 
        mydoc.doc = i;
205
 
        mydoc.val = doc.get("f");
206
 
        mydoc.val2 = doc.get("f2");
207
 
      }
208
 
 
209
 
 
210
 
      for (int i=0; i<qiter; i++) {
211
 
        Filter filt = new Filter() {
212
 
          @Override
213
 
          public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
214
 
            return randSet(reader.maxDoc());
215
 
          }
216
 
        };
217
 
 
218
 
        int top = r.nextInt((ndocs>>3)+1)+1;
219
 
        final boolean luceneSort = r.nextBoolean();
220
 
        final boolean sortMissingLast = !luceneSort && r.nextBoolean();
221
 
        final boolean sortMissingFirst = !luceneSort && !sortMissingLast;
222
 
        final boolean reverse = r.nextBoolean();
223
 
        List<SortField> sfields = new ArrayList<SortField>();
224
 
 
225
 
        final boolean secondary = r.nextBoolean();
226
 
        final boolean luceneSort2 = r.nextBoolean();
227
 
        final boolean sortMissingLast2 = !luceneSort2 && r.nextBoolean();
228
 
        final boolean sortMissingFirst2 = !luceneSort2 && !sortMissingLast2;
229
 
        final boolean reverse2 = r.nextBoolean();
230
 
 
231
 
        if (r.nextBoolean()) sfields.add( new SortField(null, SortField.SCORE));
232
 
        // hit both use-cases of sort-missing-last
233
 
        sfields.add( Sorting.getStringSortField("f", reverse, sortMissingLast, sortMissingFirst) );
234
 
        if (secondary) {
235
 
          sfields.add( Sorting.getStringSortField("f2", reverse2, sortMissingLast2, sortMissingFirst2) );
236
 
        }
237
 
        if (r.nextBoolean()) sfields.add( new SortField(null, SortField.SCORE));
238
 
 
239
 
        Sort sort = new Sort(sfields.toArray(new SortField[sfields.size()]));
240
 
 
241
 
        final String nullRep = luceneSort || sortMissingFirst && !reverse || sortMissingLast && reverse ? "" : "zzz";
242
 
        final String nullRep2 = luceneSort2 || sortMissingFirst2 && !reverse2 || sortMissingLast2 && reverse2 ? "" : "zzz";
243
 
 
244
 
        boolean trackScores = r.nextBoolean();
245
 
        boolean trackMaxScores = r.nextBoolean();
246
 
        boolean scoreInOrder = r.nextBoolean();
247
 
        final TopFieldCollector topCollector = TopFieldCollector.create(sort, top, true, trackScores, trackMaxScores, scoreInOrder);
248
 
 
249
 
        final List<MyDoc> collectedDocs = new ArrayList<MyDoc>();
250
 
        // delegate and collect docs ourselves
251
 
        Collector myCollector = new Collector() {
252
 
          int docBase;
253
 
 
254
 
          @Override
255
 
          public void setScorer(Scorer scorer) throws IOException {
256
 
            topCollector.setScorer(scorer);
257
 
          }
258
 
 
259
 
          @Override
260
 
          public void collect(int doc) throws IOException {
261
 
            topCollector.collect(doc);
262
 
            collectedDocs.add(mydocs[doc + docBase]);
263
 
          }
264
 
 
265
 
          @Override
266
 
          public void setNextReader(IndexReader reader, int docBase) throws IOException {
267
 
            topCollector.setNextReader(reader,docBase);
268
 
            this.docBase = docBase;
269
 
          }
270
 
 
271
 
          @Override
272
 
          public boolean acceptsDocsOutOfOrder() {
273
 
            return topCollector.acceptsDocsOutOfOrder();
274
 
          }
275
 
        };
276
 
 
277
 
        searcher.search(new MatchAllDocsQuery(), filt, myCollector);
278
 
 
279
 
        Collections.sort(collectedDocs, new Comparator<MyDoc>() {
280
 
          public int compare(MyDoc o1, MyDoc o2) {
281
 
            String v1 = o1.val==null ? nullRep : o1.val;
282
 
            String v2 = o2.val==null ? nullRep : o2.val;
283
 
            int cmp = v1.compareTo(v2);
284
 
            if (reverse) cmp = -cmp;
285
 
            if (cmp != 0) return cmp;
286
 
 
287
 
            if (secondary) {
288
 
               v1 = o1.val2==null ? nullRep2 : o1.val2;
289
 
               v2 = o2.val2==null ? nullRep2 : o2.val2;
290
 
               cmp = v1.compareTo(v2);
291
 
               if (reverse2) cmp = -cmp;
292
 
            }
293
 
 
294
 
            cmp = cmp==0 ? o1.doc-o2.doc : cmp;
295
 
            return cmp;
296
 
          }
297
 
        });
298
 
 
299
 
 
300
 
        TopDocs topDocs = topCollector.topDocs();
301
 
        ScoreDoc[] sdocs = topDocs.scoreDocs;
302
 
        for (int j=0; j<sdocs.length; j++) {
303
 
          int id = sdocs[j].doc;
304
 
          if (id != collectedDocs.get(j).doc) {
305
 
            log.error("Error at pos " + j
306
 
            + "\n\tsortMissingFirst=" + sortMissingFirst + " sortMissingLast=" + sortMissingLast + " reverse=" + reverse
307
 
            + "\n\tsecondary="+secondary + "sortMissingFirst=" + sortMissingFirst2 + " sortMissingLast=" + sortMissingLast2 + " reverse=" + reverse2
308
 
            + "\n\tEXPECTED=" + collectedDocs
309
 
            );
310
 
          }
311
 
          assertEquals(id, collectedDocs.get(j).doc);
312
 
        }
313
 
      }
314
 
      searcher.close();
315
 
      reader.close();
316
 
    }
317
 
    dir.close();
318
 
 
319
 
  }
320
 
 
321
 
  public DocIdSet randSet(int sz) {
322
 
    OpenBitSet obs = new OpenBitSet(sz);
323
 
    int n = r.nextInt(sz);
324
 
    for (int i=0; i<n; i++) {
325
 
      obs.fastSet(r.nextInt(sz));
326
 
    }
327
 
    return obs;
328
 
  }  
329
 
  
330
 
 
331
 
}