| /*------------------------------------------------------------------------------ |
| * 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 "PrefixQuery.h" |
| #include "CLucene/util/BitSet.h" |
| |
| CL_NS_USE(util) |
| CL_NS_USE(index) |
| CL_NS_DEF(search) |
| |
| PrefixQuery::PrefixQuery(Term* Prefix){ |
| //Func - Constructor. |
| // Constructs a query for terms starting with prefix |
| //Pre - Prefix != NULL |
| //Post - The instance has been created |
| |
| //Get a pointer to Prefix |
| prefix = _CL_POINTER(Prefix); |
| } |
| |
| PrefixQuery::PrefixQuery(const PrefixQuery& clone):Query(clone){ |
| prefix = _CL_POINTER(clone.prefix); |
| } |
| Query* PrefixQuery::clone() const{ |
| return _CLNEW PrefixQuery(*this); |
| } |
| |
| Term* PrefixQuery::getPrefix(bool pointer){ |
| if ( pointer ) |
| return _CL_POINTER(prefix); |
| else |
| return prefix; |
| } |
| |
| PrefixQuery::~PrefixQuery(){ |
| //Func - Destructor |
| //Pre - true |
| //Post - The instance has been destroyed. |
| |
| //Delete prefix by finalizing it |
| _CLDECDELETE(prefix); |
| } |
| |
| |
| /** Returns a hash code value for this object.*/ |
| size_t PrefixQuery::hashCode() const { |
| return Similarity::floatToByte(getBoost()) ^ prefix->hashCode(); |
| } |
| |
| const TCHAR* PrefixQuery::getQueryName()const{ |
| //Func - Returns the name "PrefixQuery" |
| //Pre - true |
| //Post - The string "PrefixQuery" has been returned |
| |
| return getClassName(); |
| } |
| const TCHAR* PrefixQuery::getClassName(){ |
| //Func - Returns the name "PrefixQuery" |
| //Pre - true |
| //Post - The string "PrefixQuery" has been returned |
| |
| return _T("PrefixQuery"); |
| } |
| |
| bool PrefixQuery::equals(Query * other) const{ |
| if (!(other->instanceOf(PrefixQuery::getClassName()))) |
| return false; |
| |
| PrefixQuery* rq = (PrefixQuery*)other; |
| bool ret = (this->getBoost() == rq->getBoost()) |
| && (this->prefix->equals(rq->prefix)); |
| |
| return ret; |
| } |
| |
| Query* PrefixQuery::rewrite(IndexReader* reader){ |
| BooleanQuery* query = _CLNEW BooleanQuery(); |
| TermEnum* enumerator = reader->terms(prefix); |
| Term* lastTerm = NULL; |
| try { |
| const TCHAR* prefixText = prefix->text(); |
| const TCHAR* prefixField = prefix->field(); |
| const TCHAR* tmp; |
| size_t i; |
| int32_t prefixLen = prefix->textLength(); |
| do { |
| lastTerm = enumerator->term(); |
| if (lastTerm != NULL && lastTerm->field() == prefixField ){ |
| |
| //now see if term->text() starts with prefixText |
| int32_t termLen = lastTerm->textLength(); |
| if ( prefixLen>termLen ) |
| break; //the prefix is longer than the term, can't be matched |
| |
| tmp = lastTerm->text(); |
| |
| //check for prefix match in reverse, since most change will be at the end |
| for ( i=prefixLen-1;i!=-1;--i ){ |
| if ( tmp[i] != prefixText[i] ){ |
| tmp=NULL;//signals inequality |
| break; |
| } |
| } |
| if ( tmp == NULL ) |
| break; |
| |
| TermQuery* tq = _CLNEW TermQuery(lastTerm); // found a match |
| tq->setBoost(getBoost()); // set the boost |
| query->add(tq,true,false, false); // add to query |
| } else |
| break; |
| _CLDECDELETE(lastTerm); |
| } while (enumerator->next()); |
| }_CLFINALLY( |
| enumerator->close(); |
| _CLDELETE(enumerator); |
| _CLDECDELETE(lastTerm); |
| ); |
| _CLDECDELETE(lastTerm); |
| |
| |
| //if we only added one clause and the clause is not prohibited then |
| //we can just return the query |
| if (query->getClauseCount() == 1) { // optimize 1-clause queries |
| BooleanClause* c=0; |
| query->getClauses(&c); |
| |
| if (!c->prohibited) { // just return clause |
| c->deleteQuery=false; |
| Query* ret = c->query; |
| |
| _CLDELETE(query); |
| return ret; |
| } |
| } |
| |
| return query; |
| } |
| |
| Query* PrefixQuery::combine(Query** queries) { |
| return Query::mergeBooleanQueries(queries); |
| } |
| |
| TCHAR* PrefixQuery::toString(const TCHAR* field) const{ |
| //Func - Creates a user-readable version of this query and returns it as as string |
| //Pre - field != NULL |
| //Post - a user-readable version of this query has been returned as as string |
| |
| //Instantiate a stringbuffer buffer to store the readable version temporarily |
| CL_NS(util)::StringBuffer buffer; |
| //check if field equal to the field of prefix |
| if( field==NULL || _tcscmp(prefix->field(),field) != 0 ) { |
| //Append the field of prefix to the buffer |
| buffer.append(prefix->field()); |
| //Append a colon |
| buffer.append(_T(":") ); |
| } |
| //Append the text of the prefix |
| buffer.append(prefix->text()); |
| //Append a wildchar character |
| buffer.append(_T("*")); |
| //if the boost factor is not eaqual to 1 |
| if (getBoost() != 1.0f) { |
| //Append ^ |
| buffer.append(_T("^")); |
| //Append the boost factor |
| buffer.appendFloat( getBoost(),1); |
| } |
| //Convert StringBuffer buffer to TCHAR block and return it |
| return buffer.toString(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| PrefixFilter::PrefixFilter( Term* prefix ) |
| { |
| this->prefix = _CL_POINTER(prefix); |
| } |
| |
| PrefixFilter::~PrefixFilter() |
| { |
| _CLDECDELETE(prefix); |
| } |
| |
| PrefixFilter::PrefixFilter( const PrefixFilter& copy ) : |
| prefix( _CL_POINTER(copy.prefix) ) |
| { |
| } |
| |
| Filter* PrefixFilter::clone() const { |
| return _CLNEW PrefixFilter(*this ); |
| } |
| |
| TCHAR* PrefixFilter::toString() |
| { |
| //Instantiate a stringbuffer buffer to store the readable version temporarily |
| CL_NS(util)::StringBuffer buffer; |
| //check if field equal to the field of prefix |
| if( prefix->field() != NULL ) { |
| //Append the field of prefix to the buffer |
| buffer.append(prefix->field()); |
| //Append a colon |
| buffer.append(_T(":") ); |
| } |
| //Append the text of the prefix |
| buffer.append(prefix->text()); |
| buffer.append(_T("*")); |
| |
| //Convert StringBuffer buffer to TCHAR block and return it |
| return buffer.toString(); |
| } |
| |
| /** Returns a BitSet with true for documents which should be permitted in |
| search results, and false for those that should not. */ |
| BitSet* PrefixFilter::bits( IndexReader* reader ) |
| { |
| BitSet* bts = _CLNEW BitSet( reader->maxDoc() ); |
| TermEnum* enumerator = reader->terms(prefix); |
| TermDocs* docs = reader->termDocs(); |
| const TCHAR* prefixText = prefix->text(); |
| const TCHAR* prefixField = prefix->field(); |
| const TCHAR* tmp; |
| size_t i; |
| int32_t prefixLen = prefix->textLength(); |
| Term* lastTerm = NULL; |
| |
| try{ |
| do{ |
| lastTerm = enumerator->term(false); |
| if (lastTerm != NULL && lastTerm->field() == prefixField ){ |
| //now see if term->text() starts with prefixText |
| int32_t termLen = lastTerm->textLength(); |
| if ( prefixLen>termLen ) |
| break; //the prefix is longer than the term, can't be matched |
| |
| tmp = lastTerm->text(); |
| |
| //check for prefix match in reverse, since most change will be at the end |
| for ( i=prefixLen-1;i!=-1;--i ){ |
| if ( tmp[i] != prefixText[i] ){ |
| tmp=NULL;//signals inequality |
| break; |
| } |
| } |
| if ( tmp == NULL ) |
| break; |
| |
| docs->seek(enumerator); |
| while (docs->next()) { |
| bts->set(docs->doc()); |
| } |
| } |
| }while(enumerator->next()); |
| } _CLFINALLY( |
| docs->close(); |
| _CLDELETE(docs); |
| enumerator->close(); |
| _CLDELETE(enumerator); |
| ) |
| |
| return bts; |
| } |
| |
| CL_NS_END |