define new searchdataclausepath to replace the old dir: filtering mechanism. ORing dirs now works
This commit is contained in:
parent
e219fa016d
commit
3e6a9971c3
10 changed files with 200 additions and 164 deletions
|
@ -39,7 +39,7 @@ bool subtreelist(RclConfig *config, const string& top,
|
||||||
Rcl::SearchData *sd = new Rcl::SearchData(Rcl::SCLT_OR, cstr_null);
|
Rcl::SearchData *sd = new Rcl::SearchData(Rcl::SCLT_OR, cstr_null);
|
||||||
RefCntr<Rcl::SearchData> rq(sd);
|
RefCntr<Rcl::SearchData> rq(sd);
|
||||||
|
|
||||||
rq->addDirSpec(top);
|
sd->addClause(new Rcl::SearchDataClausePath(top, false));
|
||||||
|
|
||||||
Rcl::Query query(&rcldb);
|
Rcl::Query query(&rcldb);
|
||||||
query.setQuery(rq);
|
query.setQuery(rq);
|
||||||
|
|
|
@ -384,9 +384,9 @@ void AdvSearch::runSearch()
|
||||||
string cat;
|
string cat;
|
||||||
if ((qit = cat_rtranslations.find(qcat)) !=
|
if ((qit = cat_rtranslations.find(qcat)) !=
|
||||||
cat_rtranslations.end()) {
|
cat_rtranslations.end()) {
|
||||||
cat = (const char *)qit->second.toUtf8();
|
cat = qs2utf8s(qit->second);
|
||||||
} else {
|
} else {
|
||||||
cat = (const char *)qcat.toUtf8();
|
cat = qs2utf8s(qcat);
|
||||||
}
|
}
|
||||||
vector<string> types;
|
vector<string> types;
|
||||||
theconfig->getMimeCatTypes(cat, types);
|
theconfig->getMimeCatTypes(cat, types);
|
||||||
|
@ -395,8 +395,7 @@ void AdvSearch::runSearch()
|
||||||
sdata->addFiletype(*it);
|
sdata->addFiletype(*it);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sdata->addFiletype((const char *)
|
sdata->addFiletype(qs2utf8s(yesFiltypsLB->item(i)->text()));
|
||||||
yesFiltypsLB->item(i)->text().toUtf8());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,8 +421,9 @@ void AdvSearch::runSearch()
|
||||||
|
|
||||||
if (!subtreeCMB->currentText().isEmpty()) {
|
if (!subtreeCMB->currentText().isEmpty()) {
|
||||||
QString current = subtreeCMB->currentText();
|
QString current = subtreeCMB->currentText();
|
||||||
sdata->addDirSpec((const char*)subtreeCMB->currentText().toUtf8(),
|
sdata->addClause(new Rcl::SearchDataClausePath(
|
||||||
direxclCB->isChecked());
|
(const char*)current.toLocal8Bit(),
|
||||||
|
direxclCB->isChecked()));
|
||||||
// Keep history clean and sorted. Maybe there would be a
|
// Keep history clean and sorted. Maybe there would be a
|
||||||
// simpler way to do this
|
// simpler way to do this
|
||||||
list<QString> entries;
|
list<QString> entries;
|
||||||
|
@ -463,12 +463,25 @@ void AdvSearch::fromSearch(RefCntr<SearchData> sdata)
|
||||||
addClause();
|
addClause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subtreeCMB->setEditText("");
|
||||||
|
direxclCB->setChecked(0);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < sdata->m_query.size(); i++) {
|
for (unsigned int i = 0; i < sdata->m_query.size(); i++) {
|
||||||
// Set fields from clause
|
// Set fields from clause
|
||||||
if (sdata->m_query[i]->getTp() == SCLT_SUB) {
|
if (sdata->m_query[i]->getTp() == SCLT_SUB) {
|
||||||
LOGERR(("AdvSearch::fromSearch: SUB clause found !\n"));
|
LOGERR(("AdvSearch::fromSearch: SUB clause found !\n"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (sdata->m_query[i]->getTp() == SCLT_PATH) {
|
||||||
|
SearchDataClausePath *cs =
|
||||||
|
dynamic_cast<SearchDataClausePath*>(sdata->m_query[i]);
|
||||||
|
// We can only use one such clause. There should be only one too
|
||||||
|
// if this is sfrom aved search data.
|
||||||
|
QString qdir = QString::fromLocal8Bit(cs->gettext().c_str());
|
||||||
|
subtreeCMB->setEditText(qdir);
|
||||||
|
direxclCB->setChecked(cs->getexclude());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
SearchDataClauseSimple *cs =
|
SearchDataClauseSimple *cs =
|
||||||
dynamic_cast<SearchDataClauseSimple*>(sdata->m_query[i]);
|
dynamic_cast<SearchDataClauseSimple*>(sdata->m_query[i]);
|
||||||
m_clauseWins[i]->setFromClause(cs);
|
m_clauseWins[i]->setFromClause(cs);
|
||||||
|
@ -531,16 +544,6 @@ void AdvSearch::fromSearch(RefCntr<SearchData> sdata)
|
||||||
minSizeLE->setText("");
|
minSizeLE->setText("");
|
||||||
maxSizeLE->setText("");
|
maxSizeLE->setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sdata->m_dirspecs.empty()) {
|
|
||||||
// Can only use one entry
|
|
||||||
QString qdir = QString::fromLocal8Bit(sdata->m_dirspecs[0].dir.c_str());
|
|
||||||
subtreeCMB->setEditText(qdir);
|
|
||||||
direxclCB->setChecked(sdata->m_dirspecs[0].exclude);
|
|
||||||
} else {
|
|
||||||
subtreeCMB->setEditText("");
|
|
||||||
direxclCB->setChecked(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdvSearch::slotHistoryNext()
|
void AdvSearch::slotHistoryNext()
|
||||||
|
|
|
@ -29,8 +29,8 @@ using namespace Rcl;
|
||||||
class SDHXMLHandler : public QXmlDefaultHandler {
|
class SDHXMLHandler : public QXmlDefaultHandler {
|
||||||
public:
|
public:
|
||||||
SDHXMLHandler()
|
SDHXMLHandler()
|
||||||
: slack(0)
|
|
||||||
{
|
{
|
||||||
|
resetTemps();
|
||||||
}
|
}
|
||||||
bool startElement(const QString & /* namespaceURI */,
|
bool startElement(const QString & /* namespaceURI */,
|
||||||
const QString & /* localName */,
|
const QString & /* localName */,
|
||||||
|
@ -165,11 +165,11 @@ bool SDHXMLHandler::endElement(const QString & /* namespaceURI */,
|
||||||
} else if (qName == "YD") {
|
} else if (qName == "YD") {
|
||||||
string d;
|
string d;
|
||||||
base64_decode((const char*)currentText.trimmed().toAscii(), d);
|
base64_decode((const char*)currentText.trimmed().toAscii(), d);
|
||||||
sd->addDirSpec(d);
|
sd->addClause(new SearchDataClausePath(d));
|
||||||
} else if (qName == "ND") {
|
} else if (qName == "ND") {
|
||||||
string d;
|
string d;
|
||||||
base64_decode((const char*)currentText.trimmed().toAscii(), d);
|
base64_decode((const char*)currentText.trimmed().toAscii(), d);
|
||||||
sd->addDirSpec(d, true);
|
sd->addClause(new SearchDataClausePath(d, true));
|
||||||
} else if (qName == "SD") {
|
} else if (qName == "SD") {
|
||||||
// Closing current search descriptor. Finishing touches...
|
// Closing current search descriptor. Finishing touches...
|
||||||
if (hasdates)
|
if (hasdates)
|
||||||
|
|
|
@ -64,10 +64,11 @@ void WasaQuery::describe(string &desc) const
|
||||||
desc += "NULL";
|
desc += "NULL";
|
||||||
break;
|
break;
|
||||||
case OP_LEAF:
|
case OP_LEAF:
|
||||||
|
if (m_exclude)
|
||||||
|
desc += "NOT (";
|
||||||
desc += fieldspec + m_value;
|
desc += fieldspec + m_value;
|
||||||
break;
|
if (m_exclude)
|
||||||
case OP_EXCL:
|
desc += ")";
|
||||||
desc += string("NOT (" ) + fieldspec + m_value + ") ";
|
|
||||||
break;
|
break;
|
||||||
case OP_OR:
|
case OP_OR:
|
||||||
case OP_AND:
|
case OP_AND:
|
||||||
|
@ -429,11 +430,12 @@ StringToWasaQuery::Internal::stringToQuery(const string& str, string& reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nclause->m_op = WasaQuery::OP_LEAF;
|
||||||
// +- indicator ?
|
// +- indicator ?
|
||||||
if (checkSubMatch(SMI_PM, match, reason) && match[0] == '-') {
|
if (checkSubMatch(SMI_PM, match, reason) && match[0] == '-') {
|
||||||
nclause->m_op = WasaQuery::OP_EXCL;
|
nclause->m_exclude = true;
|
||||||
} else {
|
} else {
|
||||||
nclause->m_op = WasaQuery::OP_LEAF;
|
nclause->m_exclude = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prev_or) {
|
if (prev_or) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ using std::vector;
|
||||||
class WasaQuery {
|
class WasaQuery {
|
||||||
public:
|
public:
|
||||||
/** Type of this element: leaf or AND/OR chain */
|
/** Type of this element: leaf or AND/OR chain */
|
||||||
enum Op {OP_NULL, OP_LEAF, OP_EXCL, OP_OR, OP_AND};
|
enum Op {OP_NULL, OP_LEAF, OP_OR, OP_AND};
|
||||||
/** Relation to be searched between field and value. Recoll actually only
|
/** Relation to be searched between field and value. Recoll actually only
|
||||||
supports "contain" except for a size field */
|
supports "contain" except for a size field */
|
||||||
enum Rel {REL_NULL, REL_EQUALS, REL_CONTAINS, REL_LT, REL_LTE,
|
enum Rel {REL_NULL, REL_EQUALS, REL_CONTAINS, REL_LT, REL_LTE,
|
||||||
|
@ -63,7 +63,8 @@ public:
|
||||||
typedef vector<WasaQuery*> subqlist_t;
|
typedef vector<WasaQuery*> subqlist_t;
|
||||||
|
|
||||||
WasaQuery()
|
WasaQuery()
|
||||||
: m_op(OP_NULL), m_modifiers(0), m_slack(0), m_weight(1.0)
|
: m_op(OP_NULL), m_rel(REL_NULL), m_exclude(false),
|
||||||
|
m_modifiers(0), m_slack(0), m_weight(1.0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~WasaQuery();
|
~WasaQuery();
|
||||||
|
@ -79,6 +80,9 @@ public:
|
||||||
/** Relation between field and value: =, :, <,>,<=, >= */
|
/** Relation between field and value: =, :, <,>,<=, >= */
|
||||||
WasaQuery::Rel m_rel;
|
WasaQuery::Rel m_rel;
|
||||||
|
|
||||||
|
/* Negating flag */
|
||||||
|
bool m_exclude;
|
||||||
|
|
||||||
/* String value. Valid for op == OP_LEAF or EXCL */
|
/* String value. Valid for op == OP_LEAF or EXCL */
|
||||||
string m_value;
|
string m_value;
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,13 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
if (!stringicmp("mime", (*it)->m_fieldspec) ||
|
if (!stringicmp("mime", (*it)->m_fieldspec) ||
|
||||||
!stringicmp("format", (*it)->m_fieldspec)) {
|
!stringicmp("format", (*it)->m_fieldspec)) {
|
||||||
if ((*it)->m_op == WasaQuery::OP_LEAF) {
|
if ((*it)->m_op == WasaQuery::OP_LEAF) {
|
||||||
sdata->addFiletype((*it)->m_value);
|
if ((*it)->m_exclude) {
|
||||||
} else if ((*it)->m_op == WasaQuery::OP_EXCL) {
|
sdata->remFiletype((*it)->m_value);
|
||||||
sdata->remFiletype((*it)->m_value);
|
} else {
|
||||||
|
sdata->addFiletype((*it)->m_value);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reason = "internal error: mime clause neither leaf not excl??";
|
reason = "internal error: mime clause not leaf??";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -78,10 +80,8 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
// categories like "audio", "presentation", etc.
|
// categories like "audio", "presentation", etc.
|
||||||
if (!stringicmp("rclcat", (*it)->m_fieldspec) ||
|
if (!stringicmp("rclcat", (*it)->m_fieldspec) ||
|
||||||
!stringicmp("type", (*it)->m_fieldspec)) {
|
!stringicmp("type", (*it)->m_fieldspec)) {
|
||||||
if ((*it)->m_op != WasaQuery::OP_LEAF &&
|
if ((*it)->m_op != WasaQuery::OP_LEAF) {
|
||||||
(*it)->m_op != WasaQuery::OP_EXCL) {
|
reason = "internal error: rclcat/type clause not leaf??";
|
||||||
reason = "internal error: rclcat/type clause neither leaf"
|
|
||||||
"nor excl??";
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
vector<string> mtypes;
|
vector<string> mtypes;
|
||||||
|
@ -89,10 +89,11 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
&& !mtypes.empty()) {
|
&& !mtypes.empty()) {
|
||||||
for (vector<string>::iterator mit = mtypes.begin();
|
for (vector<string>::iterator mit = mtypes.begin();
|
||||||
mit != mtypes.end(); mit++) {
|
mit != mtypes.end(); mit++) {
|
||||||
if ((*it)->m_op == WasaQuery::OP_LEAF)
|
if ((*it)->m_exclude) {
|
||||||
sdata->addFiletype(*mit);
|
|
||||||
else
|
|
||||||
sdata->remFiletype(*mit);
|
sdata->remFiletype(*mit);
|
||||||
|
} else {
|
||||||
|
sdata->addFiletype(*mit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reason = "Unknown rclcat/type value: no mime types found";
|
reason = "Unknown rclcat/type value: no mime types found";
|
||||||
|
@ -101,14 +102,6 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtering on location
|
|
||||||
if (!stringicmp("dir", (*it)->m_fieldspec)) {
|
|
||||||
string dir = path_tildexpand((*it)->m_value);
|
|
||||||
sdata->addDirSpec(dir, (*it)->m_op == WasaQuery::OP_EXCL,
|
|
||||||
(*it)->m_weight);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle "date" spec
|
// Handle "date" spec
|
||||||
if (!stringicmp("date", (*it)->m_fieldspec)) {
|
if (!stringicmp("date", (*it)->m_fieldspec)) {
|
||||||
if ((*it)->m_op != WasaQuery::OP_LEAF) {
|
if ((*it)->m_op != WasaQuery::OP_LEAF) {
|
||||||
|
@ -181,9 +174,9 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case WasaQuery::OP_LEAF: {
|
case WasaQuery::OP_LEAF: {
|
||||||
LOGDEB0(("wasaQueryToRcl: leaf clause [%s]:[%s] slack %d\n",
|
LOGDEB0(("wasaQueryToRcl: leaf clause [%s:%s] slack %d excl %d\n",
|
||||||
(*it)->m_fieldspec.c_str(), (*it)->m_value.c_str(),
|
(*it)->m_fieldspec.c_str(), (*it)->m_value.c_str(),
|
||||||
(*it)->m_slack));
|
(*it)->m_slack, (*it)->m_exclude));
|
||||||
|
|
||||||
// Change terms found in the "autosuffs" list into "ext"
|
// Change terms found in the "autosuffs" list into "ext"
|
||||||
// field queries
|
// field queries
|
||||||
|
@ -198,23 +191,45 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I'm not sure I understand the phrase/near detection
|
if (!stringicmp("dir", (*it)->m_fieldspec)) {
|
||||||
// thereafter anymore, maybe it would be better to have an
|
// dir filtering special case
|
||||||
// explicit flag. Mods can only be set after a double
|
nclause = new Rcl::SearchDataClausePath((*it)->m_value,
|
||||||
// quote.
|
(*it)->m_exclude);
|
||||||
if (TextSplit::hasVisibleWhite((*it)->m_value) || mods) {
|
} else if ((*it)->m_exclude) {
|
||||||
Rcl::SClType tp = Rcl::SCLT_PHRASE;
|
if (wasa->m_op != WasaQuery::OP_AND) {
|
||||||
if (mods & WasaQuery::WQM_PROX) {
|
LOGERR(("wasaQueryToRcl: excl clause inside OR list!\n"));
|
||||||
tp = Rcl::SCLT_NEAR;
|
continue;
|
||||||
}
|
}
|
||||||
nclause = new Rcl::SearchDataClauseDist(tp, (*it)->m_value,
|
// Note: have to add dquotes which will be translated to
|
||||||
(*it)->m_slack,
|
// phrase if there are several words in there. Not pretty
|
||||||
(*it)->m_fieldspec);
|
// but should work. If there is actually a single
|
||||||
} else {
|
// word, it will not be taken as a phrase, and
|
||||||
nclause = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND,
|
// stem-expansion will work normally
|
||||||
(*it)->m_value,
|
// Have to do this because searchdata has nothing like and_not
|
||||||
|
nclause = new Rcl::SearchDataClauseSimple(Rcl::SCLT_EXCL,
|
||||||
|
string("\"") +
|
||||||
|
(*it)->m_value + "\"",
|
||||||
(*it)->m_fieldspec);
|
(*it)->m_fieldspec);
|
||||||
|
} else {
|
||||||
|
// I'm not sure I understand the phrase/near detection
|
||||||
|
// thereafter anymore, maybe it would be better to have an
|
||||||
|
// explicit flag. Mods can only be set after a double
|
||||||
|
// quote.
|
||||||
|
if (TextSplit::hasVisibleWhite((*it)->m_value) || mods) {
|
||||||
|
Rcl::SClType tp = Rcl::SCLT_PHRASE;
|
||||||
|
if (mods & WasaQuery::WQM_PROX) {
|
||||||
|
tp = Rcl::SCLT_NEAR;
|
||||||
|
}
|
||||||
|
nclause = new Rcl::SearchDataClauseDist(tp, (*it)->m_value,
|
||||||
|
(*it)->m_slack,
|
||||||
|
(*it)->m_fieldspec);
|
||||||
|
} else {
|
||||||
|
nclause = new Rcl::SearchDataClauseSimple(Rcl::SCLT_AND,
|
||||||
|
(*it)->m_value,
|
||||||
|
(*it)->m_fieldspec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nclause == 0) {
|
if (nclause == 0) {
|
||||||
reason = "Out of memory";
|
reason = "Out of memory";
|
||||||
LOGERR(("wasaQueryToRcl: out of memory\n"));
|
LOGERR(("wasaQueryToRcl: out of memory\n"));
|
||||||
|
@ -223,31 +238,6 @@ static Rcl::SearchData *wasaQueryToRcl(const RclConfig *config,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WasaQuery::OP_EXCL:
|
|
||||||
LOGDEB2(("wasaQueryToRcl: excl clause [%s]:[%s]\n",
|
|
||||||
(*it)->m_fieldspec.c_str(), (*it)->m_value.c_str()));
|
|
||||||
if (wasa->m_op != WasaQuery::OP_AND) {
|
|
||||||
LOGERR(("wasaQueryToRcl: negative clause inside OR list!\n"));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Note: have to add dquotes which will be translated to
|
|
||||||
// phrase if there are several words in there. Not pretty
|
|
||||||
// but should work. If there is actually a single
|
|
||||||
// word, it will not be taken as a phrase, and
|
|
||||||
// stem-expansion will work normally
|
|
||||||
// Have to do this because searchdata has nothing like and_not
|
|
||||||
nclause = new Rcl::SearchDataClauseSimple(Rcl::SCLT_EXCL,
|
|
||||||
string("\"") +
|
|
||||||
(*it)->m_value + "\"",
|
|
||||||
(*it)->m_fieldspec);
|
|
||||||
|
|
||||||
if (nclause == 0) {
|
|
||||||
reason = "Out of memory";
|
|
||||||
LOGERR(("wasaQueryToRcl: out of memory\n"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WasaQuery::OP_OR:
|
case WasaQuery::OP_OR:
|
||||||
LOGDEB2(("wasaQueryToRcl: OR clause [%s]:[%s]\n",
|
LOGDEB2(("wasaQueryToRcl: OR clause [%s]:[%s]\n",
|
||||||
(*it)->m_fieldspec.c_str(), (*it)->m_value.c_str()));
|
(*it)->m_fieldspec.c_str(), (*it)->m_value.c_str()));
|
||||||
|
|
|
@ -128,7 +128,7 @@ bool SearchData::clausesToQuery(Rcl::Db &db, SClType tp,
|
||||||
// addClause())
|
// addClause())
|
||||||
Xapian::Query::op op;
|
Xapian::Query::op op;
|
||||||
if (tp == SCLT_AND) {
|
if (tp == SCLT_AND) {
|
||||||
if ((*it)->m_tp == SCLT_EXCL) {
|
if ((*it)->m_tp == SCLT_EXCL || (*it)->getexclude()) {
|
||||||
op = Xapian::Query::OP_AND_NOT;
|
op = Xapian::Query::OP_AND_NOT;
|
||||||
} else {
|
} else {
|
||||||
op = Xapian::Query::OP_AND;
|
op = Xapian::Query::OP_AND;
|
||||||
|
@ -274,36 +274,6 @@ bool SearchData::toNativeQuery(Rcl::Db &db, void *d)
|
||||||
xq = xq.empty() ? tq : Xapian::Query(Xapian::Query::OP_AND_NOT, xq, tq);
|
xq = xq.empty() ? tq : Xapian::Query(Xapian::Query::OP_AND_NOT, xq, tq);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the directory filtering clauses. Each is a phrase of terms
|
|
||||||
// prefixed with the pathelt prefix XP
|
|
||||||
for (vector<DirSpec>::const_iterator dit = m_dirspecs.begin();
|
|
||||||
dit != m_dirspecs.end(); dit++) {
|
|
||||||
vector<string> vpath;
|
|
||||||
stringToTokens(dit->dir, vpath, "/");
|
|
||||||
vector<string> pvpath;
|
|
||||||
if (dit->dir[0] == '/')
|
|
||||||
pvpath.push_back(wrap_prefix(pathelt_prefix));
|
|
||||||
for (vector<string>::const_iterator pit = vpath.begin();
|
|
||||||
pit != vpath.end(); pit++){
|
|
||||||
pvpath.push_back(wrap_prefix(pathelt_prefix) + *pit);
|
|
||||||
}
|
|
||||||
Xapian::Query::op tdop;
|
|
||||||
if (dit->weight == 1.0) {
|
|
||||||
tdop = dit->exclude ?
|
|
||||||
Xapian::Query::OP_AND_NOT : Xapian::Query::OP_FILTER;
|
|
||||||
} else {
|
|
||||||
tdop = dit->exclude ?
|
|
||||||
Xapian::Query::OP_AND_NOT : Xapian::Query::OP_AND_MAYBE;
|
|
||||||
}
|
|
||||||
Xapian::Query tdq = Xapian::Query(Xapian::Query::OP_PHRASE,
|
|
||||||
pvpath.begin(), pvpath.end());
|
|
||||||
if (dit->weight != 1.0)
|
|
||||||
tdq = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
|
|
||||||
tdq, dit->weight);
|
|
||||||
|
|
||||||
xq = Xapian::Query(tdop, xq, tdq);
|
|
||||||
}
|
|
||||||
|
|
||||||
*((Xapian::Query *)d) = xq;
|
*((Xapian::Query *)d) = xq;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -418,7 +388,7 @@ bool SearchData::maybeAddAutoPhrase(Rcl::Db& db, double freqThreshold)
|
||||||
// Add clause to current list. OR lists cant have EXCL clauses.
|
// Add clause to current list. OR lists cant have EXCL clauses.
|
||||||
bool SearchData::addClause(SearchDataClause* cl)
|
bool SearchData::addClause(SearchDataClause* cl)
|
||||||
{
|
{
|
||||||
if (m_tp == SCLT_OR && (cl->m_tp == SCLT_EXCL)) {
|
if (m_tp == SCLT_OR && (cl->m_tp == SCLT_EXCL || cl->getexclude())) {
|
||||||
LOGERR(("SearchData::addClause: cant add EXCL to OR list\n"));
|
LOGERR(("SearchData::addClause: cant add EXCL to OR list\n"));
|
||||||
m_reason = "No Negative (AND_NOT) clauses allowed in OR queries";
|
m_reason = "No Negative (AND_NOT) clauses allowed in OR queries";
|
||||||
return false;
|
return false;
|
||||||
|
@ -438,7 +408,6 @@ void SearchData::erase()
|
||||||
delete *it;
|
delete *it;
|
||||||
m_query.clear();
|
m_query.clear();
|
||||||
m_filetypes.clear();
|
m_filetypes.clear();
|
||||||
m_dirspecs.clear();
|
|
||||||
m_description.erase();
|
m_description.erase();
|
||||||
m_reason.erase();
|
m_reason.erase();
|
||||||
m_haveDates = false;
|
m_haveDates = false;
|
||||||
|
@ -1169,6 +1138,35 @@ bool SearchDataClauseFilename::toNativeQuery(Rcl::Db &db, void *p)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Translate a dir: path filtering clause. See comments in .h
|
||||||
|
bool SearchDataClausePath::toNativeQuery(Rcl::Db &db, void *p)
|
||||||
|
{
|
||||||
|
LOGDEB(("SearchDataClausePath::toNativeQuery: [%s]\n", m_text.c_str()));
|
||||||
|
Xapian::Query *qp = (Xapian::Query *)p;
|
||||||
|
*qp = Xapian::Query();
|
||||||
|
|
||||||
|
if (m_text.empty()) {
|
||||||
|
LOGERR(("SearchDataClausePath: empty path??\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vector<string> vpath;
|
||||||
|
stringToTokens(m_text, vpath, "/");
|
||||||
|
vector<string> pvpath;
|
||||||
|
if (m_text[0] == '/')
|
||||||
|
pvpath.push_back(wrap_prefix(pathelt_prefix));
|
||||||
|
for (vector<string>::const_iterator pit = vpath.begin();
|
||||||
|
pit != vpath.end(); pit++){
|
||||||
|
pvpath.push_back(wrap_prefix(pathelt_prefix) + *pit);
|
||||||
|
}
|
||||||
|
*qp = Xapian::Query(Xapian::Query::OP_PHRASE,
|
||||||
|
pvpath.begin(), pvpath.end());
|
||||||
|
|
||||||
|
if (m_weight != 1.0) {
|
||||||
|
*qp = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, *qp, m_weight);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Translate NEAR or PHRASE clause.
|
// Translate NEAR or PHRASE clause.
|
||||||
bool SearchDataClauseDist::toNativeQuery(Rcl::Db &db, void *p)
|
bool SearchDataClauseDist::toNativeQuery(Rcl::Db &db, void *p)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Rcl {
|
||||||
/** Search clause types */
|
/** Search clause types */
|
||||||
enum SClType {
|
enum SClType {
|
||||||
SCLT_AND,
|
SCLT_AND,
|
||||||
SCLT_OR, SCLT_EXCL, SCLT_FILENAME, SCLT_PHRASE, SCLT_NEAR,
|
SCLT_OR, SCLT_EXCL, SCLT_FILENAME, SCLT_PHRASE, SCLT_NEAR, SCLT_PATH,
|
||||||
SCLT_SUB
|
SCLT_SUB
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ public:
|
||||||
commoninit();
|
commoninit();
|
||||||
}
|
}
|
||||||
SearchData()
|
SearchData()
|
||||||
: m_tp(SCLT_AND), m_stemlang("english")
|
: m_tp(SCLT_AND)
|
||||||
{
|
{
|
||||||
commoninit();
|
commoninit();
|
||||||
}
|
}
|
||||||
|
@ -118,12 +118,6 @@ public:
|
||||||
*/
|
*/
|
||||||
bool maybeAddAutoPhrase(Rcl::Db &db, double threshold);
|
bool maybeAddAutoPhrase(Rcl::Db &db, double threshold);
|
||||||
|
|
||||||
/** Set/get top subdirectory for filtering results */
|
|
||||||
void addDirSpec(const std::string& t, bool excl = false, float w = 1.0)
|
|
||||||
{
|
|
||||||
m_dirspecs.push_back(DirSpec(t, excl, w));
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& getStemLang() {return m_stemlang;}
|
const std::string& getStemLang() {return m_stemlang;}
|
||||||
|
|
||||||
void setMinSize(size_t size) {m_minSize = size;}
|
void setMinSize(size_t size) {m_minSize = size;}
|
||||||
|
@ -182,20 +176,6 @@ private:
|
||||||
// Excluded set of file types if not empty
|
// Excluded set of file types if not empty
|
||||||
std::vector<std::string> m_nfiletypes;
|
std::vector<std::string> m_nfiletypes;
|
||||||
|
|
||||||
// Restrict to subtree or exclude one
|
|
||||||
class DirSpec {
|
|
||||||
public:
|
|
||||||
std::string dir;
|
|
||||||
bool exclude;
|
|
||||||
// For positive spec: affect weight instead of filter
|
|
||||||
float weight;
|
|
||||||
DirSpec(const std::string&d, bool x, float w)
|
|
||||||
: dir(d), exclude(x), weight(w)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::vector<DirSpec> m_dirspecs;
|
|
||||||
|
|
||||||
bool m_haveDates;
|
bool m_haveDates;
|
||||||
DateInterval m_dates; // Restrict to date interval
|
DateInterval m_dates; // Restrict to date interval
|
||||||
size_t m_maxSize;
|
size_t m_maxSize;
|
||||||
|
@ -240,7 +220,7 @@ public:
|
||||||
|
|
||||||
SearchDataClause(SClType tp)
|
SearchDataClause(SClType tp)
|
||||||
: m_tp(tp), m_parentSearch(0), m_haveWildCards(0),
|
: m_tp(tp), m_parentSearch(0), m_haveWildCards(0),
|
||||||
m_modifiers(SDCM_NONE), m_weight(1.0)
|
m_modifiers(SDCM_NONE), m_weight(1.0), m_exclude(false)
|
||||||
{}
|
{}
|
||||||
virtual ~SearchDataClause() {}
|
virtual ~SearchDataClause() {}
|
||||||
virtual bool toNativeQuery(Rcl::Db &db, void *) = 0;
|
virtual bool toNativeQuery(Rcl::Db &db, void *) = 0;
|
||||||
|
@ -299,8 +279,12 @@ public:
|
||||||
{
|
{
|
||||||
m_weight = w;
|
m_weight = w;
|
||||||
}
|
}
|
||||||
friend class SearchData;
|
virtual bool getexclude() const
|
||||||
|
{
|
||||||
|
return m_exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class SearchData;
|
||||||
protected:
|
protected:
|
||||||
std::string m_reason;
|
std::string m_reason;
|
||||||
SClType m_tp;
|
SClType m_tp;
|
||||||
|
@ -308,6 +292,7 @@ protected:
|
||||||
bool m_haveWildCards;
|
bool m_haveWildCards;
|
||||||
Modifier m_modifiers;
|
Modifier m_modifiers;
|
||||||
float m_weight;
|
float m_weight;
|
||||||
|
bool m_exclude;
|
||||||
private:
|
private:
|
||||||
SearchDataClause(const SearchDataClause&)
|
SearchDataClause(const SearchDataClause&)
|
||||||
{
|
{
|
||||||
|
@ -404,6 +389,54 @@ protected:
|
||||||
std::string m_text;
|
std::string m_text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pathname filtering clause. This is special because of history:
|
||||||
|
* - Pathname filtering used to be performed as a post-processing step
|
||||||
|
* done with the url fields of doc data records.
|
||||||
|
* - Then it was done as special phrase searchs on path elements prefixed
|
||||||
|
* with XP.
|
||||||
|
* Up to this point dir filtering data was stored as part of the searchdata
|
||||||
|
* object, not in the SearchDataClause tree. Only one, then a list,
|
||||||
|
* of clauses where stored, and they were always ANDed together.
|
||||||
|
*
|
||||||
|
* In order to allow for OR searching, dir clauses are now stored in a
|
||||||
|
* specific SearchDataClause, but this is still special because the field has
|
||||||
|
* non-standard phrase-like processing, reflected in index storage by
|
||||||
|
* an empty element representing / (as "XP").
|
||||||
|
*
|
||||||
|
* A future version should use a standard phrase with an anchor to the
|
||||||
|
* start if the path starts with /. As this implies an index format
|
||||||
|
* change but is no important enough to warrant it, this has to wait for
|
||||||
|
* the next format change.
|
||||||
|
*/
|
||||||
|
class SearchDataClausePath : public SearchDataClause {
|
||||||
|
public:
|
||||||
|
SearchDataClausePath(const std::string& txt, bool excl = false)
|
||||||
|
: SearchDataClause(SCLT_PATH), m_text(txt)
|
||||||
|
{
|
||||||
|
m_exclude = excl;
|
||||||
|
m_haveWildCards = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~SearchDataClausePath()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void getTerms(HighlightData&) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool toNativeQuery(Rcl::Db &, void *);
|
||||||
|
virtual const std::string& gettext() const
|
||||||
|
{
|
||||||
|
return m_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_text;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A clause coming from a NEAR or PHRASE entry field. There is only one
|
* A clause coming from a NEAR or PHRASE entry field. There is only one
|
||||||
* std::string group, and a specified distance, which applies to it.
|
* std::string group, and a specified distance, which applies to it.
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Handle translation from rcl's SearchData structures to Xapian Queries
|
// Handle translation from rcl's SearchData structures to XML. Used for
|
||||||
|
// complex search history storage in the GUI
|
||||||
|
|
||||||
#include "autoconfig.h"
|
#include "autoconfig.h"
|
||||||
|
|
||||||
|
@ -64,6 +65,20 @@ string SearchData::asXML()
|
||||||
LOGERR(("SearchData::asXML: can't do subclauses !\n"));
|
LOGERR(("SearchData::asXML: can't do subclauses !\n"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
//if (c->getexclude())
|
||||||
|
// os << "<NEG/>" << endl;
|
||||||
|
if (c->getTp() == SCLT_PATH) {
|
||||||
|
// Keep these apart, for compat with the older history format
|
||||||
|
SearchDataClausePath *cl =
|
||||||
|
dynamic_cast<SearchDataClausePath*>(c);
|
||||||
|
if (cl->getexclude()) {
|
||||||
|
os << "<ND>" << base64_encode(cl->gettext()) << "</ND>" << endl;
|
||||||
|
} else {
|
||||||
|
os << "<YD>" << base64_encode(cl->gettext()) << "</YD>" << endl;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
SearchDataClauseSimple *cl =
|
SearchDataClauseSimple *cl =
|
||||||
dynamic_cast<SearchDataClauseSimple*>(c);
|
dynamic_cast<SearchDataClauseSimple*>(c);
|
||||||
os << "<C>" << endl;
|
os << "<C>" << endl;
|
||||||
|
@ -100,7 +115,6 @@ string SearchData::asXML()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (m_minSize != size_t(-1)) {
|
if (m_minSize != size_t(-1)) {
|
||||||
os << "<MIS>" << m_minSize << "</MIS>" << endl;
|
os << "<MIS>" << m_minSize << "</MIS>" << endl;
|
||||||
}
|
}
|
||||||
|
@ -126,14 +140,6 @@ string SearchData::asXML()
|
||||||
os << "</IT>" << endl;
|
os << "</IT>" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (vector<DirSpec>::const_iterator dit = m_dirspecs.begin();
|
|
||||||
dit != m_dirspecs.end(); dit++) {
|
|
||||||
if (dit->exclude) {
|
|
||||||
os << "<ND>" << base64_encode(dit->dir) << "</ND>" << endl;
|
|
||||||
} else {
|
|
||||||
os << "<YD>" << base64_encode(dit->dir) << "</YD>" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os << "</SD>";
|
os << "</SD>";
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,18 +57,18 @@ bool StemDb::stemExpand(const std::string& langs, const std::string& term,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef RCL_INDEX_STRIPCHARS
|
#ifndef RCL_INDEX_STRIPCHARS
|
||||||
// Expand the unaccented stem
|
|
||||||
if (!o_index_stripchars) {
|
if (!o_index_stripchars) {
|
||||||
string unac;
|
string unac;
|
||||||
unacmaybefold(term, unac, "UTF-8", UNACOP_UNAC);
|
unacmaybefold(term, unac, "UTF-8", UNACOP_UNAC);
|
||||||
if (term != unac) {
|
// Expand the unaccented stem, using the unaccented stem
|
||||||
for (vector<string>::const_iterator it = llangs.begin();
|
// db. Because it's a different db, We need to do it even if
|
||||||
it != llangs.end(); it++) {
|
// the input has no accent (unac == term)
|
||||||
SynTermTransStem stemmer(*it);
|
for (vector<string>::const_iterator it = llangs.begin();
|
||||||
XapComputableSynFamMember expander(getdb(), synFamStemUnac,
|
it != llangs.end(); it++) {
|
||||||
*it, &stemmer);
|
SynTermTransStem stemmer(*it);
|
||||||
(void)expander.synExpand(unac, result);
|
XapComputableSynFamMember expander(getdb(), synFamStemUnac,
|
||||||
}
|
*it, &stemmer);
|
||||||
|
(void)expander.synExpand(unac, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue