--- a/src/rcldb/searchdata.cpp
+++ b/src/rcldb/searchdata.cpp
@@ -510,13 +510,13 @@
private:
void expandTerm(bool dont, const string& term, list<string>& exp,
- string& sterm, string *prefix = 0);
+ string& sterm, const string& prefix);
// After splitting entry on whitespace: process non-phrase element
void processSimpleSpan(const string& span, bool nostemexp, list<Xapian::Query> &pqueries);
// Process phrase/near element
void processPhraseOrNear(TextSplitQ *splitData,
list<Xapian::Query> &pqueries,
- bool useNear, int slack);
+ bool useNear, int slack, int mods);
Db& m_db;
const string& m_field;
@@ -554,7 +554,7 @@
void StringToXapianQ::expandTerm(bool nostemexp,
const string& term,
list<string>& exp,
- string &sterm, string *prefix)
+ string &sterm, const string& prefix)
{
LOGDEB2(("expandTerm: field [%s] term [%s] stemlang [%s] nostemexp %d\n",
m_field.c_str(), term.c_str(), m_stemlang.c_str(), nostemexp));
@@ -571,29 +571,20 @@
nostemexp = true;
if (nostemexp && !haswild) {
- // Neither stemming nor wildcard expansion: just the word
- string pfx;
- const FieldTraits *ftp;
- if (!m_field.empty() && m_db.fieldToTraits(m_field, &ftp)) {
- pfx = ftp->pfx;
- }
-
sterm = term;
m_uterms.push_back(sterm);
- exp.push_front(pfx+term);
+ exp.push_front(prefix + term);
exp.resize(1);
- if (prefix)
- *prefix = pfx;
} else {
TermMatchResult res;
if (haswild) {
m_db.termMatch(Rcl::Db::ET_WILD, m_stemlang, term, res, -1,
- m_field, prefix);
+ m_field);
} else {
sterm = term;
m_uterms.push_back(sterm);
- m_db.termMatch(Rcl::Db::ET_STEM, m_stemlang, term, res, -1, m_field,
- prefix);
+ m_db.termMatch(Rcl::Db::ET_STEM, m_stemlang, term, res, -1,
+ m_field);
}
for (list<TermMatchEntry>::const_iterator it = res.entries.begin();
it != res.entries.end(); it++) {
@@ -642,8 +633,15 @@
{
list<string> exp;
string sterm; // dumb version of user term
+
string prefix;
- expandTerm(nostemexp, span, exp, sterm, &prefix);
+ const FieldTraits *ftp;
+ if (!m_field.empty() && m_db.fieldToTraits(m_field, &ftp)) {
+ prefix = ftp->pfx;
+ }
+
+ expandTerm(nostemexp, span, exp, sterm, prefix);
+
// m_terms is used for highlighting, we don't want prefixes in there.
for (list<string>::const_iterator it = exp.begin();
it != exp.end(); it++) {
@@ -658,10 +656,9 @@
// less wqf). This does not happen if there are wildcards anywhere
// in the search.
if (m_doBoostUserTerms && !sterm.empty()) {
- xq = Xapian::Query(Xapian::Query::OP_OR,
- xq,
- Xapian::Query(prefix+sterm,
- original_term_wqf_booster));
+ xq = Xapian::Query(Xapian::Query::OP_OR, xq,
+ Xapian::Query(prefix+sterm,
+ original_term_wqf_booster));
}
pqueries.push_back(xq);
}
@@ -672,13 +669,24 @@
// don't do stemming for PHRASE though)
void StringToXapianQ::processPhraseOrNear(TextSplitQ *splitData,
list<Xapian::Query> &pqueries,
- bool useNear, int slack)
+ bool useNear, int slack, int mods)
{
Xapian::Query::op op = useNear ? Xapian::Query::OP_NEAR :
Xapian::Query::OP_PHRASE;
list<Xapian::Query> orqueries;
bool hadmultiple = false;
vector<vector<string> >groups;
+
+ string prefix;
+ const FieldTraits *ftp;
+ if (!m_field.empty() && m_db.fieldToTraits(m_field, &ftp)) {
+ prefix = ftp->pfx;
+ }
+
+ if (mods & Rcl::SearchDataClause::SDCM_ANCHORSTART) {
+ orqueries.push_back(Xapian::Query(prefix + start_of_field_term));
+ slack++;
+ }
// Go through the list and perform stem/wildcard expansion for each element
vector<bool>::iterator nxit = splitData->nostemexps.begin();
@@ -691,8 +699,7 @@
string sterm;
list<string>exp;
- string prefix;
- expandTerm(nostemexp, *it, exp, sterm, &prefix);
+ expandTerm(nostemexp, *it, exp, sterm, prefix);
// groups is used for highlighting, we don't want prefixes in there.
vector<string> noprefs;
@@ -707,6 +714,11 @@
if (exp.size() > 1)
hadmultiple = true;
#endif
+ }
+
+ if (mods & Rcl::SearchDataClause::SDCM_ANCHOREND) {
+ orqueries.push_back(Xapian::Query(prefix + end_of_field_term));
+ slack++;
}
// Generate an appropriate PHRASE/NEAR query with adjusted slack
@@ -725,6 +737,23 @@
vector<string> comb;
multiply_groups(groups.begin(), groups.end(), comb, allcombs);
m_groups.insert(m_groups.end(), allcombs.begin(), allcombs.end());
+}
+
+// Trim string beginning with ^ or ending with $ and convert to flags
+static int stringToMods(string& s)
+{
+ int mods = 0;
+ // Check for an anchored search
+ trimstring(s);
+ if (s.length() > 0 && s[0] == '^') {
+ mods |= Rcl::SearchDataClause::SDCM_ANCHORSTART;
+ s.erase(0, 1);
+ }
+ if (s.length() > 0 && s[s.length()-1] == '$') {
+ mods |= Rcl::SearchDataClause::SDCM_ANCHOREND;
+ s.erase(s.length()-1);
+ }
+ return mods;
}
/**
@@ -772,7 +801,8 @@
for (list<string>::iterator it = phrases.begin();
it != phrases.end(); it++) {
LOGDEB0(("strToXapianQ: phrase/word: [%s]\n", it->c_str()));
-
+ int mods = stringToMods(*it);
+ int terminc = mods != 0 ? 1 : 0;
// If there are multiple spans in this element, including
// at least one composite, we have to increase the slack
// else a phrase query including a span would fail.
@@ -803,7 +833,7 @@
}
LOGDEB0(("strToXapianQ: termcount: %d\n", splitter->terms.size()));
- switch (splitter->terms.size()) {
+ switch (splitter->terms.size() + terminc) {
case 0:
continue;// ??
case 1:
@@ -811,7 +841,7 @@
splitter->nostemexps.front(), pqueries);
break;
default:
- processPhraseOrNear(splitter, pqueries, useNear, slack);
+ processPhraseOrNear(splitter, pqueries, useNear, slack, mods);
}
}
} catch (const Xapian::Error &e) {