| /*------------------------------------------------------------------------------ |
| * 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 "test.h" |
| |
| class MQPTestFilter: public TokenFilter { |
| public: |
| |
| bool inPhrase; |
| int32_t savedStart, savedEnd; |
| |
| /** |
| * Filter which discards the token 'stop' and which expands the |
| * token 'phrase' into 'phrase1 phrase2' |
| */ |
| MQPTestFilter(TokenStream* in): |
| TokenFilter(in,true), |
| inPhrase(false), |
| savedStart(0), |
| savedEnd(0) |
| { |
| } |
| |
| CL_NS(analysis)::Token* next(CL_NS(analysis)::Token* token) { |
| if (inPhrase) { |
| inPhrase = false; |
| token->set( _T("phrase2"), savedStart, savedEnd); |
| return token; |
| }else{ |
| while( input->next(token) ){ |
| if ( _tcscmp(token->termBuffer<TCHAR> (), _T("phrase")) == 0 ) { |
| inPhrase = true; |
| savedStart = token->startOffset(); |
| savedEnd = token->endOffset(); |
| token->set( _T("phrase1"), savedStart, savedEnd); |
| return token; |
| }else if ( _tcscmp(token->termBuffer<TCHAR> (), _T("stop") ) !=0 ){ |
| return token; |
| } |
| } |
| } |
| return NULL; |
| } |
| }; |
| |
| class MQPTestAnalyzer: public Analyzer { |
| public: |
| MQPTestAnalyzer() { |
| } |
| |
| /** Filters LowerCaseTokenizer with StopFilter. */ |
| TokenStream* tokenStream(const TCHAR* /*fieldName*/, Reader* reader) { |
| return _CLNEW MQPTestFilter(_CLNEW LowerCaseTokenizer<TCHAR> (reader)); |
| } |
| }; |
| |
| void assertQueryEquals(CuTest *tc,const TCHAR* result, Query* q) { |
| if ( q == NULL ) |
| return; |
| |
| TCHAR* s = q->toString(); |
| int ret = _tcscmp(s,result); |
| _CLDELETE(q); |
| if ( ret != 0 ) { |
| TCHAR buf[HUGE_STRING_LEN]; |
| _sntprintf(buf, HUGE_STRING_LEN, _T("FAILED Query yielded /%s/, expecting /%s/\n"), s, result); |
| _CLDELETE_LCARRAY(s); |
| CuFail(tc, buf); |
| return; |
| } |
| _CLDELETE_LCARRAY(s); |
| } |
| |
| // verify parsing of query using a stopping analyzer |
| void assertStopQueryEquals(CuTest *tc, const TCHAR* qtxt, const TCHAR* expectedRes) { |
| const TCHAR* fields[] = {_T("b"), _T("t"), NULL }; |
| const uint8_t occur[] = {BooleanClause::SHOULD, BooleanClause::SHOULD, NULL}; |
| MQPTestAnalyzer *a = _CLNEW MQPTestAnalyzer(); |
| MultiFieldQueryParser mfqp(fields, a); |
| |
| Query *q = mfqp.parse(qtxt); |
| assertQueryEquals(tc, expectedRes, q); |
| |
| q = MultiFieldQueryParser::parse(qtxt, reinterpret_cast<const TCHAR**>(&fields), |
| reinterpret_cast<const uint8_t*>(&occur), a); |
| assertQueryEquals(tc, expectedRes, q); |
| _CLDELETE(a); |
| } |
| |
| /** test stop words arsing for both the non static form, and for the |
| * corresponding static form (qtxt, fields[]). */ |
| void tesStopwordsParsing(CuTest *tc) { |
| assertStopQueryEquals(tc, _T("one"), _T("b:one t:one")); |
| assertStopQueryEquals(tc, _T("one stop"), _T("b:one t:one")); |
| assertStopQueryEquals(tc, _T("one (stop)"), _T("b:one t:one")); |
| assertStopQueryEquals(tc, _T("one ((stop))"), _T("b:one t:one")); |
| assertStopQueryEquals(tc, _T("stop"), _T("")); |
| assertStopQueryEquals(tc, _T("(stop)"), _T("")); |
| assertStopQueryEquals(tc, _T("((stop))"), _T("")); |
| } |
| |
| void testMFQPSimple(CuTest *tc) { |
| const TCHAR* fields[] = {_T("b"), _T("t"), NULL}; |
| Analyzer* a = _CLNEW StandardAnalyzer(); |
| MultiFieldQueryParser mfqp(fields, a); |
| |
| Query *q = mfqp.parse(_T("one")); |
| assertQueryEquals(tc, _T("b:one t:one"), q); |
| |
| q = mfqp.parse(_T("one two")); |
| assertQueryEquals(tc, _T("(b:one t:one) (b:two t:two)"),q); |
| |
| q = mfqp.parse(_T("+one +two")); |
| assertQueryEquals(tc, _T("+(b:one t:one) +(b:two t:two)"), q); |
| |
| q = mfqp.parse(_T("+one -two -three")); |
| assertQueryEquals(tc, _T("+(b:one t:one) -(b:two t:two) -(b:three t:three)"), q); |
| |
| q = mfqp.parse(_T("one^2 two")); |
| assertQueryEquals(tc, _T("((b:one t:one)^2.0) (b:two t:two)"), q); |
| |
| q = mfqp.parse(_T("one~ two")); |
| assertQueryEquals(tc, _T("(b:one~0.5 t:one~0.5) (b:two t:two)"), q); |
| |
| q = mfqp.parse(_T("one~0.8 two^2")); |
| assertQueryEquals(tc, _T("(b:one~0.8 t:one~0.8) ((b:two t:two)^2.0)"), q); |
| |
| q = mfqp.parse(_T("one* two*")); |
| assertQueryEquals(tc, _T("(b:one* t:one*) (b:two* t:two*)"), q); |
| |
| q = mfqp.parse(_T("[a TO c] two")); |
| assertQueryEquals(tc, _T("(b:[a TO c] t:[a TO c]) (b:two t:two)"), q); |
| |
| q = mfqp.parse(_T("w?ldcard")); |
| assertQueryEquals(tc, _T("b:w?ldcard t:w?ldcard"), q); |
| |
| q = mfqp.parse(_T("\"foo bar\"")); |
| assertQueryEquals(tc, _T("b:\"foo bar\" t:\"foo bar\""), q); |
| |
| q = mfqp.parse(_T("\"aa bb cc\" \"dd ee\"")); |
| assertQueryEquals(tc, _T("(b:\"aa bb cc\" t:\"aa bb cc\") (b:\"dd ee\" t:\"dd ee\")"), q); |
| |
| q = mfqp.parse(_T("\"foo bar\"~4")); |
| assertQueryEquals(tc, _T("b:\"foo bar\"~4 t:\"foo bar\"~4"), q); |
| |
| // make sure that terms which have a field are not touched: |
| q = mfqp.parse(_T("one f:two")); |
| assertQueryEquals(tc, _T("(b:one t:one) f:two"), q); |
| |
| // AND mode: |
| mfqp.setDefaultOperator(QueryParser::AND_OPERATOR); |
| q = mfqp.parse(_T("one two")); |
| assertQueryEquals(tc, _T("+(b:one t:one) +(b:two t:two)"), q); |
| q = mfqp.parse(_T("\"aa bb cc\" \"dd ee\"")); |
| assertQueryEquals(tc, _T("+(b:\"aa bb cc\" t:\"aa bb cc\") +(b:\"dd ee\" t:\"dd ee\")"), q); |
| |
| _CLDELETE(a); |
| } |
| |
| CuSuite *testMultiFieldQueryParser(void) |
| { |
| CuSuite *suite = CuSuiteNew(_T("CLucene Multi-Field QP Test")); |
| |
| SUITE_ADD_TEST(suite, tesStopwordsParsing); |
| SUITE_ADD_TEST(suite, testMFQPSimple); |
| |
| return suite; |
| } |