blob: 36b5aa90fb6bf7888756c2ed300c550cb2034fc8 [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.
------------------------------------------------------------------------------*/
#define ANKERL_NANOBENCH_IMPLEMENT
#include "test.h"
#include "CLucene/document/FieldSelector.h"
#include "CLucene/index/SDocumentWriter.h"
#include <nanobench.h>
#include <memory>
//an in memory input stream for testing binary data
class MemReader : public CL_NS(util)::Reader {
signed char *value;
size_t len;
int64_t pos;
public:
MemReader(const char *value, const int32_t length = -1) {
if (length >= 0)
this->len = length;
else
this->len = strlen(value);
this->pos = 0;
this->value = _CL_NEWARRAY(signed char, this->len);
memcpy(this->value, value, this->len);
}
virtual ~MemReader() {
_CLDELETE_ARRAY(this->value);
}
int32_t read(const signed char *&start, int32_t min, int32_t max) {
start = this->value + pos;
int32_t r = max > min ? max : min;
if (len - pos < r)
r = len - pos;
pos += r;
return r;
}
int64_t position() {
return pos;
}
int64_t skip(int64_t ntoskip) {
int64_t s = ntoskip;
if (len - pos < s)
s = len - pos;
this->pos += s;
return s;
}
size_t size() {
return len;
}
};
void TestReaderValueField(CuTest *tc) {
RAMDirectory dir;
SimpleAnalyzer<char> analyzer;
IndexWriter w(&dir, &analyzer, true);
w.setUseCompoundFile(false);
auto field_name = lucene::util::Misc::_charToWide("f3");
Document doc;
auto field = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
auto char_string_reader = std::make_unique<lucene::util::SStringReader<char>>();
char_string_reader->init("value1", 6, true);
auto stream = analyzer.tokenStream(field->name(), char_string_reader.get());
field->setValue(stream);
doc.add(*field);
w.addDocument(&doc);
w.close();
IndexSearcher searcher(&dir);
auto t1 = std::make_unique<Term>(_T("f3"), _T("value1"));
auto query1 =std::make_unique<TermQuery>(t1.get());
Hits *hits1 = searcher.search(query1.get());
CLUCENE_ASSERT(1 == hits1->length());
_CLDELETE(stream)
_CLDELETE(hits1)
_CLDELETE_ARRAY(field_name)
}
void TestMultiSetValueField(CuTest *tc) {
RAMDirectory dir;
SimpleAnalyzer<char> analyzer;
IndexWriter w(&dir, &analyzer, true);
w.setUseCompoundFile(false);
auto field_name = lucene::util::Misc::_charToWide("f3");
Document doc;
auto field = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
auto char_string_reader = std::make_unique<lucene::util::SStringReader<char>>();
char_string_reader->init("value1", 6, false);
auto stream = analyzer.tokenStream(field->name(), char_string_reader.get());
field->setValue(stream);
char_string_reader->init("value2", 6, false);
auto stream2 = analyzer.tokenStream(field->name(), char_string_reader.get());
field->setValue(stream2);
doc.add(*field);
w.addDocument(&doc);
w.close();
IndexSearcher searcher(&dir);
auto t1 = std::make_unique<Term>(_T("f3"), _T("value1"));
auto query1 = std::make_unique<TermQuery>(t1.get());
Hits *hits1 = searcher.search(query1.get());
CLUCENE_ASSERT(0 == hits1->length());
auto t2 = std::make_unique<Term>(_T("f3"), _T("value2"));
auto query2 = std::make_unique<TermQuery>(t2.get());
Hits *hits2 = searcher.search(query2.get());
CLUCENE_ASSERT(1 == hits2->length());
doc.clear();
_CLDELETE(stream)
_CLDELETE(stream2)
_CLDELETE(hits1)
_CLDELETE(hits2)
_CLDELETE_ARRAY(field_name)
}
void TestMultiAddValueField(CuTest *tc) {
RAMDirectory dir;
auto field_name = lucene::util::Misc::_charToWide("f3");
SimpleAnalyzer<char> analyzer;
IndexWriter w(&dir, &analyzer, true);
w.setUseCompoundFile(false);
Document doc;
auto field1 = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
auto char_string_reader = std::make_unique<lucene::util::SStringReader<char>>();
char_string_reader->init("value1", 6, false);
auto stream = analyzer.tokenStream(field1->name(), char_string_reader.get());
field1->setValue(stream);
doc.add(*field1);
auto field2 = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
auto char_string_reader2 = std::make_unique<lucene::util::SStringReader<char>>();
char_string_reader2->init("value2", 6, false);
auto stream2 = analyzer.tokenStream(field2->name(), char_string_reader2.get());
field2->setValue(stream2);
doc.add(*field2);
try {
w.addDocument(&doc);
w.close();
} catch (CLuceneError& ae) {
std::cout <<ae.what() << std::endl;
throw ae;
}
auto t1 = std::make_unique<Term>(_T("f3"), _T("value1"));
auto query1 = std::make_unique<TermQuery>(t1.get());
IndexSearcher searcher(&dir);
Hits *hits1 = searcher.search(query1.get());
CLUCENE_ASSERT(1 == hits1->length());
auto t2 = std::make_unique<Term>(_T("f3"), _T("value2"));
auto query2 = std::make_unique<TermQuery>(t2.get());
Hits *hits2 = searcher.search(query2.get());
CLUCENE_ASSERT(1 == hits2->length());
doc.removeFields(_T("f3"));
CLUCENE_ASSERT(doc.getFields()->size() == 0);
_CLDELETE(hits1);
_CLDELETE(hits2);
_CLDELETE(stream);
_CLDELETE(stream2);
_CLDELETE_ARRAY(field_name)
}
void TestFields(CuTest *tc) {
Field *f = _CLNEW Field(_T("test"), _T("value"), Field::INDEX_TOKENIZED);
CLUCENE_ASSERT(f->isIndexed() && f->isTokenized());
CLUCENE_ASSERT(!f->isStored() && !f->isBinary() && !f->getOmitNorms());
_CLDELETE(f);
f = _CLNEW Field(_T("test"), _T("value"), Field::STORE_YES | Field::INDEX_NONORMS);
//CLUCENE_ASSERT(f->isIndexed());
//CLUCENE_ASSERT(!f->isTokenized());
CLUCENE_ASSERT(f->getOmitNorms());
CLUCENE_ASSERT(f->isStored() && !f->isBinary());
_CLDELETE(f);
Document doc;
doc.add(*_CLNEW Field(_T("f1"), _T("value"), Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f2"), _T("value"), Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f3"), _T("value1"), Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f3"), _T("value2"), Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f4"), _T("value"), Field::INDEX_TOKENIZED));
CLUCENE_ASSERT(doc.getFields()->size() == 5);
_CLLDELETE(doc.fields());//just fetch the fields (to test deprecated loads)
doc.removeField(_T("f3"));
CLUCENE_ASSERT(doc.getFields()->size() == 4);
//test deprecated enumerator
DocumentFieldEnumeration *e = doc.fields();
int count = 0;
while (e->hasMoreElements()) {
Field *field4 = e->nextElement();
CLUCENE_ASSERT(field4 != NULL);
count++;
}
CLUCENE_ASSERT(count == 4);
_CLDELETE(e);
doc.add(*_CLNEW Field(_T("f3"), _T("value3"), Field::INDEX_TOKENIZED));
CLUCENE_ASSERT(doc.getFields()->size() == 5);
//test deprecated enumerator
e = doc.fields();
count = 0;
while (e->hasMoreElements()) {
Field *field5 = e->nextElement();
CLUCENE_ASSERT(field5 != NULL);
count++;
}
CLUCENE_ASSERT(count == 5);
_CLDELETE(e);
doc.removeFields(_T("f3"));
CLUCENE_ASSERT(doc.getFields()->size() == 3);
doc.removeFields(_T("f4"));
CLUCENE_ASSERT(doc.getFields()->size() == 2);
doc.removeFields(_T("f1"));
CLUCENE_ASSERT(doc.getFields()->size() == 1);
doc.removeFields(_T("f2"));
CLUCENE_ASSERT(doc.getFields()->size() == 0);
}
/*
void TestDateTools(CuTest *tc) {
TCHAR* t = CL_NS(document)::DateTools::timeToString( Misc::currentTimeMillis() , CL_NS(document)::DateTools::MILLISECOND_FORMAT);
_CLDELETE_ARRAY(t);
TCHAR buf[30];
const TCHAR* xpt = _T("19700112102054321");
int64_t vv = (int64_t)987654321;
CL_NS(document)::DateTools::timeToString( vv , CL_NS(document)::DateTools::MILLISECOND_FORMAT, buf, 30);
if ( _tcscmp(buf,xpt) != 0 ) {
CuFail(tc, _T("timeToString failed\n"), buf, xpt);
}
_CLDELETE_ARRAY(t);
}
*/
void TestFieldSelectors(CuTest *tc) {
RAMDirectory dir;
const TCHAR *longStrValue = _T("too long a field...");
{
WhitespaceAnalyzer<TCHAR> a;
IndexWriter w(&dir, &a, true);
for (int i = 0; i < 3; i++) {
Document doc;
doc.add(*_CLNEW Field(_T("f1"), _T("value1"), Field::STORE_NO | Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f2"), _T("value2"), Field::STORE_NO | Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f3"), _T("value3"), Field::STORE_NO | Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f4"), _T("value4"), Field::STORE_NO | Field::INDEX_TOKENIZED));
doc.add(*_CLNEW Field(_T("f5"), longStrValue, Field::STORE_NO | Field::INDEX_TOKENIZED));
w.addDocument(&doc);
}
w.flush();
}
IndexReader *reader = IndexReader::open(&dir);
MapFieldSelector fieldsToLoad;
fieldsToLoad.add(_T("f2"), FieldSelector::LOAD);
fieldsToLoad.add(_T("f3"), FieldSelector::LAZY_LOAD);
fieldsToLoad.add(_T("f5"), FieldSelector::SIZE);
Document doc;
CLUCENE_ASSERT(reader->document(0, doc, &fieldsToLoad));
CLUCENE_ASSERT(doc.getFields()->size() == 3);
CuAssertStrEquals(tc, _T("check f2"), _T("value2"), doc.get(_T("f2")));
CuAssertStrEquals(tc, _T("check f3"), _T("value3"), doc.get(_T("f3")));
Field *byteField = doc.getField(_T("f5"));
const ValueArray<uint8_t> &bytes = *byteField->binaryValue();
uint32_t shouldBeInt = 2 * _tcslen(longStrValue);
ValueArray<uint8_t> shouldBe(4);
shouldBe[0] = (uint8_t) (shouldBeInt >> 24);
shouldBe[1] = (uint8_t) (shouldBeInt >> 16);
shouldBe[2] = (uint8_t) (shouldBeInt >> 8);
shouldBe[3] = (uint8_t) shouldBeInt;
CLUCENE_ASSERT(byteField != NULL);
CLUCENE_ASSERT(memcmp(shouldBe.values, bytes.values, 4) == 0);
_CLDELETE(reader);
_CL_LDECREF(&dir);//derefence since we are on the stack...
}
void _TestDocumentWithOptions(CuTest *tc, int storeBit, FieldSelector::FieldSelectorResult fieldSelectorBit) {
char factbook[1024];
strcpy(factbook, clucene_data_location);
strcat(factbook, "/reuters-21578/feldman-cia-worldfactbook-data.txt");
CuAssert(tc, _T("Factbook file does not exist"), Misc::dir_Exists(factbook));
Document doc;
Field *f;
const TCHAR *_ts, *_ts2;
const ValueArray<uint8_t> *strm;
RAMDirectory ram;
const char *areaderString = "a binary field";
const TCHAR *treaderString = _T("a string reader field");
size_t readerStringLen = strlen(areaderString);
SimpleAnalyzer<TCHAR> an;
IndexWriter writer(&ram, &an, true);//no analyzer needed since we are not indexing...
ValueArray<uint8_t> b((uint8_t *) strdup(areaderString), strlen(areaderString));
//use binary utf8
doc.add(*_CLNEW Field(_T("binaryField"), &b,
Field::TERMVECTOR_NO | storeBit | Field::INDEX_NO, true));
writer.addDocument(&doc);
doc.clear();
//use reader
doc.add(*_CLNEW Field(_T("readerField"), _CLNEW StringReader(treaderString),
Field::TERMVECTOR_NO | storeBit | Field::INDEX_NO));
writer.addDocument(&doc);
doc.clear();
//done adding a few documents, now try and add a few more...
//writer.optimize();
//use big file
doc.add(*_CLNEW Field(_T("fileField"),
_CLNEW FileReader(factbook, SimpleInputStreamReader::ASCII),
Field::TERMVECTOR_NO | storeBit | Field::INDEX_NO));
writer.addDocument(&doc);
doc.clear();
//another optimise...
//writer.optimize();
writer.close();
IndexReader *reader = IndexReader::open(&ram);
MapFieldSelector fieldsToLoad;
fieldsToLoad.add(_T("fileField"), fieldSelectorBit);
fieldsToLoad.add(_T("readerField"), fieldSelectorBit);
fieldsToLoad.add(_T("binaryField"), fieldSelectorBit);
//now check binary field
reader->document(0, doc);
f = doc.getField(_T("binaryField"));
strm = f->binaryValue();
CuAssertIntEquals(tc, _T("Check binary length is correct"), readerStringLen, b.length);
for (size_t i = 0; i < readerStringLen; i++) {
CuAssertIntEquals(tc, _T("Check binary values are the same"), (*strm)[i], areaderString[i]);
}
doc.clear();
//and check reader stream
reader->document(1, doc);
f = doc.getField(_T("readerField"));
_ts = f->stringValue();
CuAssertStrEquals(tc, _T("Check readerField length is correct"), treaderString, _ts);
doc.clear();
//now check the large field field
reader->document(2, doc);
f = doc.getField(_T("fileField"));
_ts = f->stringValue();
FileReader fbStream(factbook, FileReader::ASCII);
int i = 0;
_ts2 = NULL;
do {
int32_t rd = fbStream.read((const void**)&_ts2, 1, 1);
if (rd == -1)
break;
CLUCENE_ASSERT(rd == 1);
CLUCENE_ASSERT(_ts[i] == *_ts2);
i++;
} while (true);
CLUCENE_ASSERT(i == _tcslen(_ts));
doc.clear();
reader->close();
_CLDELETE(reader);
_CL_LDECREF(&ram);//this is in the stack...
}
void TestBinaryDocument(CuTest *tc) {
_TestDocumentWithOptions(tc, Field::STORE_YES, FieldSelector::LOAD);
}
void TestCompressedDocument(CuTest *tc) {
_TestDocumentWithOptions(tc, Field::STORE_COMPRESS, FieldSelector::LOAD);
}
void TestLazyBinaryDocument(CuTest *tc) {
_TestDocumentWithOptions(tc, Field::STORE_YES, FieldSelector::LAZY_LOAD);
}
void TestLazyCompressedDocument(CuTest *tc) {
_TestDocumentWithOptions(tc, Field::STORE_COMPRESS, FieldSelector::LAZY_LOAD);
}
void TestSetFieldBench(CuTest *tc) {
ankerl::nanobench::Bench().epochIterations(1).run("TestSetFieldBench", [&] {
RAMDirectory dir;
SimpleAnalyzer<TCHAR> analyzer;
IndexWriter w(&dir, &analyzer, true);
Document doc;
auto field_name = lucene::util::Misc::_charToWide("f3");
auto field = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
doc.add(*field);
for (int i = 0; i < 10000; ++i) {
auto value1 = lucene::util::Misc::_charToWide("value1");
field->setValue(value1, false);
w.addDocument(&doc);
}
w.close();
doc.clear();
});
}
string generateRandomString(int length) {
static const char alphanum[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
string randomString;
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(0, sizeof(alphanum) - 2);
for (int i = 0; i < length; ++i) {
randomString += alphanum[dis(gen)];
}
return randomString;
}
const int32_t MAX_FIELD_LEN = 0x7FFFFFFFL;
const int32_t MAX_BUFFER_DOCS = 100000000;
const int32_t MERGE_FACTOR = 100000000;
void TestAddDocument(CuTest *tc) {
try {
RAMDirectory dir;
SimpleAnalyzer<char> sanalyzer;
IndexWriter w(&dir, &sanalyzer, true);
w.setUseCompoundFile(false);
w.setMaxBufferedDocs(MAX_BUFFER_DOCS);
w.setRAMBufferSizeMB(256);
w.setMaxFieldLength(MAX_FIELD_LEN);
w.setMergeFactor(MERGE_FACTOR);
w.setDocumentWriter(_CLNEW SDocumentsWriter<char>(w.getDirectory(), &w));
Document doc;
auto field_name = lucene::util::Misc::_charToWide("f3");
auto field = _CLNEW Field(field_name, Field::INDEX_TOKENIZED | Field::STORE_NO);
doc.add(*field);
for (int i = 0; i <= 2000000; i++) {
std::string value1 = "value1";
if (i > 0) value1 = generateRandomString(2000);
auto stringReader = _CLNEW lucene::util::SStringReader<char>(
value1.c_str(), strlen(value1.c_str()), false);
auto stream = sanalyzer.reusableTokenStream(field_name, stringReader);
field->setValue(stream);
w.addDocument(&doc, &sanalyzer);
}
IndexSearcher searcher(&dir);
Term* t2 = _CLNEW Term(_T("f3"), _T("value1"));
auto* query2 = _CLNEW TermQuery(t2);
Hits* hits2 = searcher.search(query2);
CLUCENE_ASSERT(1 == hits2->length());
doc.clear();
w.close();
} catch (CLuceneError& ae) {
std::cout <<ae.what() << std::endl;
throw ae;
}
}
void TestNewFieldBench(CuTest *tc) {
ankerl::nanobench::Bench().epochIterations(1).run("TestNewFieldBench", [&] {
RAMDirectory dir;
//std::string value = "value1";
//std::string field_name = "f3";
//auto field_name_w = std::wstring(field_name.begin(), field_name.end());
auto field_name = lucene::util::Misc::_charToWide("f3");
//auto field_name_w = lucene::util::Misc::_charToWide(field_name.c_str());
SimpleAnalyzer<TCHAR> analyzer;
IndexWriter w(&dir, &analyzer, true);
Document doc;
for (int i = 0; i < 10000; ++i) {
//wchar_t value1[value.length()];
//lucene::util::Misc::_cpycharToWide(value.c_str(), value1, value.length());
//auto value1 = std::wstring(value).c_str();
auto value1 = lucene::util::Misc::_charToWide("value1");
auto field = _CLNEW Field(field_name, value1, Field::INDEX_TOKENIZED | Field::STORE_NO, false);
doc.add(*field);
w.addDocument(&doc);
doc.clear();
//_CLDELETE_ARRAY(value1);
}
w.close();
//_CLDELETE(field);
//_CLDELETE_ARRAY(field_name_w);
//doc.add(*);
});
}
CuSuite *testdocument(void) {
CuSuite *suite = CuSuiteNew(_T("CLucene Document Test"));
SUITE_ADD_TEST(suite, TestFields);
SUITE_ADD_TEST(suite, TestMultiSetValueField);
SUITE_ADD_TEST(suite, TestMultiAddValueField);
SUITE_ADD_TEST(suite, TestReaderValueField);
//SUITE_ADD_TEST(suite, TestAddDocument);
return suite;
}