Ticket #128: qp_exact_text.patch

File qp_exact_text.patch, 6.7 KB (added by Richard Boulton, 17 years 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     *