/*------------------------------------------------------------------------------ * 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" #ifndef NO_RANGE_QUERY #include "RangeQuery.h" #include "SearchHeader.h" #include "Scorer.h" #include "BooleanQuery.h" #include "TermQuery.h" #include "CLucene/index/Term.h" #include "CLucene/index/Terms.h" #include "CLucene/index/IndexReader.h" #include "CLucene/util/StringBuffer.h" CL_NS_USE(index) CL_NS_USE(util) CL_NS_DEF(search) RangeQuery::RangeQuery(Term* lowerTerm, Term* upperTerm, const bool Inclusive){ //Func - Constructor //Pre - (LowerTerm != NULL OR UpperTerm != NULL) AND // if LowerTerm and UpperTerm are valid pointer then the fieldnames must be the same //Post - The instance has been created if (lowerTerm == NULL && upperTerm == NULL) { _CLTHROWA(CL_ERR_IllegalArgument,"At least one term must be non-null"); } if (lowerTerm != NULL && upperTerm != NULL && lowerTerm->field() != upperTerm->field()) { _CLTHROWA(CL_ERR_IllegalArgument,"Both terms must be for the same field"); } // if we have a lowerTerm, start there. otherwise, start at beginning if (lowerTerm != NULL) { this->lowerTerm = _CL_POINTER(lowerTerm); } else { this->lowerTerm = _CLNEW Term(upperTerm->field(), LUCENE_BLANK_STRING); } //todo: could we not have an open end as well??? this->upperTerm = (upperTerm != NULL ? _CL_POINTER(upperTerm) : NULL); this->inclusive = Inclusive; } RangeQuery::RangeQuery(const RangeQuery& clone): Query(clone){ this->inclusive = clone.inclusive; this->upperTerm = (clone.upperTerm != NULL ? _CL_POINTER(clone.upperTerm) : NULL ); this->lowerTerm = (clone.lowerTerm != NULL ? _CL_POINTER(clone.lowerTerm) : NULL ); } Query* RangeQuery::clone() const{ return _CLNEW RangeQuery(*this); } RangeQuery::~RangeQuery() { //Func - Destructor //Pre - true //Post - The instance has been destroyed _CLDECDELETE(lowerTerm); _CLDECDELETE(upperTerm); } /** Returns a hash code value for this object.*/ size_t RangeQuery::hashCode() const { return Similarity::floatToByte(getBoost()) ^ (lowerTerm != NULL ? lowerTerm->hashCode() : 0) ^ (upperTerm != NULL ? upperTerm->hashCode() : 0) ^ (this->inclusive ? 1 : 0); } const TCHAR* RangeQuery::getQueryName() const{ return getClassName(); } const TCHAR* RangeQuery::getClassName(){ return _T("RangeQuery"); } Query* RangeQuery::combine(Query** queries) { return Query::mergeBooleanQueries(queries); } bool RangeQuery::equals(Query * other) const{ if (!(other->instanceOf(RangeQuery::getClassName()))) return false; RangeQuery* rq = (RangeQuery*)other; bool ret = (this->getBoost() == rq->getBoost()) && (this->isInclusive() == rq->isInclusive()) && (this->getLowerTerm()->equals(rq->getLowerTerm())) && (this->getUpperTerm()->equals(rq->getUpperTerm())); return ret; } /** * FIXME: Describe rewrite method here. * * @param reader an IndexReader value * @return a Query value * @exception IOException if an error occurs */ Query* RangeQuery::rewrite(IndexReader* reader){ BooleanQuery* query = _CLNEW BooleanQuery; TermEnum* enumerator = reader->terms(lowerTerm); Term* lastTerm = NULL; try { bool checkLower = false; if (!inclusive) // make adjustments to set to exclusive checkLower = true; const TCHAR* testField = getField(); do { lastTerm = enumerator->term(); if (lastTerm != NULL && lastTerm->field() == testField ) { if (!checkLower || _tcscmp(lastTerm->text(),lowerTerm->text()) > 0) { checkLower = false; if (upperTerm != NULL) { int compare = _tcscmp(upperTerm->text(),lastTerm->text()); /* if beyond the upper term, or is exclusive and * this is equal to the upper term, break out */ if ((compare < 0) || (!inclusive && compare == 0)) 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()); }catch(...){ //todo: only catch IO Err??? _CLDECDELETE(lastTerm); //always need to delete this _CLDELETE(query); //in case of error, delete the query enumerator->close(); _CLDELETE(enumerator); throw; //rethrow } _CLDECDELETE(lastTerm); //always need to delete this enumerator->close(); _CLDELETE(enumerator); return query; } /** Prints a user-readable version of this query. */ TCHAR* RangeQuery::toString(const TCHAR* field) const { StringBuffer buffer; if ( field==NULL || _tcscmp(getField(),field)!=0 ) { buffer.append( getField() ); buffer.append( _T(":")); } buffer.append(inclusive ? _T("[") : _T("{")); buffer.append(lowerTerm != NULL ? lowerTerm->text() : _T("NULL")); buffer.append(_T(" TO ")); buffer.append(upperTerm != NULL ? upperTerm->text() : _T("NULL")); buffer.append(inclusive ? _T("]") : _T("}")); if (getBoost() != 1.0f) { buffer.append( _T("^")); buffer.appendFloat( getBoost(),1 ); } return buffer.toString(); } const TCHAR* RangeQuery::getField() const { return (lowerTerm != NULL ? lowerTerm->field() : upperTerm->field()); } /** Returns the lower term of this range query */ Term* RangeQuery::getLowerTerm(bool pointer) const { if ( pointer ) return _CL_POINTER(lowerTerm); else return lowerTerm; } /** Returns the upper term of this range query */ Term* RangeQuery::getUpperTerm(bool pointer) const { if ( pointer ) return _CL_POINTER(upperTerm); else return upperTerm; } /** Returns true if the range query is inclusive */ bool RangeQuery::isInclusive() const { return inclusive; } CL_NS_END #endif