Commit 85b443dd authored by Till Adam's avatar Till Adam

Improve contains queries.

If we are searching for 2 characters or less, we assume substring
matches anywhere don't make much sense, so we use whole word matching
via the relatively fast bif:contains. If we are looking for longer
substrings, we can search for the substring anywhere, using a regex
filter, or for the substring at the start of words, using a faster regex
that limits to word boundaries. The later is useful in particul for
address completion, which is a frequent enough special case to warrant
optimizing it a bit.

REVIEW: 104338
parent ac470f5c
......@@ -65,11 +65,25 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value )
setQuery( criterion, value, ExactMatch );
}
// helper method, returns the SPARQL sub-expression to be used for finding
// string either as a whole word, the start of a word, or anywhere in a word
static QString containsQueryString( bool doWholeWordSearch, bool matchWordBoundary )
{
if ( doWholeWordSearch )
return QString::fromLatin1( "?v bif:contains \"'%1'\" . " );
else
return QString::fromLatin1("FILTER regex(str(?v), \"%1\", \"i\")" )
.arg( matchWordBoundary? "\\\\b%1" : "%1" );
}
void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Match match )
{
if ( match == StartsWithMatch && value.size() < 4 )
match = ExactMatch;
const bool doWholeWordSearch = value.size() < 3;
const bool matchWordBoundary = match == ContainsWordBoundaryMatch;
QString query;
#ifndef AKONADI_USE_STRIGI_SEARCH
......@@ -295,7 +309,7 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?person a nco:PersonContact ; "
" nco:hasEmailAddress ?email . "
" ?email nco:emailAddress ?v . "
" ?v bif:contains \"'%1*'\" . "
" FILTER regex(str(?v), \"^%1\", \"i\") "
" } "
"}"
#endif
......@@ -324,7 +338,7 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?r <" + akonadiItemIdUri().toEncoded() + "> ?reqProp1 . "
" ?r a nco:PersonContact . "
" ?r nco:nickname ?v . "
" ?v bif:contains \"'%1*'\" . "
" FILTER regex(str(?v), \"^%1\", \"i\") "
" } "
"}"
#endif
......@@ -367,17 +381,17 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?r <" + akonadiItemIdUri().toEncoded() + "> ?reqProp1 . "
" ?r a nco:PersonContact . "
" { ?r nco:fullname ?v . "
" ?v bif:contains \"'%1*'\" . } "
" FILTER regex(str(?v), \"^%1\", \"i\") } "
" UNION "
" { ?r nco:nameGiven ?v . "
" ?v bif:contains \"'%1*'\" . } "
" FILTER regex(str(?v), \"^%1\", \"i\") } "
" UNION "
" { ?r nco:nameFamily ?v . "
" ?v bif:contains \"'%1*'\" . } "
" FILTER regex(str(?v), \"^%1\", \"i\") } "
" UNION "
" { ?r nco:hasEmailAddress ?email . "
" ?email nco:emailAddress ?v . "
" ?v bif:contains \"'%1*'\" . } "
" FILTER regex(str(?v), \"^%1\", \"i\") } "
" } "
"}"
#endif
......@@ -412,7 +426,7 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
#endif
);
}
} else if ( match == ContainsMatch ) {
} else if ( match == ContainsMatch || match == ContainsWordBoundaryMatch ) {
if ( criterion == Name ) {
query += QString::fromLatin1(
#ifdef AKONADI_USE_STRIGI_SEARCH
......@@ -437,11 +451,12 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?r <" + akonadiItemIdUri().toEncoded() + "> ?reqProp1 . "
" ?r a nco:PersonContact . "
" ?r nco:fullname ?v . "
" ?v bif:contains \"'%1'\" . "
"%1"
" } "
"} "
#endif
);
query = query.arg( containsQueryString( doWholeWordSearch, matchWordBoundary ) );
} else if ( criterion == Email ) {
query += QString::fromLatin1(
#ifdef AKONADI_USE_STRIGI_SEARCH
......@@ -467,11 +482,12 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?person a nco:PersonContact ; "
" nco:hasEmailAddress ?email . "
" ?email nco:emailAddress ?v . "
" ?v bif:contains \"'%1'\" . "
"%1"
" } "
"}"
#endif
);
query = query.arg( containsQueryString( doWholeWordSearch, matchWordBoundary ) );
} else if ( criterion == NickName ) {
query += QString::fromLatin1(
#ifdef AKONADI_USE_STRIGI_SEARCH
......@@ -496,11 +512,12 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?r <" + akonadiItemIdUri().toEncoded() + "> ?reqProp1 . "
" ?r a nco:PersonContact . "
" ?r nco:nickname ?v . "
" ?v bif:contains \"'%1'\" . "
"%1"
" } "
"}"
#endif
);
query = query.arg( containsQueryString( doWholeWordSearch, matchWordBoundary ) );
} else if ( criterion == NameOrEmail ) {
query += QString::fromLatin1(
#ifdef AKONADI_USE_STRIGI_SEARCH
......@@ -539,21 +556,22 @@ void ContactSearchJob::setQuery( Criterion criterion, const QString &value, Matc
" ?r <" + akonadiItemIdUri().toEncoded() + "> ?reqProp1 . "
" ?r a nco:PersonContact . "
" { ?r nco:fullname ?v . "
" ?v bif:contains \"'%1'\" . } "
"%1 }"
" UNION "
" { ?r nco:nameGiven ?v . "
" ?v bif:contains \"'%1'\" . } "
"%1 }"
" UNION "
" { ?r nco:nameFamily ?v . "
" ?v bif:contains \"'%1'\" . } "
"%1 }"
" UNION "
" { ?r nco:hasEmailAddress ?email . "
" ?email nco:emailAddress ?v . "
" ?v bif:contains \"'%1'\" . } "
"%1 }"
" } "
"}"
#endif
);
query = query.arg( containsQueryString( doWholeWordSearch, matchWordBoundary ) );
} else if ( criterion == ContactUid ) {
query += QString::fromLatin1(
#ifdef AKONADI_USE_STRIGI_SEARCH
......
......@@ -114,7 +114,8 @@ class AKONADI_CONTACT_EXPORT ContactSearchJob : public ItemSearchJob
{
ExactMatch, ///< The result must match exactly the pattern (case sensitive).
StartsWithMatch, ///< The result must start with the pattern (case insensitive).
ContainsMatch ///< The result must contain the pattern (case insensitive).
ContainsMatch, ///< The result must contain the pattern (case insensitive).
ContainsWordBoundaryMatch ///< The result must contain a word starting with the pattern (case insensitive).
};
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment