diff --git a/autotests/unit/engine/queryparsertest.cpp b/autotests/unit/engine/queryparsertest.cpp --- a/autotests/unit/engine/queryparsertest.cpp +++ b/autotests/unit/engine/queryparsertest.cpp @@ -23,6 +23,8 @@ #include +Q_DECLARE_METATYPE(Baloo::EngineQuery) + using namespace Baloo; class QueryParserTest : public QObject @@ -41,15 +43,17 @@ void testUnderscoreSplitting(); void testAutoExpand(); void testUnicodeLowering(); + void testMixedDelimiters(); + void testMixedDelimiters_data(); }; void QueryParserTest::testSinglePrefixWord() { QueryParser parser; parser.setAutoExapandSize(1); EngineQuery query = parser.parseQuery("The", "F"); - EngineQuery q("Fthe", EngineQuery::StartsWith, 1); + EngineQuery q("Fthe", EngineQuery::StartsWith); QCOMPARE(query, q); } @@ -61,12 +65,12 @@ EngineQuery query = parser.parseQuery("The song of Ice and Fire"); QVector queries; - queries << EngineQuery("the", EngineQuery::StartsWith, 1); - queries << EngineQuery("song", EngineQuery::StartsWith, 2); - queries << EngineQuery("of", EngineQuery::StartsWith, 3); - queries << EngineQuery("ice", EngineQuery::StartsWith, 4); - queries << EngineQuery("and", EngineQuery::StartsWith, 5); - queries << EngineQuery("fire", EngineQuery::StartsWith, 6); + queries << EngineQuery("the", EngineQuery::StartsWith); + queries << EngineQuery("song", EngineQuery::StartsWith); + queries << EngineQuery("of", EngineQuery::StartsWith); + queries << EngineQuery("ice", EngineQuery::StartsWith); + queries << EngineQuery("and", EngineQuery::StartsWith); + queries << EngineQuery("fire", EngineQuery::StartsWith); EngineQuery q(queries, EngineQuery::And); QCOMPARE(query, q); @@ -79,14 +83,14 @@ EngineQuery query = parser.parseQuery("The \"song of Ice\" Fire"); QVector phraseQueries; - phraseQueries << EngineQuery("song", 2); - phraseQueries << EngineQuery("of", 3); - phraseQueries << EngineQuery("ice", 4); + phraseQueries << EngineQuery("song"); + phraseQueries << EngineQuery("of"); + phraseQueries << EngineQuery("ice"); QVector queries; - queries << EngineQuery("the", EngineQuery::StartsWith, 1); + queries << EngineQuery("the", EngineQuery::StartsWith); queries << EngineQuery(phraseQueries, EngineQuery::Phrase); - queries << EngineQuery("fire", EngineQuery::StartsWith, 5); + queries << EngineQuery("fire", EngineQuery::StartsWith); EngineQuery q(queries, EngineQuery::And); QCOMPARE(query, q); @@ -99,8 +103,8 @@ EngineQuery query = parser.parseQuery("/opt/pro"); QVector queries; - queries << EngineQuery("opt", 1); - queries << EngineQuery("pro", 2); + queries << EngineQuery("opt"); + queries << EngineQuery("pro"); EngineQuery q(queries, EngineQuery::Phrase); QCOMPARE(query, q); @@ -113,9 +117,9 @@ EngineQuery query = parser.parseQuery("foo_bar.png"); QVector queries; - queries << EngineQuery("foo", 1); - queries << EngineQuery("bar", 2); - queries << EngineQuery("png", 3); + queries << EngineQuery("foo"); + queries << EngineQuery("bar"); + queries << EngineQuery("png"); EngineQuery q(queries, EngineQuery::Phrase); QCOMPARE(query, q); @@ -129,12 +133,12 @@ EngineQuery query = parser.parseQuery("The \"song of Ice' and Fire"); QVector queries; - queries << EngineQuery("the", EngineQuery::StartsWith, 1); - queries << EngineQuery("song", EngineQuery::StartsWith, 2); - queries << EngineQuery("of", EngineQuery::StartsWith, 3); - queries << EngineQuery("ice", EngineQuery::StartsWith, 4); - queries << EngineQuery("and", EngineQuery::StartsWith, 5); - queries << EngineQuery("fire", EngineQuery::StartsWith, 6); + queries << EngineQuery("the", EngineQuery::StartsWith); + queries << EngineQuery("song", EngineQuery::StartsWith); + queries << EngineQuery("of", EngineQuery::StartsWith); + queries << EngineQuery("ice", EngineQuery::StartsWith); + queries << EngineQuery("and", EngineQuery::StartsWith); + queries << EngineQuery("fire", EngineQuery::StartsWith); EngineQuery q(queries, EngineQuery::And); @@ -148,14 +152,14 @@ EngineQuery query = parser.parseQuery("The song@ice.com Fire"); QVector phraseQueries; - phraseQueries << EngineQuery("song", 2); - phraseQueries << EngineQuery("ice", 3); - phraseQueries << EngineQuery("com", 4); + phraseQueries << EngineQuery("song"); + phraseQueries << EngineQuery("ice"); + phraseQueries << EngineQuery("com"); QVector queries; - queries << EngineQuery("the", EngineQuery::StartsWith, 1); + queries << EngineQuery("the", EngineQuery::StartsWith); queries << EngineQuery(phraseQueries, EngineQuery::Phrase); - queries << EngineQuery("fire", EngineQuery::StartsWith, 5); + queries << EngineQuery("fire", EngineQuery::StartsWith); EngineQuery q(queries, EngineQuery::And); QCOMPARE(query, q); @@ -166,7 +170,7 @@ QueryParser parser; EngineQuery query = parser.parseQuery(QString::fromUtf8("s\xC3\xB3ng")); // sóng - EngineQuery q("song", EngineQuery::StartsWith, 1); + EngineQuery q("song", EngineQuery::StartsWith); QCOMPARE(query, q); } @@ -178,15 +182,15 @@ EngineQuery query = parser.parseQuery("The_Fire"); QVector queries; - queries << EngineQuery("the", 1); - queries << EngineQuery("fire", 2); + queries << EngineQuery("the"); + queries << EngineQuery("fire"); EngineQuery q(queries, EngineQuery::Phrase); QCOMPARE(query, q); query = parser.parseQuery("_Fire"); - q = EngineQuery("fire", EngineQuery::StartsWith, 1); + q = EngineQuery("fire", EngineQuery::StartsWith); QCOMPARE(query, q); } @@ -200,8 +204,8 @@ EngineQuery query = parser.parseQuery("the fire"); QVector queries; - queries << EngineQuery("the", EngineQuery::Equal, 1); - queries << EngineQuery("fire", EngineQuery::Equal, 2); + queries << EngineQuery("the", EngineQuery::Equal); + queries << EngineQuery("fire", EngineQuery::Equal); EngineQuery q(queries, EngineQuery::And); @@ -212,8 +216,8 @@ EngineQuery query = parser.parseQuery("'the fire"); QVector queries; - queries << EngineQuery("the", EngineQuery::Equal, 1); - queries << EngineQuery("fire", EngineQuery::Equal, 2); + queries << EngineQuery("the", EngineQuery::Equal); + queries << EngineQuery("fire", EngineQuery::Equal); EngineQuery q(queries, EngineQuery::And); @@ -225,8 +229,8 @@ EngineQuery query = parser.parseQuery("the fire"); QVector queries; - queries << EngineQuery("the", EngineQuery::Equal, 1); - queries << EngineQuery("fire", EngineQuery::StartsWith, 2); + queries << EngineQuery("the", EngineQuery::Equal); + queries << EngineQuery("fire", EngineQuery::StartsWith); EngineQuery q(queries, EngineQuery::And); @@ -241,10 +245,84 @@ QueryParser parser; EngineQuery query = parser.parseQuery(str); - EngineQuery expected = EngineQuery("hedge", EngineQuery::StartsWith, 1); + EngineQuery expected = EngineQuery("hedge", EngineQuery::StartsWith); QCOMPARE(query, expected); } +void QueryParserTest::testMixedDelimiters() +{ + QFETCH(QString, input); + QFETCH(EngineQuery, expectedQuery); + QFETCH(QString, failureReason); + + QueryParser parser; + parser.setAutoExapandSize(0); + EngineQuery query = parser.parseQuery(input); + if (!failureReason.isEmpty()) { + QEXPECT_FAIL("", qPrintable(failureReason), Continue); + } + QCOMPARE(query, expectedQuery); +} + +void QueryParserTest::testMixedDelimiters_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedQuery"); + QTest::addColumn("failureReason"); + + auto addRow = [](const QString& input, const EngineQuery& query, + const QString& failureReason) + { QTest::addRow("%s", qPrintable(input)) << input << query << failureReason; }; + + addRow("Term", {"term"}, ""); + addRow("No phrase", { {{"no"}, {"phrase"}}, EngineQuery::And}, ""); + addRow("Underscore_phrase", { {{"underscore"}, {"phrase"}}, EngineQuery::Phrase}, ""); + addRow("underscore_dot.phrase", { {{"underscore"}, {"dot"}, {"phrase"}}, EngineQuery::Phrase}, ""); + addRow("\'Quoted phrase\'", { {{"quoted"}, {"phrase"}}, EngineQuery::Phrase}, "End quote as last char"); + addRow("\'Quoted phrase\' anded tail", {{ + {{{"quoted"}, {"phrase"}}, EngineQuery::Phrase}, + {"anded"}, {"tail"}, + }, EngineQuery::And}, ""); + addRow("\'Long quoted phrase\'", { {{"long"}, {"quoted"}, {"phrase"}}, EngineQuery::Phrase}, "End quote as last char"); + addRow("Anded dot.phrase", { { + {"anded"}, + {{{"dot"}, {"phrase"}}, EngineQuery::Phrase}, + }, EngineQuery::And}, ""); + addRow("Under_score dot.phrase", {{ + {{{"under"}, {"score"}}, EngineQuery::Phrase}, + {{{"dot"}, {"phrase"}}, EngineQuery::Phrase}, + }, EngineQuery::And}, ""); + addRow("\'One quoted\' Other.withDot", {{ + {{{"one"}, {"quoted"}}, EngineQuery::Phrase}, + {{{"other"}, {"withdot"}}, EngineQuery::Phrase}, + }, EngineQuery::And}, ""); + addRow("\'One quoted with.dot\'", {{ + {"one"}, {"quoted"}, {"with"}, {"dot"} + }, EngineQuery::Phrase}, "End quote as last char"); + addRow("\'Quoted_underscore and.dot\'", {{ + {"quoted"}, {"underscore"}, {"and"}, {"dot"} + }, EngineQuery::Phrase}, "End quote as last char"); + addRow("Underscore_andTrailingDot_.", {{ + {"underscore"}, {"andtrailingdot"} + }, EngineQuery::Phrase}, ""); + addRow("\'TrailingUnderscore_ andDot.\'", {{ + {"trailingunderscore"}, {"anddot"} + }, EngineQuery::Phrase}, "End quote as last char"); + addRow("NoPhrase Under_score \'Quoted Phrase\'", {{ + {"nophrase"}, + {{{"under"}, {"score"}}, EngineQuery::Phrase}, + {{{"quoted"}, {"phrase"}}, EngineQuery::Phrase}, + }, EngineQuery::And}, "End quote as last char"); + addRow("NoPhrase \'Quoted Phrase\' Under_score", {{ + {"nophrase"}, + {{{"quoted"}, {"phrase"}}, EngineQuery::Phrase}, + {{{"under"}, {"score"}}, EngineQuery::Phrase}, + }, EngineQuery::And}, ""); + addRow("\'DegeneratedQuotedPhrase\' Anded text", { + {{"degeneratedquotedphrase"}, {"anded"}, {"text"}}, EngineQuery::And + }, "Single term in quotes is no phrase"); +} + QTEST_MAIN(QueryParserTest) #include "queryparsertest.moc" diff --git a/src/engine/enginequery.h b/src/engine/enginequery.h --- a/src/engine/enginequery.h +++ b/src/engine/enginequery.h @@ -41,18 +41,13 @@ }; EngineQuery(); - EngineQuery(const QByteArray& term, int pos = 0); - EngineQuery(const QByteArray& term, Operation op, int pos = 0); - EngineQuery(const QVector &subQueries, Operation op); + EngineQuery(const QByteArray& term, Operation op = Equal); + EngineQuery(const QVector &subQueries, Operation op = And); QByteArray term() const { return m_term; } - int pos() const { - return m_pos; - } - Operation op() const { return m_op; } @@ -74,30 +69,32 @@ } bool operator ==(const EngineQuery& q) const { - return m_term == q.m_term && m_pos == q.m_pos && m_op == q.m_op && m_subQueries == q.m_subQueries; + return m_term == q.m_term && m_op == q.m_op && m_subQueries == q.m_subQueries; } private: QByteArray m_term; - int m_pos; Operation m_op; QVector m_subQueries; }; } // namespace Baloo -inline QDebug operator << (QDebug d, const Baloo::EngineQuery& q) { +inline QDebug operator<<(QDebug d, const Baloo::EngineQuery& q) +{ QDebugStateSaver state(d); d.setAutoInsertSpaces(false); + + using Operation = Baloo::EngineQuery::Operation; if (q.op() == Baloo::EngineQuery::And) { d << "[AND " << q.subQueries() << "]"; } else if (q.op() == Baloo::EngineQuery::Or) { d << "[OR " << q.subQueries() << "]"; } else if (q.op() == Baloo::EngineQuery::Phrase) { d << "[PHRASE " << q.subQueries() << "]"; } else { Q_ASSERT(q.subQueries().isEmpty()); - d << "(" << q.term() << "," << q.pos() << "," << q.op() << ")"; + return d << q.term() << (q.op() == Operation::StartsWith ? ".." : ""); } return d; } diff --git a/src/engine/enginequery.cpp b/src/engine/enginequery.cpp --- a/src/engine/enginequery.cpp +++ b/src/engine/enginequery.cpp @@ -20,31 +20,23 @@ #include "enginequery.h" -using namespace Baloo; +namespace Baloo { EngineQuery::EngineQuery() - : m_pos(0) - , m_op(Equal) + : m_op(Equal) { } -EngineQuery::EngineQuery(const QByteArray& term, int pos) +EngineQuery::EngineQuery(const QByteArray& term, EngineQuery::Operation op) : m_term(term) - , m_pos(pos) - , m_op(Equal) -{ -} - -EngineQuery::EngineQuery(const QByteArray& term, EngineQuery::Operation op, int pos) - : m_term(term) - , m_pos(pos) , m_op(op) { } EngineQuery::EngineQuery(const QVector &subQueries, Operation op) - : m_pos(0) - , m_op(op) + : m_op(op) , m_subQueries(subQueries) { } + +} // namespace Baloo diff --git a/src/engine/queryparser.cpp b/src/engine/queryparser.cpp --- a/src/engine/queryparser.cpp +++ b/src/engine/queryparser.cpp @@ -54,7 +54,6 @@ int start = 0; int end = 0; - int position = 0; bool inDoubleQuotes = false; bool inSingleQuotes = false; @@ -126,15 +125,14 @@ const QString term = prefix + str; const QByteArray arr = term.toUtf8(); - position++; if (inDoubleQuotes || inSingleQuotes || inPhrase) { - phraseQueries << EngineQuery(arr, position); + phraseQueries << EngineQuery(arr, EngineQuery::Equal); } else { if (m_autoExpandSize && arr.size() >= m_autoExpandSize) { - queries << EngineQuery(arr, EngineQuery::StartsWith, position); + queries << EngineQuery(arr, EngineQuery::StartsWith); } else { - queries << EngineQuery(arr, position); + queries << EngineQuery(arr, EngineQuery::Equal); } } } diff --git a/src/lib/searchstore.cpp b/src/lib/searchstore.cpp --- a/src/lib/searchstore.cpp +++ b/src/lib/searchstore.cpp @@ -362,15 +362,14 @@ const QByteArrayList terms = TermGenerator::termList(value); QVector queries; - int position = 1; for (const QByteArray& term : terms) { QByteArray arr = prefix + term; // FIXME - compatibility hack, to find truncated terms with old // DBs, remove on next DB bump if (arr.size() > 25) { - queries << EngineQuery(arr.left(25), EngineQuery::StartsWith, position++); + queries << EngineQuery(arr.left(25), EngineQuery::StartsWith); } else { - queries << EngineQuery(arr, position++); + queries << EngineQuery(arr); } }