blob: c6bae0f591540430c8e9dca6e0c9ca32c4982931 [file]
/*------------------------------------------------------------------------------
* Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
*
* Distributable under the terms of either the Apache License (Version 2.0) or
* the GNU Lesser General Public License, as specified in the COPYING file.
------------------------------------------------------------------------------*/
#include "test.h"
#include "CLucene/search/MultiPhraseQuery.h"
#include "QueryUtils.h"
/// Java PrefixQuery test, 2009-06-02
void testPrefixQuery(CuTest *tc){
WhitespaceAnalyzer<TCHAR> analyzer;
RAMDirectory directory;
const TCHAR* categories[] = {_T("/Computers"), _T("/Computers/Mac"), _T("/Computers/Windows")};
IndexWriter writer( &directory, &analyzer, true);
for (int i = 0; i < 3; i++) {
Document *doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("category"), categories[i], Field::STORE_YES | Field::INDEX_UNTOKENIZED));
writer.addDocument(doc);
_CLDELETE(doc);
}
writer.close();
Term* t = _CLNEW Term(_T("category"), _T("/Computers"));
PrefixQuery *query = _CLNEW PrefixQuery(t);
IndexSearcher searcher(&directory);
Hits *hits = searcher.search(query);
CLUCENE_ASSERT(3 == hits->length()); // All documents in /Computers category and below
_CLDELETE(query);
_CLDELETE(t);
_CLDELETE(hits);
t = _CLNEW Term(_T("category"), _T("/Computers/Mac"));
query = _CLNEW PrefixQuery(t);
hits = searcher.search(query);
CLUCENE_ASSERT(1 == hits->length()); // One in /Computers/Mac
_CLDELETE(query);
_CLDELETE(t);
_CLDELETE(hits);
}
#ifndef NO_FUZZY_QUERY
/// Java FuzzyQuery test, 2009-06-02
class TestFuzzyQuery {
private:
CuTest *tc;
void addDoc(const TCHAR* text, IndexWriter* writer) {
Document* doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("field"), text, Field::STORE_YES | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLLDELETE(doc);
}
Hits* searchQuery(IndexSearcher* searcher, const TCHAR* field, const TCHAR* text,
float_t minSimilarity=FuzzyQuery::defaultMinSimilarity, size_t prefixLen=0){
Term* t = _CLNEW Term(field, text);
FuzzyQuery* query = _CLNEW FuzzyQuery(t, minSimilarity, prefixLen);
Hits* hits = searcher->search(query);
_CLLDELETE(query);
_CLLDECDELETE(t);
return hits;
}
size_t getHitsLength(IndexSearcher* searcher, const TCHAR* field, const TCHAR* text,
float_t minSimilarity=FuzzyQuery::defaultMinSimilarity, size_t prefixLen=0){
Hits* hits = searchQuery(searcher, field, text, minSimilarity, prefixLen);
size_t ret = hits->length();
_CLLDELETE(hits);
return ret;
}
public:
TestFuzzyQuery(CuTest *_tc):tc(_tc){
}
~TestFuzzyQuery(){
}
void testFuzziness() {
RAMDirectory directory;
WhitespaceAnalyzer<TCHAR> a;
IndexWriter writer(&directory, &a, true);
addDoc(_T("aaaaa"), &writer);
addDoc(_T("aaaab"), &writer);
addDoc(_T("aaabb"), &writer);
addDoc(_T("aabbb"), &writer);
addDoc(_T("abbbb"), &writer);
addDoc(_T("bbbbb"), &writer);
addDoc(_T("ddddd"), &writer);
writer.optimize();
writer.close();
IndexSearcher searcher(&directory);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa")) == 3);
// same with prefix
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,1) == 3);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,2) == 3);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,3) == 3);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,4) == 2);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,5) == 1);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaaaa"),FuzzyQuery::defaultMinSimilarity,6) == 1);
// not similar enough:
CuAssertTrue(tc, getHitsLength(&searcher, _T("field"), _T("xxxxx")) == 0);
CuAssertTrue(tc, getHitsLength(&searcher, _T("field"), _T("aaccc")) == 0); // edit distance to "aaaaa" = 3
// query identical to a word in the index:
Hits* hits = searchQuery(&searcher, _T("field"), _T("aaaaa"));
CLUCENE_ASSERT( hits->length() == 3);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
// default allows for up to two edits:
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaabb"), hits->doc(2).get(_T("field")));
_CLLDELETE(hits);
// query similar to a word in the index:
hits = searchQuery(&searcher, _T("field"), _T("aaaac"));
CLUCENE_ASSERT( hits->length() == 3);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaabb"), hits->doc(2).get(_T("field")));
_CLLDELETE(hits);
// now with prefix
hits = searchQuery(&searcher, _T("field"), _T("aaaac"), FuzzyQuery::defaultMinSimilarity, 1);
CLUCENE_ASSERT( hits->length() == 3);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaabb"), hits->doc(2).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaac"), FuzzyQuery::defaultMinSimilarity, 2);
CLUCENE_ASSERT( hits->length() == 3);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaabb"), hits->doc(2).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaac"), FuzzyQuery::defaultMinSimilarity, 3);
CLUCENE_ASSERT( hits->length() == 3);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaabb"), hits->doc(2).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaac"), FuzzyQuery::defaultMinSimilarity, 4);
CLUCENE_ASSERT( hits->length() == 2);
CuAssertStrEquals(tc, NULL, _T("aaaaa"), hits->doc(0).get(_T("field")));
CuAssertStrEquals(tc, NULL, _T("aaaab"), hits->doc(1).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaac"), FuzzyQuery::defaultMinSimilarity, 5);
CLUCENE_ASSERT( hits->length() == 0);
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("ddddX"));
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("ddddd"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
// now with prefix
hits = searchQuery(&searcher, _T("field"), _T("ddddX"), FuzzyQuery::defaultMinSimilarity, 1);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("ddddd"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("ddddX"), FuzzyQuery::defaultMinSimilarity, 2);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("ddddd"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("ddddX"), FuzzyQuery::defaultMinSimilarity, 3);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("ddddd"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("ddddX"), FuzzyQuery::defaultMinSimilarity, 4);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("ddddd"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("ddddX"), FuzzyQuery::defaultMinSimilarity, 5);
CLUCENE_ASSERT( hits->length() == 0);
_CLLDELETE(hits);
// different field = no match:
hits = searchQuery(&searcher, _T("anotherfield"), _T("ddddX"));
CLUCENE_ASSERT( hits->length() == 0);
_CLLDELETE(hits);
searcher.close();
directory.close();
}
void testFuzzinessLong() {
RAMDirectory directory;
WhitespaceAnalyzer<TCHAR> a;
IndexWriter writer(&directory, &a, true);
addDoc(_T("aaaaaaa"), &writer);
addDoc(_T("segment"), &writer);
writer.optimize();
writer.close();
IndexSearcher searcher(&directory);
// not similar enough:
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("xxxxx")) == 0);
// edit distance to "aaaaaaa" = 3, this matches because the string is longer than
// in testDefaultFuzziness so a bigger difference is allowed:
Hits* hits = searchQuery(&searcher, _T("field"), _T("aaaaccc"));
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("aaaaaaa"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
// now with prefix
hits = searchQuery(&searcher, _T("field"), _T("aaaaccc"), FuzzyQuery::defaultMinSimilarity, 1);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("aaaaaaa"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaaccc"), FuzzyQuery::defaultMinSimilarity, 4);
CLUCENE_ASSERT( hits->length() == 1);
CuAssertStrEquals(tc, NULL, _T("aaaaaaa"), hits->doc(0).get(_T("field")));
_CLLDELETE(hits);
hits = searchQuery(&searcher, _T("field"), _T("aaaaccc"), FuzzyQuery::defaultMinSimilarity, 5);
CLUCENE_ASSERT( hits->length() == 0);
_CLLDELETE(hits);
// no match, more than half of the characters is wrong:
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaacccc")) == 0);
// now with prefix
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("aaacccc"), FuzzyQuery::defaultMinSimilarity, 2) == 0);
// "student" and "stellent" are indeed similar to "segment" by default:
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("student")) == 1);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("stellent")) == 1);
// now with prefix
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("student"), FuzzyQuery::defaultMinSimilarity, 1) == 1);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("stellent"), FuzzyQuery::defaultMinSimilarity, 1) == 1);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("student"), FuzzyQuery::defaultMinSimilarity, 2) == 0);
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("stellent"), FuzzyQuery::defaultMinSimilarity, 2) == 0);
// "student" doesn't match anymore thanks to increased minimum similarity:
CLUCENE_ASSERT( getHitsLength(&searcher, _T("field"), _T("student"), 0.6f, 0) == 0);
try {
new FuzzyQuery(_CLNEW Term(_T("field"), _T("student")), 1.1f);
CuFail(tc, _T("Expected IllegalArgumentException"));
} catch (CLuceneError& /*e*/) {
// expecting exception
}
try {
new FuzzyQuery(_CLNEW Term(_T("field"), _T("student")), -0.1f);
CuFail(tc, _T("Expected IllegalArgumentException"));
} catch (CLuceneError& /*e*/) {
// expecting exception
}
searcher.close();
directory.close();
}
};
void testFuzzyQuery(CuTest *tc){
/// Run Java Lucene tests
TestFuzzyQuery tester(tc);
tester.testFuzziness();
/// Legacy CLucene tests
RAMDirectory ram;
//---
WhitespaceAnalyzer<TCHAR> an;
IndexWriter* writer = _CLNEW IndexWriter(&ram, &an, true);
//---
Document *doc = 0;
//****
doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("body"),_T("test"),Field::STORE_NO | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLDELETE(doc);
//****
doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("body"),_T("home"),Field::STORE_NO | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLDELETE(doc);
//****
doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("body"), _T("pc linux"),Field::STORE_NO | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLDELETE(doc);
//****
doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("body"), _T("tested"),Field::STORE_NO | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLDELETE(doc);
//****
doc = _CLNEW Document();
doc->add(*_CLNEW Field(_T("body"), _T("source"),Field::STORE_NO | Field::INDEX_TOKENIZED));
writer->addDocument(doc);
_CLDELETE(doc);
//---
writer->close();
_CLDELETE(writer);
//---
IndexSearcher searcher (&ram);
//---
Term* term = _CLNEW Term(_T("body"), _T("test~"));
Query* query = _CLNEW FuzzyQuery(term);
Hits* result = searcher.search(query);
CLUCENE_ASSERT(result && result->length() > 0);
//---
_CLDELETE(result);
_CLDELETE(query);
_CLDECDELETE(term);
searcher.close();
ram.close();
}
#else
void _NO_FUZZY_QUERY(CuTest *tc){
CuNotImpl(tc,_T("Fuzzy"));
}
#endif
void testMultiPhraseQuery( CuTest * tc )
{
MultiPhraseQuery * pQuery = _CLNEW MultiPhraseQuery();
Term * t1 = _CLNEW Term( _T( "field" ), _T( "t1" ));
Term * t2 = _CLNEW Term( _T( "field" ), _T( "t2" ));
Term * t3 = _CLNEW Term( _T( "field" ), _T( "t3" ));
Term * t4 = _CLNEW Term( _T( "field" ), _T( "t4" ));
CL_NS(util)::ValueArray<CL_NS(index)::Term*> terms( 3 );
terms[ 0 ] = t2;
terms[ 1 ] = t3;
terms[ 2 ] = t4;
pQuery->add( t1 );
pQuery->add( &terms );
Query * pClone = pQuery->clone();
QueryUtils::checkEqual( tc, pQuery, pClone );
_CLLDECDELETE( t1 );
_CLLDECDELETE( t2 );
_CLLDECDELETE( t3 );
_CLLDECDELETE( t4 );
_CLLDELETE( pQuery );
_CLLDELETE( pClone );
}
CuSuite *testqueries(void)
{
CuSuite *suite = CuSuiteNew(_T("CLucene Queries Test"));
SUITE_ADD_TEST(suite, testPrefixQuery);
SUITE_ADD_TEST(suite, testMultiPhraseQuery);
#ifndef NO_FUZZY_QUERY
SUITE_ADD_TEST(suite, testFuzzyQuery);
#else
SUITE_ADD_TEST(suite, _NO_FUZZY_QUERY);
#endif
return suite;
}
//EOF