From 3d5fd8f585c0000f875c3db5652b62389c7c5eb4 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Tue, 13 Apr 2021 00:51:53 +0400 Subject: [PATCH] Catalog filtering by creator works via Xapian --- include/library.h | 3 +++ src/library.cpp | 23 ++++++++++++++++++++++- test/library.cpp | 34 +++++++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/include/library.h b/include/library.h index 66e8b7d2c..adb47498d 100644 --- a/include/library.h +++ b/include/library.h @@ -123,6 +123,9 @@ class Filter { bool hasPublisher() const; const std::string& getPublisher() const { return _publisher; } + bool hasCreator() const; + const std::string& getCreator() const { return _creator; } + private: friend class Library; diff --git a/src/library.cpp b/src/library.cpp index 72a947068..0f945dc54 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -281,12 +281,14 @@ void Library::updateBookDB(const Book& book) const std::string name = book.getName(); // this is supposed to be normalized const std::string category = book.getCategory(); // this is supposed to be normalized const std::string publisher = normalizeText(book.getPublisher()); + const std::string creator = normalizeText(book.getCreator()); doc.add_value(0, title); doc.add_value(1, desc); doc.add_value(2, name); doc.add_value(3, category); doc.add_value(4, lang); doc.add_value(5, publisher); + doc.add_value(6, creator); doc.set_data(book.getId()); indexer.index_text(title, 1, "S"); @@ -295,6 +297,7 @@ void Library::updateBookDB(const Book& book) indexer.index_text(category, 1, "XC"); indexer.index_text(lang, 1, "L"); indexer.index_text(publisher, 1, "XP"); + indexer.index_text(creator, 1, "A"); // Index fields without prefixes for general search indexer.index_text(title); @@ -331,6 +334,7 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter) queryParser.add_prefix("category", "XC"); queryParser.add_prefix("lang", "L"); queryParser.add_prefix("publisher", "XP"); + queryParser.add_prefix("creator", "A"); const auto partialQueryFlag = filter.queryIsPartial() ? Xapian::QueryParser::FLAG_PARTIAL : 0; @@ -371,6 +375,16 @@ Xapian::Query publisherQuery(const std::string& publisher) return Xapian::Query(Xapian::Query::OP_PHRASE, q.get_terms_begin(), q.get_terms_end(), q.get_length()); } +Xapian::Query creatorQuery(const std::string& creator) +{ + Xapian::QueryParser queryParser; + queryParser.set_default_op(Xapian::Query::OP_OR); + queryParser.set_stemming_strategy(Xapian::QueryParser::STEM_NONE); + const auto flags = 0; + const auto q = queryParser.parse_query(normalizeText(creator), flags, "A"); + return Xapian::Query(Xapian::Query::OP_PHRASE, q.get_terms_begin(), q.get_terms_end(), q.get_length()); +} + Xapian::Query buildXapianQuery(const Filter& filter) { auto q = buildXapianQueryFromFilterQuery(filter); @@ -386,6 +400,9 @@ Xapian::Query buildXapianQuery(const Filter& filter) if ( filter.hasPublisher() ) { q = Xapian::Query(Xapian::Query::OP_AND, q, publisherQuery(filter.getPublisher())); } + if ( filter.hasCreator() ) { + q = Xapian::Query(Xapian::Query::OP_AND, q, creatorQuery(filter.getCreator())); + } return q; } @@ -698,6 +715,11 @@ bool Filter::hasPublisher() const return ACTIVE(_PUBLISHER); } +bool Filter::hasCreator() const +{ + return ACTIVE(_CREATOR); +} + bool Filter::accept(const Book& book) const { auto local = !book.getPath().empty(); @@ -713,7 +735,6 @@ bool Filter::accept(const Book& book) const FILTER(_NOREMOTE, !remote) FILTER(MAXSIZE, book.getSize() <= _maxSize) - FILTER(_CREATOR, book.getCreator() == _creator) if (ACTIVE(ACCEPTTAGS)) { if (!_acceptTags.empty()) { diff --git a/test/library.cpp b/test/library.cpp index 40da24942..e48564b32 100644 --- a/test/library.cpp +++ b/test/library.cpp @@ -472,18 +472,42 @@ TEST_F(LibraryTest, filterByCreator) "Granblue Fantasy Wiki" ); - // filtering by creator is case sensitive - EXPECT_FILTER_RESULTS(kiwix::Filter().creator("wiki"), - /* no results */ + // filtering by creator is case and diacritics insensitive + EXPECT_FILTER_RESULTS(kiwix::Filter().creator("wIkï"), + "Granblue Fantasy Wiki" ); - // filtering by creator requires full match of the full author/creator name + // filtering by creator doesn't requires full match of the full creator name EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Stack"), - /* no results */ + "Islam Stack Exchange", + "Movies & TV Stack Exchange", + "Mythology & Folklore Stack Exchange" ); + + // filtering by creator requires a full phrase match (ignoring some non-word terms) EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies & TV Stack Exchange"), "Movies & TV Stack Exchange" ); + EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies & TV"), + "Movies & TV Stack Exchange" + ); + EXPECT_FILTER_RESULTS(kiwix::Filter().creator("Movies TV"), + "Movies & TV Stack Exchange" + ); + EXPECT_FILTER_RESULTS(kiwix::Filter().creator("TV & Movies"), + /* no results */ + ); + EXPECT_FILTER_RESULTS(kiwix::Filter().creator("TV Movies"), + /* no results */ + ); + + EXPECT_FILTER_RESULTS(kiwix::Filter().query("creator:Wikipedia"), + "Encyclopédie de la Tunisie", + "Géographie par Wikipédia", + "Mathématiques", + "Ray Charles" + ); + } TEST_F(LibraryTest, filterByPublisher)