/*------------------------------------------------------------------------------ | |
* 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 "CLucene/StdHeader.h" | |
#include "FieldSortedHitQueue.h" | |
#include "FieldDocSortedHitQueue.h" | |
#include "Compare.h" | |
CL_NS_USE(util) | |
CL_NS_USE(index) | |
CL_NS_DEF(search) | |
FieldSortedHitQueue::hitqueueCacheType FieldSortedHitQueue::Comparators(false,true); | |
FieldSortedHitQueue::FieldSortedHitQueue (IndexReader* reader, SortField** _fields, int32_t size): | |
fieldsLen(0), | |
maxscore(1.0f) | |
{ | |
while ( _fields[fieldsLen] != 0 ) | |
fieldsLen++; | |
comparators = _CL_NEWARRAY(ScoreDocComparator*,fieldsLen+1); | |
SortField** tmp = _CL_NEWARRAY(SortField*,fieldsLen+1); | |
for (int32_t i=0; i<fieldsLen; ++i) { | |
const TCHAR* fieldname = _fields[i]->getField(); | |
//todo: fields[i].getLocale(), not implemented | |
comparators[i] = getCachedComparator (reader, fieldname, _fields[i]->getType(), _fields[i]->getFactory()); | |
tmp[i] = _CLNEW SortField (fieldname, comparators[i]->sortType(), _fields[i]->getReverse()); | |
} | |
comparatorsLen = fieldsLen; | |
comparators[fieldsLen]=NULL; | |
tmp[fieldsLen] = NULL; | |
this->fields = tmp; | |
initialize(size,true); | |
} | |
bool FieldSortedHitQueue::lessThan (FieldDoc* docA, FieldDoc* docB) { | |
// keep track of maximum score | |
if (docA->scoreDoc.score > maxscore) maxscore = docA->scoreDoc.score; | |
if (docB->scoreDoc.score > maxscore) maxscore = docB->scoreDoc.score; | |
// run comparators | |
int32_t c = 0; | |
for ( int32_t i=0; c==0 && i<comparatorsLen; ++i ) { | |
c = (fields[i]->getReverse()) ? comparators[i]->compare (&docB->scoreDoc, &docA->scoreDoc) : | |
comparators[i]->compare (&docA->scoreDoc, &docB->scoreDoc); | |
} | |
// avoid random sort order that could lead to duplicates (bug #31241): | |
if (c == 0) | |
return docA->scoreDoc.doc > docB->scoreDoc.doc; | |
return c > 0; | |
} | |
//static | |
ScoreDocComparator* FieldSortedHitQueue::comparatorString (IndexReader* reader, const TCHAR* field) { | |
//const TCHAR* field = CLStringIntern::intern(fieldname CL_FILELINE); | |
FieldCacheAuto* fa = FieldCache::DEFAULT->getStringIndex (reader, field); | |
//CLStringIntern::unintern(field); | |
CND_PRECONDITION(fa->contentType==FieldCacheAuto::STRING_INDEX,"Content type is incorrect"); | |
fa->ownContents = false; | |
return _CLNEW ScoreDocComparators::String(fa->stringIndex, fa->contentLen); | |
} | |
//static | |
ScoreDocComparator* FieldSortedHitQueue::comparatorInt (IndexReader* reader, const TCHAR* field){ | |
//const TCHAR* field = CLStringIntern::intern(fieldname CL_FILELINE); | |
FieldCacheAuto* fa = FieldCache::DEFAULT->getInts (reader, field); | |
//CLStringIntern::unintern(field); | |
CND_PRECONDITION(fa->contentType==FieldCacheAuto::INT_ARRAY,"Content type is incorrect"); | |
return _CLNEW ScoreDocComparators::Int32(fa->intArray, fa->contentLen); | |
} | |
//static | |
ScoreDocComparator* FieldSortedHitQueue::comparatorFloat (IndexReader* reader, const TCHAR* field) { | |
//const TCHAR* field = CLStringIntern::intern(fieldname CL_FILELINE); | |
FieldCacheAuto* fa = FieldCache::DEFAULT->getFloats (reader, field); | |
//CLStringIntern::unintern(field); | |
CND_PRECONDITION(fa->contentType==FieldCacheAuto::FLOAT_ARRAY,"Content type is incorrect"); | |
return _CLNEW ScoreDocComparators::Float (fa->floatArray, fa->contentLen); | |
} | |
//static | |
ScoreDocComparator* FieldSortedHitQueue::comparatorAuto (IndexReader* reader, const TCHAR* field){ | |
//const TCHAR* field = CLStringIntern::intern(fieldname CL_FILELINE); | |
FieldCacheAuto* fa = FieldCache::DEFAULT->getAuto (reader, field); | |
//CLStringIntern::unintern(field); | |
if (fa->contentType == FieldCacheAuto::STRING_INDEX ) { | |
return comparatorString (reader, field); | |
} else if (fa->contentType == FieldCacheAuto::INT_ARRAY) { | |
return comparatorInt (reader, field); | |
} else if (fa->contentType == FieldCacheAuto::FLOAT_ARRAY) { | |
return comparatorFloat (reader, field); | |
} else if (fa->contentType == FieldCacheAuto::STRING_ARRAY) { | |
return comparatorString (reader, field); | |
} else { | |
_CLTHROWA(CL_ERR_Runtime, "unknown data type in field"); //todo: rich error information: '"+field+"'"); | |
} | |
} | |
//todo: Locale locale, not implemented yet | |
ScoreDocComparator* FieldSortedHitQueue::getCachedComparator (IndexReader* reader, const TCHAR* fieldname, int32_t type, SortComparatorSource* factory){ | |
if (type == SortField::DOC) | |
return ScoreDocComparator::INDEXORDER; | |
if (type == SortField::DOCSCORE) | |
return ScoreDocComparator::RELEVANCE; | |
ScoreDocComparator* comparator = lookup (reader, fieldname, type, factory); | |
if (comparator == NULL) { | |
switch (type) { | |
case SortField::AUTO: | |
comparator = comparatorAuto (reader, fieldname); | |
break; | |
case SortField::INT: | |
comparator = comparatorInt (reader, fieldname); | |
break; | |
case SortField::FLOAT: | |
comparator = comparatorFloat (reader, fieldname); | |
break; | |
case SortField::STRING: | |
//if (locale != NULL) | |
// comparator = comparatorStringLocale (reader, fieldname, locale); | |
//else | |
comparator = comparatorString (reader, fieldname); | |
break; | |
case SortField::CUSTOM: | |
comparator = factory->newComparator (reader, fieldname); | |
break; | |
default: | |
_CLTHROWA(CL_ERR_Runtime,"unknown field type"); | |
//todo: extend error | |
//throw _CLNEW RuntimeException ("unknown field type: "+type); | |
} | |
store (reader, fieldname, type, factory, comparator); | |
} | |
return comparator; | |
} | |
FieldDoc* FieldSortedHitQueue::fillFields (FieldDoc* doc) const{ | |
int32_t n = comparatorsLen; | |
Comparable** fields = _CL_NEWARRAY(Comparable*,n+1); | |
for (int32_t i=0; i<n; ++i) | |
fields[i] = comparators[i]->sortValue(&doc->scoreDoc); | |
fields[n]=NULL; | |
doc->fields = fields; | |
if (maxscore > 1.0f) | |
doc->scoreDoc.score /= maxscore; // normalize scores | |
return doc; | |
} | |
ScoreDocComparator* FieldSortedHitQueue::lookup (IndexReader* reader, const TCHAR* field, int32_t type, SortComparatorSource* factory) { | |
ScoreDocComparator* sdc = NULL; | |
FieldCacheImpl::FileEntry* entry = (factory != NULL) | |
? _CLNEW FieldCacheImpl::FileEntry (field, factory) | |
: _CLNEW FieldCacheImpl::FileEntry (field, type); | |
{ | |
SCOPED_LOCK_MUTEX(Comparators.THIS_LOCK) | |
hitqueueCacheReaderType* readerCache = Comparators.get(reader); | |
if (readerCache == NULL){ | |
_CLDELETE(entry); | |
return NULL; | |
} | |
sdc = readerCache->get (entry); | |
_CLDELETE(entry); | |
} | |
return sdc; | |
} | |
void FieldSortedHitQueue::closeCallback(CL_NS(index)::IndexReader* reader, void*){ | |
SCOPED_LOCK_MUTEX(Comparators.THIS_LOCK) | |
Comparators.remove(reader); | |
} | |
//static | |
void FieldSortedHitQueue::store (IndexReader* reader, const TCHAR* field, int32_t type, SortComparatorSource* factory, ScoreDocComparator* value) { | |
FieldCacheImpl::FileEntry* entry = (factory != NULL) | |
? _CLNEW FieldCacheImpl::FileEntry (field, factory) | |
: _CLNEW FieldCacheImpl::FileEntry (field, type); | |
{ | |
SCOPED_LOCK_MUTEX(Comparators.THIS_LOCK) | |
hitqueueCacheReaderType* readerCache = Comparators.get(reader); | |
if (readerCache == NULL) { | |
readerCache = _CLNEW hitqueueCacheReaderType(true); | |
Comparators.put(reader,readerCache); | |
reader->addCloseCallback(FieldSortedHitQueue::closeCallback,NULL); | |
} | |
readerCache->put (entry, value); | |
//return NULL; //supposed to return previous value... | |
} | |
} | |
FieldSortedHitQueue::~FieldSortedHitQueue(){ | |
_CLDELETE_ARRAY(comparators); | |
if ( fields != NULL ){ | |
for ( int i=0;fields[i]!=NULL;i++ ) | |
_CLDELETE(fields[i]); | |
_CLDELETE_ARRAY(fields); | |
} | |
} | |
CL_NS_END |