001: package org.apache.lucene.search;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import junit.framework.Test;
021: import junit.framework.TestCase;
022: import junit.framework.TestSuite;
023: import junit.textui.TestRunner;
024: import org.apache.lucene.analysis.SimpleAnalyzer;
025: import org.apache.lucene.document.Document;
026: import org.apache.lucene.document.Field;
027: import org.apache.lucene.index.IndexReader;
028: import org.apache.lucene.index.IndexWriter;
029: import org.apache.lucene.index.Term;
030: import org.apache.lucene.store.RAMDirectory;
031:
032: import java.io.IOException;
033: import java.io.Serializable;
034: import java.rmi.Naming;
035: import java.rmi.registry.LocateRegistry;
036: import java.rmi.registry.Registry;
037: import java.util.BitSet;
038: import java.util.HashMap;
039: import java.util.Iterator;
040: import java.util.Locale;
041: import java.util.regex.Pattern;
042:
043: /**
044: * Unit tests for sorting code.
045: *
046: * <p>Created: Feb 17, 2004 4:55:10 PM
047: *
048: * @author Tim Jones (Nacimiento Software)
049: * @since lucene 1.4
050: * @version $Id: TestSort.java 590530 2007-10-31 01:28:25Z gsingers $
051: */
052:
053: public class TestSort extends TestCase implements Serializable {
054:
055: private Searcher full;
056: private Searcher searchX;
057: private Searcher searchY;
058: private Query queryX;
059: private Query queryY;
060: private Query queryA;
061: private Query queryE;
062: private Query queryF;
063: private Query queryG;
064: private Sort sort;
065:
066: public TestSort(String name) {
067: super (name);
068: }
069:
070: public static void main(String[] argv) {
071: if (argv == null || argv.length < 1)
072: TestRunner.run(suite());
073: else if ("server".equals(argv[0])) {
074: TestSort test = new TestSort(null);
075: try {
076: test.startServer();
077: Thread.sleep(500000);
078: } catch (Exception e) {
079: System.out.println(e);
080: e.printStackTrace();
081: }
082: }
083: }
084:
085: public static Test suite() {
086: return new TestSuite(TestSort.class);
087: }
088:
089: // document data:
090: // the tracer field is used to determine which document was hit
091: // the contents field is used to search and sort by relevance
092: // the int field to sort by int
093: // the float field to sort by float
094: // the string field to sort by string
095: // the i18n field includes accented characters for testing locale-specific sorting
096: private String[][] data = new String[][] {
097: // tracer contents int float string custom i18n long double
098: { "A", "x a", "5", "4f", "c", "A-3", "p\u00EAche", "10",
099: "-4.0" },//A
100: { "B", "y a", "5", "3.4028235E38", "i", "B-10", "HAT",
101: "1000000000", "40.0" },//B
102: { "C", "x a b c", "2147483647", "1.0", "j", "A-2",
103: "p\u00E9ch\u00E9", "99999999", "40.00002343" },//C
104: { "D", "y a b c", "-1", "0.0f", "a", "C-0", "HUT",
105: String.valueOf(Long.MAX_VALUE),
106: String.valueOf(Double.MIN_VALUE) },//D
107: { "E", "x a b c d", "5", "2f", "h", "B-8", "peach",
108: String.valueOf(Long.MIN_VALUE),
109: String.valueOf(Double.MAX_VALUE) },//E
110: { "F", "y a b c d", "2", "3.14159f", "g", "B-1",
111: "H\u00C5T", "-44", "343.034435444" },//F
112: { "G", "x a b c d", "3", "-1.0", "f", "C-100", "sin",
113: "323254543543", "4.043544" },//G
114: { "H", "y a b c d", "0", "1.4E-45", "e", "C-88",
115: "H\u00D8T", "1023423423005", "4.043545" },//H
116: { "I", "x a b c d e f", "-2147483648", "1.0e+0", "d",
117: "A-10", "s\u00EDn", "332422459999", "4.043546" },//I
118: { "J", "y a b c d e f", "4", ".5", "b", "C-7", "HOT",
119: "34334543543", "4.0000220343" },//J
120: { "W", "g", "1", null, null, null, null, null, null },
121: { "X", "g", "1", "0.1", null, null, null, null, null },
122: { "Y", "g", "1", "0.2", null, null, null, null, null },
123: { "Z", "f g", null, null, null, null, null, null, null } };
124:
125: // create an index of all the documents, or just the x, or just the y documents
126: private Searcher getIndex(boolean even, boolean odd)
127: throws IOException {
128: RAMDirectory indexStore = new RAMDirectory();
129: IndexWriter writer = new IndexWriter(indexStore,
130: new SimpleAnalyzer(), true);
131: for (int i = 0; i < data.length; ++i) {
132: if (((i % 2) == 0 && even) || ((i % 2) == 1 && odd)) {
133: Document doc = new Document();
134: doc.add(new Field("tracer", data[i][0],
135: Field.Store.YES, Field.Index.NO));
136: doc.add(new Field("contents", data[i][1],
137: Field.Store.NO, Field.Index.TOKENIZED));
138: if (data[i][2] != null)
139: doc.add(new Field("int", data[i][2],
140: Field.Store.NO, Field.Index.UN_TOKENIZED));
141: if (data[i][3] != null)
142: doc.add(new Field("float", data[i][3],
143: Field.Store.NO, Field.Index.UN_TOKENIZED));
144: if (data[i][4] != null)
145: doc.add(new Field("string", data[i][4],
146: Field.Store.NO, Field.Index.UN_TOKENIZED));
147: if (data[i][5] != null)
148: doc.add(new Field("custom", data[i][5],
149: Field.Store.NO, Field.Index.UN_TOKENIZED));
150: if (data[i][6] != null)
151: doc.add(new Field("i18n", data[i][6],
152: Field.Store.NO, Field.Index.UN_TOKENIZED));
153: if (data[i][7] != null)
154: doc.add(new Field("long", data[i][7],
155: Field.Store.NO, Field.Index.UN_TOKENIZED));
156: if (data[i][8] != null)
157: doc.add(new Field("double", data[i][8],
158: Field.Store.NO, Field.Index.UN_TOKENIZED));
159: doc.setBoost(2); // produce some scores above 1.0
160: writer.addDocument(doc);
161: }
162: }
163: writer.optimize();
164: writer.close();
165: return new IndexSearcher(indexStore);
166: }
167:
168: private Searcher getFullIndex() throws IOException {
169: return getIndex(true, true);
170: }
171:
172: private Searcher getXIndex() throws IOException {
173: return getIndex(true, false);
174: }
175:
176: private Searcher getYIndex() throws IOException {
177: return getIndex(false, true);
178: }
179:
180: private Searcher getEmptyIndex() throws IOException {
181: return getIndex(false, false);
182: }
183:
184: public void setUp() throws Exception {
185: full = getFullIndex();
186: searchX = getXIndex();
187: searchY = getYIndex();
188: queryX = new TermQuery(new Term("contents", "x"));
189: queryY = new TermQuery(new Term("contents", "y"));
190: queryA = new TermQuery(new Term("contents", "a"));
191: queryE = new TermQuery(new Term("contents", "e"));
192: queryF = new TermQuery(new Term("contents", "f"));
193: queryG = new TermQuery(new Term("contents", "g"));
194: sort = new Sort();
195: }
196:
197: // test the sorts by score and document number
198: public void testBuiltInSorts() throws Exception {
199: sort = new Sort();
200: assertMatches(full, queryX, sort, "ACEGI");
201: assertMatches(full, queryY, sort, "BDFHJ");
202:
203: sort.setSort(SortField.FIELD_DOC);
204: assertMatches(full, queryX, sort, "ACEGI");
205: assertMatches(full, queryY, sort, "BDFHJ");
206: }
207:
208: // test sorts where the type of field is specified
209: public void testTypedSort() throws Exception {
210: sort.setSort(new SortField[] {
211: new SortField("int", SortField.INT),
212: SortField.FIELD_DOC });
213: assertMatches(full, queryX, sort, "IGAEC");
214: assertMatches(full, queryY, sort, "DHFJB");
215:
216: sort.setSort(new SortField[] {
217: new SortField("float", SortField.FLOAT),
218: SortField.FIELD_DOC });
219: assertMatches(full, queryX, sort, "GCIEA");
220: assertMatches(full, queryY, sort, "DHJFB");
221:
222: sort.setSort(new SortField[] {
223: new SortField("long", SortField.LONG),
224: SortField.FIELD_DOC });
225: assertMatches(full, queryX, sort, "EACGI");
226: assertMatches(full, queryY, sort, "FBJHD");
227:
228: sort.setSort(new SortField[] {
229: new SortField("double", SortField.DOUBLE),
230: SortField.FIELD_DOC });
231: assertMatches(full, queryX, sort, "AGICE");
232: assertMatches(full, queryY, sort, "DJHBF");
233:
234: sort.setSort(new SortField[] {
235: new SortField("string", SortField.STRING),
236: SortField.FIELD_DOC });
237: assertMatches(full, queryX, sort, "AIGEC");
238: assertMatches(full, queryY, sort, "DJHFB");
239: }
240:
241: // test sorts when there's nothing in the index
242: public void testEmptyIndex() throws Exception {
243: Searcher empty = getEmptyIndex();
244:
245: sort = new Sort();
246: assertMatches(empty, queryX, sort, "");
247:
248: sort.setSort(SortField.FIELD_DOC);
249: assertMatches(empty, queryX, sort, "");
250:
251: sort.setSort(new SortField[] {
252: new SortField("int", SortField.INT),
253: SortField.FIELD_DOC });
254: assertMatches(empty, queryX, sort, "");
255:
256: sort.setSort(new SortField[] {
257: new SortField("string", SortField.STRING, true),
258: SortField.FIELD_DOC });
259: assertMatches(empty, queryX, sort, "");
260:
261: sort.setSort(new SortField[] {
262: new SortField("float", SortField.FLOAT),
263: new SortField("string", SortField.STRING) });
264: assertMatches(empty, queryX, sort, "");
265: }
266:
267: // test sorts where the type of field is determined dynamically
268: public void testAutoSort() throws Exception {
269: sort.setSort("int");
270: assertMatches(full, queryX, sort, "IGAEC");
271: assertMatches(full, queryY, sort, "DHFJB");
272:
273: sort.setSort("float");
274: assertMatches(full, queryX, sort, "GCIEA");
275: assertMatches(full, queryY, sort, "DHJFB");
276:
277: sort.setSort("string");
278: assertMatches(full, queryX, sort, "AIGEC");
279: assertMatches(full, queryY, sort, "DJHFB");
280: }
281:
282: // test sorts in reverse
283: public void testReverseSort() throws Exception {
284: sort.setSort(new SortField[] {
285: new SortField(null, SortField.SCORE, true),
286: SortField.FIELD_DOC });
287: assertMatches(full, queryX, sort, "IEGCA");
288: assertMatches(full, queryY, sort, "JFHDB");
289:
290: sort.setSort(new SortField(null, SortField.DOC, true));
291: assertMatches(full, queryX, sort, "IGECA");
292: assertMatches(full, queryY, sort, "JHFDB");
293:
294: sort.setSort("int", true);
295: assertMatches(full, queryX, sort, "CAEGI");
296: assertMatches(full, queryY, sort, "BJFHD");
297:
298: sort.setSort("float", true);
299: assertMatches(full, queryX, sort, "AECIG");
300: assertMatches(full, queryY, sort, "BFJHD");
301:
302: sort.setSort("string", true);
303: assertMatches(full, queryX, sort, "CEGIA");
304: assertMatches(full, queryY, sort, "BFHJD");
305: }
306:
307: // test sorting when the sort field is empty (undefined) for some of the documents
308: public void testEmptyFieldSort() throws Exception {
309: sort.setSort("string");
310: assertMatches(full, queryF, sort, "ZJI");
311:
312: sort.setSort("string", true);
313: assertMatches(full, queryF, sort, "IJZ");
314:
315: sort.setSort(new SortField("i18n", Locale.ENGLISH));
316: assertMatches(full, queryF, sort, "ZJI");
317:
318: sort.setSort(new SortField("i18n", Locale.ENGLISH, true));
319: assertMatches(full, queryF, sort, "IJZ");
320:
321: sort.setSort("int");
322: assertMatches(full, queryF, sort, "IZJ");
323:
324: sort.setSort("int", true);
325: assertMatches(full, queryF, sort, "JZI");
326:
327: sort.setSort("float");
328: assertMatches(full, queryF, sort, "ZJI");
329:
330: // using a nonexisting field as first sort key shouldn't make a difference:
331: sort.setSort(new SortField[] {
332: new SortField("nosuchfield", SortField.STRING),
333: new SortField("float") });
334: assertMatches(full, queryF, sort, "ZJI");
335:
336: sort.setSort("float", true);
337: assertMatches(full, queryF, sort, "IJZ");
338:
339: // When a field is null for both documents, the next SortField should be used.
340: // Works for
341: sort.setSort(new SortField[] { new SortField("int"),
342: new SortField("string", SortField.STRING),
343: new SortField("float") });
344: assertMatches(full, queryG, sort, "ZWXY");
345:
346: // Reverse the last criterium to make sure the test didn't pass by chance
347: sort.setSort(new SortField[] { new SortField("int"),
348: new SortField("string", SortField.STRING),
349: new SortField("float", true) });
350: assertMatches(full, queryG, sort, "ZYXW");
351:
352: // Do the same for a MultiSearcher
353: Searcher multiSearcher = new MultiSearcher(
354: new Searchable[] { full });
355:
356: sort.setSort(new SortField[] { new SortField("int"),
357: new SortField("string", SortField.STRING),
358: new SortField("float") });
359: assertMatches(multiSearcher, queryG, sort, "ZWXY");
360:
361: sort.setSort(new SortField[] { new SortField("int"),
362: new SortField("string", SortField.STRING),
363: new SortField("float", true) });
364: assertMatches(multiSearcher, queryG, sort, "ZYXW");
365: // Don't close the multiSearcher. it would close the full searcher too!
366:
367: // Do the same for a ParallelMultiSearcher
368: Searcher parallelSearcher = new ParallelMultiSearcher(
369: new Searchable[] { full });
370:
371: sort.setSort(new SortField[] { new SortField("int"),
372: new SortField("string", SortField.STRING),
373: new SortField("float") });
374: assertMatches(parallelSearcher, queryG, sort, "ZWXY");
375:
376: sort.setSort(new SortField[] { new SortField("int"),
377: new SortField("string", SortField.STRING),
378: new SortField("float", true) });
379: assertMatches(parallelSearcher, queryG, sort, "ZYXW");
380: // Don't close the parallelSearcher. it would close the full searcher too!
381: }
382:
383: // test sorts using a series of fields
384: public void testSortCombos() throws Exception {
385: sort.setSort(new String[] { "int", "float" });
386: assertMatches(full, queryX, sort, "IGEAC");
387:
388: sort.setSort(new SortField[] { new SortField("int", true),
389: new SortField(null, SortField.DOC, true) });
390: assertMatches(full, queryX, sort, "CEAGI");
391:
392: sort.setSort(new String[] { "float", "string" });
393: assertMatches(full, queryX, sort, "GICEA");
394: }
395:
396: // test using a Locale for sorting strings
397: public void testLocaleSort() throws Exception {
398: sort.setSort(new SortField[] { new SortField("string",
399: Locale.US) });
400: assertMatches(full, queryX, sort, "AIGEC");
401: assertMatches(full, queryY, sort, "DJHFB");
402:
403: sort.setSort(new SortField[] { new SortField("string",
404: Locale.US, true) });
405: assertMatches(full, queryX, sort, "CEGIA");
406: assertMatches(full, queryY, sort, "BFHJD");
407: }
408:
409: // test using various international locales with accented characters
410: // (which sort differently depending on locale)
411: public void testInternationalSort() throws Exception {
412: sort.setSort(new SortField("i18n", Locale.US));
413: assertMatches(full, queryY, sort, "BFJDH");
414:
415: sort.setSort(new SortField("i18n", new Locale("sv", "se")));
416: assertMatches(full, queryY, sort, "BJDFH");
417:
418: sort.setSort(new SortField("i18n", new Locale("da", "dk")));
419: assertMatches(full, queryY, sort, "BJDHF");
420:
421: sort.setSort(new SortField("i18n", Locale.US));
422: assertMatches(full, queryX, sort, "ECAGI");
423:
424: sort.setSort(new SortField("i18n", Locale.FRANCE));
425: assertMatches(full, queryX, sort, "EACGI");
426: }
427:
428: // Test the MultiSearcher's ability to preserve locale-sensitive ordering
429: // by wrapping it around a single searcher
430: public void testInternationalMultiSearcherSort() throws Exception {
431: Searcher multiSearcher = new MultiSearcher(
432: new Searchable[] { full });
433:
434: sort.setSort(new SortField("i18n", new Locale("sv", "se")));
435: assertMatches(multiSearcher, queryY, sort, "BJDFH");
436:
437: sort.setSort(new SortField("i18n", Locale.US));
438: assertMatches(multiSearcher, queryY, sort, "BFJDH");
439:
440: sort.setSort(new SortField("i18n", new Locale("da", "dk")));
441: assertMatches(multiSearcher, queryY, sort, "BJDHF");
442: }
443:
444: // test a custom sort function
445: public void testCustomSorts() throws Exception {
446: sort.setSort(new SortField("custom", SampleComparable
447: .getComparatorSource()));
448: assertMatches(full, queryX, sort, "CAIEG");
449: sort.setSort(new SortField("custom", SampleComparable
450: .getComparatorSource(), true));
451: assertMatches(full, queryY, sort, "HJDBF");
452: SortComparator custom = SampleComparable.getComparator();
453: sort.setSort(new SortField("custom", custom));
454: assertMatches(full, queryX, sort, "CAIEG");
455: sort.setSort(new SortField("custom", custom, true));
456: assertMatches(full, queryY, sort, "HJDBF");
457: }
458:
459: // test a variety of sorts using more than one searcher
460: public void testMultiSort() throws Exception {
461: MultiSearcher searcher = new MultiSearcher(new Searchable[] {
462: searchX, searchY });
463: runMultiSorts(searcher);
464: }
465:
466: // test a variety of sorts using a parallel multisearcher
467: public void testParallelMultiSort() throws Exception {
468: Searcher searcher = new ParallelMultiSearcher(new Searchable[] {
469: searchX, searchY });
470: runMultiSorts(searcher);
471: }
472:
473: // test a variety of sorts using a remote searcher
474: public void testRemoteSort() throws Exception {
475: Searchable searcher = getRemote();
476: MultiSearcher multi = new MultiSearcher(
477: new Searchable[] { searcher });
478: runMultiSorts(multi);
479: }
480:
481: // test custom search when remote
482: public void testRemoteCustomSort() throws Exception {
483: Searchable searcher = getRemote();
484: MultiSearcher multi = new MultiSearcher(
485: new Searchable[] { searcher });
486: sort.setSort(new SortField("custom", SampleComparable
487: .getComparatorSource()));
488: assertMatches(multi, queryX, sort, "CAIEG");
489: sort.setSort(new SortField("custom", SampleComparable
490: .getComparatorSource(), true));
491: assertMatches(multi, queryY, sort, "HJDBF");
492: SortComparator custom = SampleComparable.getComparator();
493: sort.setSort(new SortField("custom", custom));
494: assertMatches(multi, queryX, sort, "CAIEG");
495: sort.setSort(new SortField("custom", custom, true));
496: assertMatches(multi, queryY, sort, "HJDBF");
497: }
498:
499: // test that the relevancy scores are the same even if
500: // hits are sorted
501: public void testNormalizedScores() throws Exception {
502:
503: // capture relevancy scores
504: HashMap scoresX = getScores(full.search(queryX));
505: HashMap scoresY = getScores(full.search(queryY));
506: HashMap scoresA = getScores(full.search(queryA));
507:
508: // we'll test searching locally, remote and multi
509: MultiSearcher remote = new MultiSearcher(
510: new Searchable[] { getRemote() });
511: MultiSearcher multi = new MultiSearcher(new Searchable[] {
512: searchX, searchY });
513:
514: // change sorting and make sure relevancy stays the same
515:
516: sort = new Sort();
517: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
518: assertSameValues(scoresX,
519: getScores(remote.search(queryX, sort)));
520: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
521: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
522: assertSameValues(scoresY,
523: getScores(remote.search(queryY, sort)));
524: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
525: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
526: assertSameValues(scoresA,
527: getScores(remote.search(queryA, sort)));
528: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
529:
530: sort.setSort(SortField.FIELD_DOC);
531: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
532: assertSameValues(scoresX,
533: getScores(remote.search(queryX, sort)));
534: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
535: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
536: assertSameValues(scoresY,
537: getScores(remote.search(queryY, sort)));
538: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
539: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
540: assertSameValues(scoresA,
541: getScores(remote.search(queryA, sort)));
542: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
543:
544: sort.setSort("int");
545: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
546: assertSameValues(scoresX,
547: getScores(remote.search(queryX, sort)));
548: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
549: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
550: assertSameValues(scoresY,
551: getScores(remote.search(queryY, sort)));
552: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
553: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
554: assertSameValues(scoresA,
555: getScores(remote.search(queryA, sort)));
556: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
557:
558: sort.setSort("float");
559: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
560: assertSameValues(scoresX,
561: getScores(remote.search(queryX, sort)));
562: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
563: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
564: assertSameValues(scoresY,
565: getScores(remote.search(queryY, sort)));
566: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
567: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
568: assertSameValues(scoresA,
569: getScores(remote.search(queryA, sort)));
570: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
571:
572: sort.setSort("string");
573: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
574: assertSameValues(scoresX,
575: getScores(remote.search(queryX, sort)));
576: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
577: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
578: assertSameValues(scoresY,
579: getScores(remote.search(queryY, sort)));
580: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
581: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
582: assertSameValues(scoresA,
583: getScores(remote.search(queryA, sort)));
584: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
585:
586: sort.setSort(new String[] { "int", "float" });
587: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
588: assertSameValues(scoresX,
589: getScores(remote.search(queryX, sort)));
590: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
591: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
592: assertSameValues(scoresY,
593: getScores(remote.search(queryY, sort)));
594: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
595: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
596: assertSameValues(scoresA,
597: getScores(remote.search(queryA, sort)));
598: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
599:
600: sort.setSort(new SortField[] { new SortField("int", true),
601: new SortField(null, SortField.DOC, true) });
602: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
603: assertSameValues(scoresX,
604: getScores(remote.search(queryX, sort)));
605: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
606: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
607: assertSameValues(scoresY,
608: getScores(remote.search(queryY, sort)));
609: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
610: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
611: assertSameValues(scoresA,
612: getScores(remote.search(queryA, sort)));
613: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
614:
615: sort.setSort(new String[] { "float", "string" });
616: assertSameValues(scoresX, getScores(full.search(queryX, sort)));
617: assertSameValues(scoresX,
618: getScores(remote.search(queryX, sort)));
619: assertSameValues(scoresX, getScores(multi.search(queryX, sort)));
620: assertSameValues(scoresY, getScores(full.search(queryY, sort)));
621: assertSameValues(scoresY,
622: getScores(remote.search(queryY, sort)));
623: assertSameValues(scoresY, getScores(multi.search(queryY, sort)));
624: assertSameValues(scoresA, getScores(full.search(queryA, sort)));
625: assertSameValues(scoresA,
626: getScores(remote.search(queryA, sort)));
627: assertSameValues(scoresA, getScores(multi.search(queryA, sort)));
628:
629: }
630:
631: public void testTopDocsScores() throws Exception {
632:
633: // There was previously a bug in FieldSortedHitQueue.maxscore when only a single
634: // doc was added. That is what the following tests for.
635: Sort sort = new Sort();
636: int nDocs = 10;
637:
638: // try to pick a query that will result in an unnormalized
639: // score greater than 1 to test for correct normalization
640: final TopDocs docs1 = full.search(queryE, null, nDocs, sort);
641:
642: // a filter that only allows through the first hit
643: Filter filt = new Filter() {
644: public BitSet bits(IndexReader reader) throws IOException {
645: BitSet bs = new BitSet(reader.maxDoc());
646: bs.set(docs1.scoreDocs[0].doc);
647: return bs;
648: }
649: };
650:
651: TopDocs docs2 = full.search(queryE, filt, nDocs, sort);
652:
653: assertEquals(docs1.scoreDocs[0].score,
654: docs2.scoreDocs[0].score, 1e-6);
655: }
656:
657: // runs a variety of sorts useful for multisearchers
658: private void runMultiSorts(Searcher multi) throws Exception {
659: sort.setSort(SortField.FIELD_DOC);
660: assertMatchesPattern(multi, queryA, sort,
661: "[AB]{2}[CD]{2}[EF]{2}[GH]{2}[IJ]{2}");
662:
663: sort.setSort(new SortField("int", SortField.INT));
664: assertMatchesPattern(multi, queryA, sort, "IDHFGJ[ABE]{3}C");
665:
666: sort.setSort(new SortField[] {
667: new SortField("int", SortField.INT),
668: SortField.FIELD_DOC });
669: assertMatchesPattern(multi, queryA, sort, "IDHFGJ[AB]{2}EC");
670:
671: sort.setSort("int");
672: assertMatchesPattern(multi, queryA, sort, "IDHFGJ[AB]{2}EC");
673:
674: sort.setSort(new SortField[] {
675: new SortField("float", SortField.FLOAT),
676: SortField.FIELD_DOC });
677: assertMatchesPattern(multi, queryA, sort, "GDHJ[CI]{2}EFAB");
678:
679: sort.setSort("float");
680: assertMatchesPattern(multi, queryA, sort, "GDHJ[CI]{2}EFAB");
681:
682: sort.setSort("string");
683: assertMatches(multi, queryA, sort, "DJAIHGFEBC");
684:
685: sort.setSort("int", true);
686: assertMatchesPattern(multi, queryA, sort, "C[AB]{2}EJGFHDI");
687:
688: sort.setSort("float", true);
689: assertMatchesPattern(multi, queryA, sort, "BAFE[IC]{2}JHDG");
690:
691: sort.setSort("string", true);
692: assertMatches(multi, queryA, sort, "CBEFGHIAJD");
693:
694: sort.setSort(new SortField[] { new SortField("string",
695: Locale.US) });
696: assertMatches(multi, queryA, sort, "DJAIHGFEBC");
697:
698: sort.setSort(new SortField[] { new SortField("string",
699: Locale.US, true) });
700: assertMatches(multi, queryA, sort, "CBEFGHIAJD");
701:
702: sort.setSort(new String[] { "int", "float" });
703: assertMatches(multi, queryA, sort, "IDHFGJEABC");
704:
705: sort.setSort(new String[] { "float", "string" });
706: assertMatches(multi, queryA, sort, "GDHJICEFAB");
707:
708: sort.setSort("int");
709: assertMatches(multi, queryF, sort, "IZJ");
710:
711: sort.setSort("int", true);
712: assertMatches(multi, queryF, sort, "JZI");
713:
714: sort.setSort("float");
715: assertMatches(multi, queryF, sort, "ZJI");
716:
717: sort.setSort("string");
718: assertMatches(multi, queryF, sort, "ZJI");
719:
720: sort.setSort("string", true);
721: assertMatches(multi, queryF, sort, "IJZ");
722: }
723:
724: // make sure the documents returned by the search match the expected list
725: private void assertMatches(Searcher searcher, Query query,
726: Sort sort, String expectedResult) throws IOException {
727: Hits result = searcher.search(query, sort);
728: StringBuffer buff = new StringBuffer(10);
729: int n = result.length();
730: for (int i = 0; i < n; ++i) {
731: Document doc = result.doc(i);
732: String[] v = doc.getValues("tracer");
733: for (int j = 0; j < v.length; ++j) {
734: buff.append(v[j]);
735: }
736: }
737: assertEquals(expectedResult, buff.toString());
738: }
739:
740: // make sure the documents returned by the search match the expected list pattern
741: private void assertMatchesPattern(Searcher searcher, Query query,
742: Sort sort, String pattern) throws IOException {
743: Hits result = searcher.search(query, sort);
744: StringBuffer buff = new StringBuffer(10);
745: int n = result.length();
746: for (int i = 0; i < n; ++i) {
747: Document doc = result.doc(i);
748: String[] v = doc.getValues("tracer");
749: for (int j = 0; j < v.length; ++j) {
750: buff.append(v[j]);
751: }
752: }
753: // System.out.println ("matching \""+buff+"\" against pattern \""+pattern+"\"");
754: assertTrue(Pattern.compile(pattern).matcher(buff.toString())
755: .matches());
756: }
757:
758: private HashMap getScores(Hits hits) throws IOException {
759: HashMap scoreMap = new HashMap();
760: int n = hits.length();
761: for (int i = 0; i < n; ++i) {
762: Document doc = hits.doc(i);
763: String[] v = doc.getValues("tracer");
764: assertEquals(v.length, 1);
765: scoreMap.put(v[0], new Float(hits.score(i)));
766: }
767: return scoreMap;
768: }
769:
770: // make sure all the values in the maps match
771: private void assertSameValues(HashMap m1, HashMap m2) {
772: int n = m1.size();
773: int m = m2.size();
774: assertEquals(n, m);
775: Iterator iter = m1.keySet().iterator();
776: while (iter.hasNext()) {
777: Object key = iter.next();
778: Object o1 = m1.get(key);
779: Object o2 = m2.get(key);
780: if (o1 instanceof Float) {
781: assertEquals(((Float) o1).floatValue(), ((Float) o2)
782: .floatValue(), 1e-6);
783: } else {
784: assertEquals(m1.get(key), m2.get(key));
785: }
786: }
787: }
788:
789: private Searchable getRemote() throws Exception {
790: try {
791: return lookupRemote();
792: } catch (Throwable e) {
793: startServer();
794: return lookupRemote();
795: }
796: }
797:
798: private Searchable lookupRemote() throws Exception {
799: return (Searchable) Naming
800: .lookup("//localhost/SortedSearchable");
801: }
802:
803: private void startServer() throws Exception {
804: // construct an index
805: Searcher local = getFullIndex();
806: // local.search (queryA, new Sort());
807:
808: // publish it
809: Registry reg = LocateRegistry.createRegistry(1099);
810: RemoteSearchable impl = new RemoteSearchable(local);
811: Naming.rebind("//localhost/SortedSearchable", impl);
812: }
813:
814: }
|