/*
* 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.
*/
using System;
using NUnit.Framework;
using StandardAnalyzer = Lucene.Net.Analysis.Standard.StandardAnalyzer;
using DateTools = Lucene.Net.Documents.DateTools;
using Document = Lucene.Net.Documents.Document;
using Field = Lucene.Net.Documents.Field;
using IndexReader = Lucene.Net.Index.IndexReader;
using IndexWriter = Lucene.Net.Index.IndexWriter;
using Term = Lucene.Net.Index.Term;
using Directory = Lucene.Net.Store.Directory;
using RAMDirectory = Lucene.Net.Store.RAMDirectory;
namespace Lucene.Net.Search
{
/// Unit test for sorting code.
///
///
/// Martin Seitz (T-Systems)
///
[Serializable]
[TestFixture]
public class TestCustomSearcherSort
{
private Directory Index
{
// create an index for testing
get
{
RAMDirectory indexStore = new RAMDirectory();
IndexWriter writer = new IndexWriter(indexStore, new StandardAnalyzer(), true);
RandomGen random = new RandomGen(this);
for (int i = 0; i < INDEX_SIZE; ++i)
{
// don't decrease; if to low the problem doesn't show up
Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document();
if ((i % 5) != 0)
{
// some documents must not have an entry in the first sort field
doc.Add(new Field("publicationDate_", random.GetLuceneDate(), Field.Store.YES, Field.Index.UN_TOKENIZED));
}
if ((i % 7) == 0)
{
// some documents to match the query (see below)
doc.Add(new Field("content", "test", Field.Store.YES, Field.Index.TOKENIZED));
}
// every document has a defined 'mandant' field
doc.Add(new Field("mandant", System.Convert.ToString(i % 3), Field.Store.YES, Field.Index.UN_TOKENIZED));
writer.AddDocument(doc);
}
writer.Optimize();
writer.Close();
return indexStore;
}
}
private Directory index = null;
private Query query = null;
// reduced from 20000 to 2000 to speed up test...
private const int INDEX_SIZE = 2000;
[STAThread]
public static void Main(System.String[] argv)
{
// TestRunner.run(Suite()); // {{Aroush}} how is this done in NUnit?
}
public static NUnit.Framework.TestCase Suite()
{
return null; // return new NUnit.Core.TestSuite(typeof(TestCustomSearcherSort)); {{Aroush}} how is this done in NUnit?
}
/// Create index and query for test cases.
[SetUp]
public virtual void SetUp()
{
index = Index;
query = new TermQuery(new Term("content", "test"));
}
/// Run the test using two CustomSearcher instances.
[Test]
public virtual void TestFieldSortCustomSearcher()
{
// log("Run testFieldSortCustomSearcher");
// define the sort criteria
Sort custSort = new Sort(new SortField[]{new SortField("publicationDate_"), SortField.FIELD_SCORE});
Searcher searcher = new CustomSearcher(this, index, 2);
// search and check hits
MatchHits(searcher, custSort);
}
/// Run the test using one CustomSearcher wrapped by a MultiSearcher.
[Test]
public virtual void TestFieldSortSingleSearcher()
{
// log("Run testFieldSortSingleSearcher");
// define the sort criteria
Sort custSort = new Sort(new SortField[]{new SortField("publicationDate_"), SortField.FIELD_SCORE});
Searcher searcher = new MultiSearcher(new Lucene.Net.Search.Searchable[]{new CustomSearcher(this, index, 2)});
// search and check hits
MatchHits(searcher, custSort);
}
/// Run the test using two CustomSearcher instances.
[Test]
public virtual void TestFieldSortMultiCustomSearcher()
{
// log("Run testFieldSortMultiCustomSearcher");
// define the sort criteria
Sort custSort = new Sort(new SortField[]{new SortField("publicationDate_"), SortField.FIELD_SCORE});
Searcher searcher = new MultiSearcher(new Lucene.Net.Search.Searchable[]{new CustomSearcher(this, index, 0), new CustomSearcher(this, index, 2)});
// search and check hits
MatchHits(searcher, custSort);
}
// make sure the documents returned by the search match the expected list
private void MatchHits(Searcher searcher, Sort sort)
{
// make a query without sorting first
Hits hitsByRank = searcher.Search(query);
CheckHits(hitsByRank, "Sort by rank: "); // check for duplicates
System.Collections.IDictionary resultMap = new System.Collections.SortedList();
// store hits in TreeMap - TreeMap does not allow duplicates; existing entries are silently overwritten
for (int hitid = 0; hitid < hitsByRank.Length(); ++hitid)
{
resultMap[(System.Int32) hitsByRank.Id(hitid)] = (System.Int32) hitid; // Value: Hits-Objekt Index
}
// now make a query using the sort criteria
Hits resultSort = searcher.Search(query, sort);
CheckHits(resultSort, "Sort by custom criteria: "); // check for duplicates
System.String lf = SupportClass.AppSettings.Get("line.separator", "\n");
// besides the sorting both sets of hits must be identical
for (int hitid = 0; hitid < resultSort.Length(); ++hitid)
{
System.Int32 idHitDate = (System.Int32) resultSort.Id(hitid); // document ID from sorted search
if (!resultMap.Contains(idHitDate))
{
Log("ID " + idHitDate + " not found. Possibliy a duplicate.");
}
Assert.IsTrue(resultMap.Contains(idHitDate)); // same ID must be in the Map from the rank-sorted search
// every hit must appear once in both result sets --> remove it from the Map.
// At the end the Map must be empty!
resultMap.Remove(idHitDate);
}
if (resultMap.Count == 0)
{
// log("All hits matched");
}
else
{
Log("Couldn't match " + resultMap.Count + " hits.");
}
Assert.AreEqual(resultMap.Count, 0);
}
/// Check the hits for duplicates.
///
///
private void CheckHits(Hits hits, System.String prefix)
{
if (hits != null)
{
System.Collections.IDictionary idMap = new System.Collections.SortedList();
for (int docnum = 0; docnum < hits.Length(); ++docnum)
{
System.Int32 luceneId;
try
{
luceneId = (System.Int32) hits.Id(docnum);
if (idMap.Contains(luceneId))
{
System.Text.StringBuilder message = new System.Text.StringBuilder(prefix);
message.Append("Duplicate key for hit index = ");
message.Append(docnum);
message.Append(", previous index = ");
message.Append(((System.Int32) idMap[luceneId]).ToString());
message.Append(", Lucene ID = ");
message.Append(luceneId);
Log(message.ToString());
}
else
{
idMap[luceneId] = (System.Int32) docnum;
}
}
catch (System.IO.IOException ioe)
{
System.Text.StringBuilder message = new System.Text.StringBuilder(prefix);
message.Append("Error occurred for hit index = ");
message.Append(docnum);
message.Append(" (");
message.Append(ioe.Message);
message.Append(")");
Log(message.ToString());
}
}
}
}
// Simply write to console - choosen to be independant of log4j etc
private void Log(System.String message)
{
System.Console.Out.WriteLine(message);
}
public class CustomSearcher : IndexSearcher
{
private void InitBlock(TestCustomSearcherSort enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
}
private TestCustomSearcherSort enclosingInstance;
public TestCustomSearcherSort Enclosing_Instance
{
get
{
return enclosingInstance;
}
}
private int switcher;
///
///
/// IOException
public CustomSearcher(TestCustomSearcherSort enclosingInstance, Directory directory, int switcher):base(directory)
{
InitBlock(enclosingInstance);
this.switcher = switcher;
}
///
///
public CustomSearcher(TestCustomSearcherSort enclosingInstance, IndexReader r, int switcher):base(r)
{
InitBlock(enclosingInstance);
this.switcher = switcher;
}
///
///
/// IOException
public CustomSearcher(TestCustomSearcherSort enclosingInstance, System.String path, int switcher):base(path)
{
InitBlock(enclosingInstance);
this.switcher = switcher;
}
/* (non-Javadoc)
* @see Lucene.Net.search.Searchable#search(Lucene.Net.search.Query, Lucene.Net.search.Filter, int, Lucene.Net.search.Sort)
*/
public override TopFieldDocs Search(Query query, Filter filter, int nDocs, Sort sort)
{
BooleanQuery bq = new BooleanQuery();
bq.Add(query, BooleanClause.Occur.MUST);
bq.Add(new TermQuery(new Term("mandant", System.Convert.ToString(switcher))), BooleanClause.Occur.MUST);
return base.Search(bq, filter, nDocs, sort);
}
/* (non-Javadoc)
* @see Lucene.Net.search.Searchable#search(Lucene.Net.search.Query, Lucene.Net.search.Filter, int)
*/
public override TopDocs Search(Query query, Filter filter, int nDocs)
{
BooleanQuery bq = new BooleanQuery();
bq.Add(query, BooleanClause.Occur.MUST);
bq.Add(new TermQuery(new Term("mandant", System.Convert.ToString(switcher))), BooleanClause.Occur.MUST);
return base.Search(bq, filter, nDocs);
}
}
private class RandomGen
{
public RandomGen(TestCustomSearcherSort enclosingInstance)
{
InitBlock(enclosingInstance);
}
private void InitBlock(TestCustomSearcherSort enclosingInstance)
{
this.enclosingInstance = enclosingInstance;
System.DateTime temp_calendar;
temp_calendar = new System.DateTime(1980, 1, 1, 0, 0, 0, 0, new System.Globalization.GregorianCalendar());
base_Renamed = temp_calendar;
}
private TestCustomSearcherSort enclosingInstance;
public TestCustomSearcherSort Enclosing_Instance
{
// Just to generate some different Lucene Date strings
get
{
return enclosingInstance;
}
}
private System.Random random = new System.Random((System.Int32) 0); // to generate some arbitrary contents
// private System.Globalization.Calendar base_Renamed;
private System.DateTime base_Renamed;
// Just to generate some different Lucene Date strings
public System.String GetLuceneDate()
{
long v1 = base_Renamed.Millisecond;
long v2 = random.Next();
long v3 = System.Int32.MinValue;
return DateTools.TimeToString(v1 + v2 - v3, DateTools.Resolution.DAY);
}
}
}
}