/*------------------------------------------------------------------------------
* 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