Ticket #128: qp_exact_text.patch

File qp_exact_text.patch, 6.7 kB (added by richard, 21 months ago)

Draft implementation, no documentation, too few tests

  • xapian-bindings/xapian.i

     
    955955    Query parse_query(const string &q, unsigned flags); 
    956956 
    957957    void add_prefix(const std::string &field, const std::string &prefix); 
     958    void add_exact_prefix(const std::string &field, const std::string &prefix); 
    958959    void add_boolean_prefix(const std::string & field, const std::string &prefix); 
    959960 
    960961    TermIterator stoplist_begin() const; 
  • xapian-core/queryparser/queryparser.lemony

     
    461461                              new Term(prefix, 0), &state); 
    462462                        continue; 
    463463                    } 
     464                     
     465                    if (type == PrefixInfo::EXACT_TEXT) { 
     466                        it = p; 
     467                        if (prefix_needs_colon(prefix, *it)) 
     468                            prefix += ':'; 
     469                        field += ':'; 
     470                        string term; 
    464471 
     472                        if (*it == '"') { 
     473                            // Quoted term: continue until unescaped end quote 
     474                            field += *it; 
     475                            ++it; 
     476                            while (it != end && *it != '"') { 
     477                                field += *it; 
     478                                if (*it == '\\') { 
     479                                    ++it; 
     480                                    if (it == end) { 
     481                                        term.append(ubuf, to_utf8('\\' , ubuf)); 
     482                                    } else if (*it == '"' || *it == '\\') { 
     483                                        term.append(ubuf, to_utf8(*it++, ubuf)); 
     484                                    } else { 
     485                                        term.append(ubuf, to_utf8('\\' , ubuf)); 
     486                                        term.append(ubuf, to_utf8(*it++, ubuf)); 
     487                                    } 
     488                                } else { 
     489                                    term.append(ubuf, to_utf8(*it++, ubuf)); 
     490                                } 
     491                            } 
     492                            if (it != end) 
     493                                ++it; 
     494                        } else { 
     495                            // Non-quoted term: continue until space or ')'. 
     496                            while (it != end && *it > ' ' && *it != ')') 
     497                                term.append(ubuf, to_utf8(*it++, ubuf)); 
     498                            field += term; 
     499                        } 
     500 
     501                        prefix += term; 
     502                        unstem.insert(make_pair(prefix, field)); 
     503                        Parse(pParser, TERM, 
     504                              new Term(prefix, term_pos++), &state); 
     505                        continue; 
     506                    } 
     507 
    465508                    Assert(type == PrefixInfo::FREE_TEXT); 
    466509 
    467510                    if (ch == '"' && (flags & FLAG_PHRASE)) { 
     
    475518                        continue; 
    476519                    } 
    477520 
     521 
    478522                    if (ch == '(' && (flags & FLAG_BOOLEAN)) { 
    479523                        // Prefixed subexpression, e.g.: title:(fast NEAR food) 
    480524                        Parse(pParser, BRA, NULL, &state); 
     
    938982    if (P->hate.can_match()) { 
    939983        if (E->empty()) { 
    940984            delete E; 
    941             // Can't just hate! 
    942             yy_parse_failed(yypParser); 
    943             return; 
     985            if (state->flags & QueryParser::FLAG_PURE_NOT) { 
     986                E = new QpQuery("", 1, 0); 
     987            } else { 
     988                // Can't just hate! 
     989                yy_parse_failed(yypParser); 
     990                return; 
     991            } 
    944992        } 
    945993        *E = QpQuery(Query::OP_AND_NOT, *E, P->hate); 
    946994    } 
  • xapian-core/queryparser/queryparser_internal.h

     
    3737struct PrefixInfo { 
    3838    typedef enum { 
    3939        FREE_TEXT, 
     40        EXACT_TEXT, 
    4041        BOOL_FILTER 
    4142    } prefix_type; 
    4243    PrefixInfo::prefix_type type; 
  • xapian-core/queryparser/queryparser.cc

     
    130130} 
    131131 
    132132void 
     133QueryParser::add_exact_prefix(const std::string &field, 
     134                              const std::string &prefix) 
     135{ 
     136    internal->prefixes.insert( 
     137        make_pair(field, PrefixInfo(PrefixInfo::EXACT_TEXT, prefix))); 
     138} 
     139 
     140void 
    133141QueryParser::add_boolean_prefix(const std::string &field, 
    134142                                const std::string &prefix) 
    135143{ 
  • xapian-core/tests/queryparsertest.cc

     
    8787    { "-site:xapian.org mail", "(mail:(pos=1) AND_NOT Hxapian.org)" }, 
    8888    { "site:xapian.org", "Hxapian.org" }, 
    8989    { "mug +site:xapian.org -site:cvs.xapian.org", "((mug:(pos=1) AND_NOT Hcvs.xapian.org) FILTER Hxapian.org)" }, 
     90    { "exact:xapian.org exact:cvs.xapian.org", "(Exapian.org:(pos=1) OR Ecvs.xapian.org:(pos=2))" }, 
     91    { "+exact:xapian.org -exact:cvs.xapian.org", "(Exapian.org:(pos=1) AND_NOT Ecvs.xapian.org:(pos=2))" }, 
     92    { "-exact:cvs.xapian.org", "Ecvs.xapian.org:(pos=1)" }, 
     93    { "mug +exact:xapian.org", "(Exapian.org:(pos=2) AND_MAYBE mug:(pos=1))" }, 
    9094    { "mug -site:cvs.xapian.org +site:xapian.org", "((mug:(pos=1) AND_NOT Hcvs.xapian.org) FILTER Hxapian.org)" }, 
    9195    { "NOT windows", "Syntax: <expression> NOT <expression>" }, 
    9296    { "a AND (NOT b)", "Syntax: <expression> NOT <expression>" }, 
     
    564568    { "AND NOT windows", "Syntax: <expression> AND NOT <expression>" }, 
    565569    { "gordian NOT", "Syntax: <expression> NOT <expression>" }, 
    566570    { "gordian AND NOT", "Syntax: <expression> AND NOT <expression>" }, 
     571    { "NOT exact:cvs.xapian.org", "(<alldocuments> AND_NOT Ecvs.xapian.org:(pos=1))" }, 
     572    { "-exact:cvs.xapian.org", "(<alldocuments> AND_NOT Ecvs.xapian.org:(pos=1))" }, 
     573    { "-exact:\"hello world\"", "(<alldocuments> AND_NOT Ehello world:(pos=1))" }, 
     574    { "-exact:help", "(<alldocuments> AND_NOT Ehelp:(pos=1))" }, 
    567575    { NULL, NULL } 
    568576}; 
    569577 
     
    576584    queryparser.add_prefix("title", "XT"); 
    577585    queryparser.add_prefix("subject", "XT"); 
    578586    queryparser.add_boolean_prefix("site", "H"); 
     587    queryparser.add_exact_prefix("exact", "E"); 
    579588    for (test *p = test_or_queries; p->query; ++p) { 
    580589        string expect, parsed; 
    581590        if (p->expect) 
     
    845854    Xapian::QueryParser qp; 
    846855    qp.set_stemmer(Xapian::Stem("english")); 
    847856    qp.set_stemming_strategy(QueryParser::STEM_SOME); 
     857    qp.add_exact_prefix("exact", "E"); 
    848858    for (test *p = test_pure_not_queries; p->query; ++p) { 
    849859        string expect, parsed; 
    850860        if (p->expect) 
     
    854864        try { 
    855865            Xapian::Query qobj = qp.parse_query(p->query, 
    856866                                                QueryParser::FLAG_BOOLEAN | 
    857                                                 QueryParser::FLAG_PURE_NOT); 
     867                                                QueryParser::FLAG_PURE_NOT | 
     868                                                QueryParser::FLAG_LOVEHATE); 
    858869            parsed = qobj.get_description(); 
    859870            expect = string("Xapian::Query(") + expect + ')'; 
    860871        } catch (const Xapian::Error &e) { 
  • xapian-core/include/xapian/queryparser.h

     
    228228     */ 
    229229    void add_prefix(const std::string &field, const std::string &prefix); 
    230230 
     231    /** 
     232     */ 
     233    void add_exact_prefix(const std::string &field, const std::string &prefix); 
     234 
    231235    /** Add a boolean term prefix allowing the user to restrict a 
    232236     *  search with a boolean filter specified in the free text query. 
    233237     *