From 0df7a6959af21e62ed4e185659f30174fe58fecc Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Fri, 4 Mar 2022 00:06:31 +0200 Subject: [PATCH] lib/query,parser: update to use mu-message-fields --- lib/mu-data.hh | 18 +-- lib/mu-parser.cc | 230 +++++++++++++++------------------ lib/mu-query-match-deciders.cc | 17 +-- lib/mu-query-matches.hh | 8 +- lib/mu-query-results.hh | 25 ++-- lib/mu-query-threads.cc | 2 +- lib/mu-query.cc | 168 ++++++++++++------------ lib/mu-query.hh | 11 +- 8 files changed, 229 insertions(+), 250 deletions(-) diff --git a/lib/mu-data.hh b/lib/mu-data.hh index d5d91a06..e8abf6d0 100644 --- a/lib/mu-data.hh +++ b/lib/mu-data.hh @@ -24,6 +24,7 @@ #include #include +#include #include namespace Mu { @@ -34,13 +35,14 @@ struct Data { enum class Type { Value, Range }; virtual ~Data() = default; - Type type; /**< type of data */ - std::string field; /**< full name of the field */ - std::string prefix; /**< Xapian prefix for thef field */ - unsigned id; /**< Xapian value no for the field */ + Type type; /**< type of data */ + std::string field; /**< full name of the field */ + std::string prefix; /**< Xapian prefix for thef field */ + Message::Field::Id id; /**< Xapian value no for the field */ protected: - Data(Type _type, const std::string& _field, const std::string& _prefix, unsigned _id) + Data(Type _type, const std::string& _field, const std::string& _prefix, + Message::Field::Id _id) : type(_type), field(_field), prefix(_prefix), id(_id) { } @@ -80,7 +82,7 @@ struct Range : public Data { */ Range(const std::string& _field, const std::string& _prefix, - unsigned _id, + Message::Field::Id _id, const std::string& _lower, const std::string& _upper) : @@ -103,12 +105,12 @@ struct Value : public Data { * * @param _field the field * @param _prefix the xapian prefix - * @param _id xapian value number + * @param _id field id * @param _value the value */ Value(const std::string& _field, const std::string& _prefix, - unsigned _id, + Message::Field::Id _id, const std::string& _value, bool _phrase = false) : Data(Value::Type::Value, _field, _prefix, _id), value(_value), phrase(_phrase) diff --git a/lib/mu-parser.cc b/lib/mu-parser.cc index d8287c47..64730f1b 100644 --- a/lib/mu-parser.cc +++ b/lib/mu-parser.cc @@ -17,12 +17,18 @@ ** 02110-1301, USA. */ #include "mu-parser.hh" + +#include +#include + + #include "mu-tokenizer.hh" #include "utils/mu-utils.hh" #include "utils/mu-error.hh" -#include +#include "mu-message.hh" using namespace Mu; +using namespace Mu::Message; // 3 precedence levels: units (NOT,()) > factors (OR) > terms (AND) @@ -52,17 +58,14 @@ struct FieldInfo { const std::string field; const std::string prefix; bool supports_phrase; - unsigned id; + Field::Id id; }; using FieldInfoVec = std::vector; - -using Flags = Parser::Flags; - struct Parser::Private { - Private(const Store& store, Flags flags) : store_{store}, flags_{flags} {} + Private(const Store& store, Parser::Flags flags) : store_{store}, flags_{flags} {} std::vector process_regex(const std::string& field, - const std::regex& rx) const; + const std::regex& rx) const; Mu::Tree term_1(Mu::Tokens& tokens, WarningVec& warnings) const; Mu::Tree term_2(Mu::Tokens& tokens, Node::Type& op, WarningVec& warnings) const; @@ -71,118 +74,92 @@ struct Parser::Private { Mu::Tree unit(Mu::Tokens& tokens, WarningVec& warnings) const; Mu::Tree data(Mu::Tokens& tokens, WarningVec& warnings) const; Mu::Tree range(const FieldInfoVec& fields, - const std::string& lower, - const std::string& upper, - size_t pos, - WarningVec& warnings) const; + const std::string& lower, + const std::string& upper, + size_t pos, + WarningVec& warnings) const; Mu::Tree regex(const FieldInfoVec& fields, - const std::string& v, - size_t pos, - WarningVec& warnings) const; + const std::string& v, + size_t pos, + WarningVec& warnings) const; Mu::Tree value(const FieldInfoVec& fields, - const std::string& v, - size_t pos, - WarningVec& warnings) const; + const std::string& v, + size_t pos, + WarningVec& warnings) const; private: const Store& store_; - const Flags flags_; + const Parser::Flags flags_; }; -static MuMsgFieldId -field_id(const std::string& field) -{ - if (field.empty()) - return MU_MSG_FIELD_ID_NONE; - - MuMsgFieldId id = mu_msg_field_id_from_name(field.c_str(), FALSE); - if (id != MU_MSG_FIELD_ID_NONE) - return id; - else if (field.length() == 1) - return mu_msg_field_id_from_shortcut(field[0], FALSE); - else - return MU_MSG_FIELD_ID_NONE; -} - static std::string process_value(const std::string& field, const std::string& value) { - const auto id = field_id(field); - if (id == MU_MSG_FIELD_ID_NONE) - return value; - switch (id) { - case MU_MSG_FIELD_ID_PRIO: { - if (!value.empty()) - return std::string(1, value[0]); - } break; - - case MU_MSG_FIELD_ID_FLAGS: - if (const auto info{message_flag_info(value)}; info) - return std::string(1, info->shortcut_lower()); - break; - - default: - break; + const auto id_opt{message_field_id(field)}; + if (id_opt) { + switch (*id_opt) { + case Field::Id::Priority: { + if (!value.empty()) + return std::string(1, value[0]); + } break; + case Field::Id::Flags: + if (const auto info{message_flag_info(value)}; info) + return std::string(1, info->shortcut_lower()); + break; + default: + break; + } } return value; // XXX prio/flags, etc. alias } static void -add_field(std::vector& fields, MuMsgFieldId id) +add_field(std::vector& fields, Field::Id field_id) { - const auto shortcut = mu_msg_field_shortcut(id); - if (!shortcut) + const auto field{message_field(field_id)}; + if (!field.shortcut) return; // can't be searched - const auto name = mu_msg_field_name(id); - const auto pfx = mu_msg_field_xapian_prefix(id); - - if (!name || !pfx) - return; - - fields.push_back({{name}, {pfx}, (bool)mu_msg_field_xapian_index(id), id}); + fields.emplace_back(FieldInfo{std::string{field.name}, field.xapian_term(), + field.is_full_text(), field_id}); } static std::vector -process_field(const std::string& field, Flags flags) +process_field(const std::string& field_str, Parser::Flags flags) { std::vector fields; - if (any_of(flags & Flags::UnitTest)) { - add_field(fields, MU_MSG_FIELD_ID_MSGID); + if (any_of(flags & Parser::Flags::UnitTest)) { + add_field(fields, Field::Id::MessageId); return fields; } - if (field == "contact" || field == "recip") { // multi fields - add_field(fields, MU_MSG_FIELD_ID_TO); - add_field(fields, MU_MSG_FIELD_ID_CC); - add_field(fields, MU_MSG_FIELD_ID_BCC); - if (field == "contact") - add_field(fields, MU_MSG_FIELD_ID_FROM); - } else if (field == "") { - add_field(fields, MU_MSG_FIELD_ID_TO); - add_field(fields, MU_MSG_FIELD_ID_CC); - add_field(fields, MU_MSG_FIELD_ID_BCC); - add_field(fields, MU_MSG_FIELD_ID_FROM); - add_field(fields, MU_MSG_FIELD_ID_SUBJECT); - add_field(fields, MU_MSG_FIELD_ID_BODY_TEXT); - } else { - const auto id = field_id(field); - if (id != MU_MSG_FIELD_ID_NONE) - add_field(fields, id); - } + if (field_str == "contact" || field_str == "recip") { // multi fields + add_field(fields, Field::Id::To); + add_field(fields, Field::Id::Cc); + add_field(fields, Field::Id::Bcc); + if (field_str == "contact") + add_field(fields, Field::Id::From); + } else if (field_str.empty()) { + add_field(fields, Field::Id::To); + add_field(fields, Field::Id::Cc); + add_field(fields, Field::Id::Bcc); + add_field(fields, Field::Id::From); + add_field(fields, Field::Id::Subject); + add_field(fields, Field::Id::BodyText); + } else if (const auto id_opt{message_field_id(field_str)}; id_opt) + add_field(fields, *id_opt); return fields; } static bool -is_range_field(const std::string& field) +is_range_field(const std::string& field_str) { - const auto id = field_id(field); - if (id == MU_MSG_FIELD_ID_NONE) + if (const auto field_id_opt{message_field_id(field_str)}; !field_id_opt) return false; else - return mu_msg_field_is_range_field(id); + return message_field(*field_id_opt).is_range(); } struct MyRange { @@ -191,19 +168,20 @@ struct MyRange { }; static MyRange -process_range(const std::string& field, const std::string& lower, const std::string& upper) +process_range(const std::string& field_str, + const std::string& lower, const std::string& upper) { - const auto id = field_id(field); - if (id == MU_MSG_FIELD_ID_NONE) + const auto id_opt{message_field_id(field_str)}; + if (!id_opt) return {lower, upper}; std::string l2 = lower; std::string u2 = upper; - if (id == MU_MSG_FIELD_ID_DATE) { + if (*id_opt == Field::Id::Date) { l2 = Mu::date_to_time_t_string(lower, true); u2 = Mu::date_to_time_t_string(upper, false); - } else if (id == MU_MSG_FIELD_ID_SIZE) { + } else if (*id_opt == Field::Id::Size) { l2 = Mu::size_to_string(lower, true); u2 = Mu::size_to_string(upper, false); } @@ -212,16 +190,17 @@ process_range(const std::string& field, const std::string& lower, const std::str } std::vector -Parser::Private::process_regex(const std::string& field, const std::regex& rx) const +Parser::Private::process_regex(const std::string& field_str, + const std::regex& rx) const { - const auto id = field_id(field); - if (id == MU_MSG_FIELD_ID_NONE) + const auto id_opt{message_field_id(field_str)}; + if (!id_opt) return {}; - char pfx[] = {mu_msg_field_shortcut(id), '\0'}; - + const auto field{message_field(*id_opt)}; + const auto prefix{field.xapian_term()}; std::vector terms; - store_.for_each_term(pfx, [&](auto&& str) { + store_.for_each_term(prefix, [&](auto&& str) { if (std::regex_search(str.c_str() + 1, rx)) // avoid copy terms.emplace_back(str); return true; @@ -244,9 +223,9 @@ empty() Mu::Tree Parser::Private::value(const FieldInfoVec& fields, - const std::string& v, - size_t pos, - WarningVec& warnings) const + const std::string& v, + size_t pos, + WarningVec& warnings) const { auto val = utf8_flatten(v); @@ -256,30 +235,30 @@ Parser::Private::value(const FieldInfoVec& fields, if (fields.size() == 1) { const auto item = fields.front(); return Tree({Node::Type::Value, - std::make_unique(item.field, - item.prefix, - item.id, - process_value(item.field, val), - item.supports_phrase)}); + std::make_unique(item.field, + item.prefix, + item.id, + process_value(item.field, val), + item.supports_phrase)}); } // a 'multi-field' such as "recip:" Tree tree(Node{Node::Type::OpOr}); for (const auto& item : fields) tree.add_child(Tree({Node::Type::Value, - std::make_unique(item.field, - item.prefix, - item.id, - process_value(item.field, val), - item.supports_phrase)})); + std::make_unique(item.field, + item.prefix, + item.id, + process_value(item.field, val), + item.supports_phrase)})); return tree; } Mu::Tree Parser::Private::regex(const FieldInfoVec& fields, - const std::string& v, - size_t pos, - WarningVec& warnings) const + const std::string& v, + size_t pos, + WarningVec& warnings) const { if (v.length() < 2) throw BUG("expected regexp, got '%s'", v.c_str()); @@ -312,10 +291,10 @@ Parser::Private::regex(const FieldInfoVec& fields, Mu::Tree Parser::Private::range(const FieldInfoVec& fields, - const std::string& lower, - const std::string& upper, - size_t pos, - WarningVec& warnings) const + const std::string& lower, + const std::string& upper, + size_t pos, + WarningVec& warnings) const { if (fields.empty()) throw BUG("expected field"); @@ -329,11 +308,11 @@ Parser::Private::range(const FieldInfoVec& fields, prange = process_range(field.field, upper, lower); return Tree({Node::Type::Range, - std::make_unique(field.field, - field.prefix, - field.id, - prange.lower, - prange.upper)}); + std::make_unique(field.field, + field.prefix, + field.id, + prange.lower, + prange.upper)}); } Mu::Tree @@ -370,10 +349,10 @@ Parser::Private::data(Mu::Tokens& tokens, WarningVec& warnings) const const auto dotdot = val.find(".."); if (dotdot != std::string::npos) return range(fields, - val.substr(0, dotdot), - val.substr(dotdot + 2), - token.pos, - warnings); + val.substr(0, dotdot), + val.substr(dotdot + 2), + token.pos, + warnings); else if (is_range_field(fields.front().field)) { // range field without a range - treat as field:val..val return range(fields, val, val, token.pos, warnings); @@ -511,7 +490,8 @@ Parser::Private::term_1(Mu::Tokens& tokens, WarningVec& warnings) const } } -Mu::Parser::Parser(const Store& store, Flags flags) : priv_{std::make_unique(store, flags)} +Mu::Parser::Parser(const Store& store, Parser::Flags flags) : + priv_{std::make_unique(store, flags)} { } diff --git a/lib/mu-query-match-deciders.cc b/lib/mu-query-match-deciders.cc index 381acbbd..f4b0eeb6 100644 --- a/lib/mu-query-match-deciders.cc +++ b/lib/mu-query-match-deciders.cc @@ -23,6 +23,7 @@ #include "utils/mu-option.hh" using namespace Mu; +using namespace Mu::Message; // We use a MatchDecider to gather information about the matches, and decide // whether to include them in the results. @@ -47,12 +48,12 @@ struct MatchDecider : public Xapian::MatchDecider { { QueryMatch qm{}; - auto msgid{opt_string(doc, MU_MSG_FIELD_ID_MSGID) - .value_or(*opt_string(doc, MU_MSG_FIELD_ID_PATH))}; + auto msgid{opt_string(doc, Field::Id::MessageId) + .value_or(*opt_string(doc, Field::Id::Path))}; if (!decider_info_.message_ids.emplace(std::move(msgid)).second) qm.flags |= QueryMatch::Flags::Duplicate; - const auto path{opt_string(doc, MU_MSG_FIELD_ID_PATH)}; + const auto path{opt_string(doc, Field::Id::Path)}; if (!path || ::access(path->c_str(), R_OK) != 0) qm.flags |= QueryMatch::Flags::Unreadable; @@ -86,7 +87,7 @@ struct MatchDecider : public Xapian::MatchDecider { */ void gather_thread_ids(const Xapian::Document& doc) const { - auto thread_id{opt_string(doc, MU_MSG_FIELD_ID_THREAD_ID)}; + auto thread_id{opt_string(doc, Field::Id::ThreadId)}; if (thread_id) decider_info_.thread_ids.emplace(std::move(*thread_id)); } @@ -95,10 +96,10 @@ struct MatchDecider : public Xapian::MatchDecider { const QueryFlags qflags_; DeciderInfo& decider_info_; - private: - Option opt_string(const Xapian::Document& doc, MuMsgFieldId id) const noexcept - { - std::string val = xapian_try([&] { return doc.get_value(id); }, std::string{""}); +private: + Option opt_string(const Xapian::Document& doc, Field::Id id) const noexcept { + const auto value_no{message_field(id).value_no()}; + std::string val = xapian_try([&] { return doc.get_value(value_no); }, std::string{""}); if (val.empty()) return Nothing; else diff --git a/lib/mu-query-matches.hh b/lib/mu-query-matches.hh index 052cc791..d901930e 100644 --- a/lib/mu-query-matches.hh +++ b/lib/mu-query-matches.hh @@ -73,7 +73,7 @@ class QueryResultsIterator { QueryResultsIterator(Xapian::MSetIterator it, size_t max_num, - MuMsgFieldId sort_field, + Message::Field::Id sort_field, MuMsgIterFlags flags, MatchInfo& minfo) : it_{it}, match_info_{minfo} @@ -124,7 +124,7 @@ class QueryResultsIterator { std::string message_id() const { g_return_val_if_fail(it_ != Xapian::MSetIterator::end(), ""); - return document().get_value(MU_MSG_FIELD_ID_MSGID); + return document().get_value(Field::Id::MessageId); } /** @@ -136,7 +136,7 @@ class QueryResultsIterator { std::string path() const { g_return_val_if_fail(it_ != Xapian::MSetIterator::end(), ""); - return document().get_value(MU_MSG_FIELD_ID_PATH); + return document().get_value(Field::Id::Path); } /** @@ -149,7 +149,7 @@ class QueryResultsIterator { std::vector references() const { g_return_val_if_fail(it_ != Xapian::MSetIterator::end(), {}); - return split(document().get_value(MU_MSG_FIELD_ID_REFS), ","); + return split(document().get_value(Field::Id::References), ","); } private: diff --git a/lib/mu-query-results.hh b/lib/mu-query-results.hh index 84f9636d..b6f787ed 100644 --- a/lib/mu-query-results.hh +++ b/lib/mu-query-results.hh @@ -59,7 +59,7 @@ enum struct QueryFlags { Threading = 1 << 4, /**< calculate threading info */ // internal Leader = 1 << 5, /**< This is the leader query (for internal use - * only)*/ + * only)*/ }; MU_ENABLE_BITOPS(QueryFlags); @@ -219,7 +219,7 @@ public: */ Option message_id() const noexcept { - return opt_string(MU_MSG_FIELD_ID_MSGID); + return opt_string(Message::Field::Id::MessageId); } /** @@ -230,7 +230,7 @@ public: */ Option thread_id() const noexcept { - return opt_string(MU_MSG_FIELD_ID_THREAD_ID); + return opt_string(Message::Field::Id::ThreadId); } /** @@ -239,7 +239,7 @@ public: * * @return a filesystem path */ - Option path() const noexcept { return opt_string(MU_MSG_FIELD_ID_PATH); } + Option path() const noexcept { return opt_string(Message::Field::Id::Path); } /** * Get the date for the document (message) the iterator is pointing at. @@ -247,7 +247,7 @@ public: * * @return a filesystem path */ - Option date() const noexcept { return opt_string(MU_MSG_FIELD_ID_DATE); } + Option date() const noexcept { return opt_string(Message::Field::Id::Date); } /** * Get the file-system path for the document (message) this iterator is @@ -255,7 +255,7 @@ public: * * @return the subject */ - Option subject() const noexcept { return opt_string(MU_MSG_FIELD_ID_SUBJECT); } + Option subject() const noexcept { return opt_string(Message::Field::Id::Subject); } /** * Get the references for the document (messages) this is iterator is @@ -266,7 +266,7 @@ public: */ std::vector references() const noexcept { - return split(document().get_value(MU_MSG_FIELD_ID_REFS), ","); + return split(opt_string(Message::Field::Id::References).value_or(""), ","); } /** @@ -276,10 +276,11 @@ public: * * @return the value */ - Option opt_string(MuMsgFieldId id) const noexcept + Option opt_string(Message::Field::Id id) const noexcept { std::string empty; - std::string val = xapian_try([&] { return document().get_value(id); }, empty); + const auto value_no{message_field(id).value_no()}; + std::string val = xapian_try([&] {return document().get_value(value_no);}, empty); if (val.empty()) return Nothing; else @@ -314,14 +315,14 @@ public: return xapian_try( [&] { auto docp{reinterpret_cast( - new Xapian::Document(document()))}; + new Xapian::Document(document()))}; GError* err{}; g_clear_pointer(&msg_, mu_msg_unref); if (!(msg_ = mu_msg_new_from_doc(docp, &err))) { delete docp; g_warning("failed to crate message for %s: %s", - path().value_or("").c_str(), - err ? err->message : "somethng went wrong"); + path().value_or("").c_str(), + err ? err->message : "somethng went wrong"); g_clear_error(&err); } return msg_; diff --git a/lib/mu-query-threads.cc b/lib/mu-query-threads.cc index 30710905..dd8f838d 100644 --- a/lib/mu-query-threads.cc +++ b/lib/mu-query-threads.cc @@ -18,7 +18,7 @@ */ #include "mu-query-threads.hh" -#include "mu-msg-fields.h" +#include "mu-message-fields.hh" #include #include diff --git a/lib/mu-query.cc b/lib/mu-query.cc index e97e4ba7..01df9c31 100644 --- a/lib/mu-query.cc +++ b/lib/mu-query.cc @@ -29,72 +29,64 @@ #include #include -#include "mu-msg-fields.h" #include "mu-query-results.hh" #include "mu-query-match-deciders.hh" #include "mu-query-threads.hh" #include using namespace Mu; +using namespace Mu::Message; struct Query::Private { - Private(const Store& store) - : store_{store}, parser_{store_} {} + Private(const Store& store) : store_{store}, parser_{store_} {} // New // bool calculate_threads (Xapian::Enquire& enq, size maxnum); - Xapian::Enquire - make_enquire(const std::string& expr, MuMsgFieldId sortfieldid, QueryFlags qflags) const; - Xapian::Enquire make_related_enquire(const StringSet& thread_ids, - MuMsgFieldId sortfieldid, - QueryFlags qflags) const; + Xapian::Enquire make_enquire(const std::string& expr, + std::optional sortfield_id, + QueryFlags qflags) const; + Xapian::Enquire make_related_enquire(const StringSet& thread_ids, + std::optional sortfield_id, + QueryFlags qflags) const; - Option run_threaded(QueryResults&& qres, - Xapian::Enquire& enq, - QueryFlags qflags, - size_t max_size) const; - Option run_singular(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const; - Option run_related(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const; - Option run(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const; + Option run_threaded(QueryResults&& qres, Xapian::Enquire& enq, + QueryFlags qflags, size_t max_size) const; + Option run_singular(const std::string& expr, + std::optional sortfield_id, + QueryFlags qflags, size_t maxnum) const; + Option run_related(const std::string& expr, + std::optional sortfield_id, + QueryFlags qflags, size_t maxnum) const; - size_t store_size() const - { - return store_.database().get_doccount(); - } + Option run(const std::string& expr, + std::optional sortfield_id, QueryFlags qflags, + size_t maxnum) const; + + size_t store_size() const { return store_.database().get_doccount(); } const Store& store_; const Parser parser_; }; -Query::Query(const Store& store) - : priv_{std::make_unique(store)} {} +Query::Query(const Store& store) : priv_{std::make_unique(store)} {} Query::Query(Query&& other) = default; Query::~Query() = default; static Xapian::Enquire& -maybe_sort(Xapian::Enquire& enq, MuMsgFieldId sortfieldid, QueryFlags qflags) +sort_enquire(Xapian::Enquire& enq, Field::Id sortfield_id, QueryFlags qflags) { - if (sortfieldid != MU_MSG_FIELD_ID_NONE) - enq.set_sort_by_value(static_cast(sortfieldid), - any_of(qflags & QueryFlags::Descending)); + const auto value_no{message_field(sortfield_id).value_no()}; + enq.set_sort_by_value(value_no, any_of(qflags & QueryFlags::Descending)); + return enq; } Xapian::Enquire -Query::Private::make_enquire(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags) const +Query::Private::make_enquire(const std::string& expr, + std::optional sortfield_id, + QueryFlags qflags) const { Xapian::Enquire enq{store_.database()}; @@ -109,29 +101,33 @@ Query::Private::make_enquire(const std::string& expr, g_debug("qtree: %s", to_string(tree).c_str()); } - return maybe_sort(enq, sortfieldid, qflags); + if (sortfield_id) + sort_enquire(enq, *sortfield_id, qflags); + + return enq; } Xapian::Enquire Query::Private::make_related_enquire(const StringSet& thread_ids, - MuMsgFieldId sortfieldid, - QueryFlags qflags) const + std::optional sortfield_id, + QueryFlags qflags) const { - Xapian::Enquire enq{store_.database()}; - static std::string pfx(1, mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_THREAD_ID)); - + Xapian::Enquire enq{store_.database()}; std::vector qvec; for (auto&& t : thread_ids) - qvec.emplace_back(pfx + t); + qvec.emplace_back(message_field(Field::Id::ThreadId).xapian_term(t)); + Xapian::Query qr{Xapian::Query::OP_OR, qvec.begin(), qvec.end()}; enq.set_query(qr); - return maybe_sort(enq, sortfieldid, qflags); + if (sortfield_id) + sort_enquire(enq, *sortfield_id, qflags); + + return enq; } struct ThreadKeyMaker : public Xapian::KeyMaker { - ThreadKeyMaker(const QueryMatches& matches) - : match_info_(matches) {} + ThreadKeyMaker(const QueryMatches& matches) : match_info_(matches) {} std::string operator()(const Xapian::Document& doc) const override { const auto it{match_info_.find(doc.get_docid())}; @@ -141,10 +137,8 @@ struct ThreadKeyMaker : public Xapian::KeyMaker { }; Option -Query::Private::run_threaded(QueryResults&& qres, - Xapian::Enquire& enq, - QueryFlags qflags, - size_t maxnum) const +Query::Private::run_threaded(QueryResults&& qres, Xapian::Enquire& enq, QueryFlags qflags, + size_t maxnum) const { const auto descending{any_of(qflags & QueryFlags::Descending)}; @@ -163,9 +157,8 @@ Query::Private::run_threaded(QueryResults&& qres, Option Query::Private::run_singular(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const + std::optional sortfield_id, + QueryFlags qflags, size_t maxnum) const { // i.e. a query _without_ related messages, but still possibly // with threading. @@ -179,10 +172,11 @@ Query::Private::run_singular(const std::string& expr, DeciderInfo minfo{}; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wextra" - auto enq{make_enquire(expr, threading ? MU_MSG_FIELD_ID_DATE : sortfieldid, qflags)}; + auto enq{make_enquire(expr, threading ? Field::Id::Date : sortfield_id, qflags)}; #pragma GCC diagnostic ignored "-Wswitch-default" #pragma GCC diagnostic pop - auto mset{enq.get_mset(0, maxnum, {}, make_leader_decider(singular_qflags, minfo).get())}; + auto mset{enq.get_mset(0, maxnum, {}, + make_leader_decider(singular_qflags, minfo).get())}; mset.fetch(); auto qres{QueryResults{mset, std::move(minfo.matches)}}; @@ -191,9 +185,11 @@ Query::Private::run_singular(const std::string& expr, } static Option -opt_string(const Xapian::Document& doc, MuMsgFieldId id) noexcept +opt_string(const Xapian::Document& doc, Field::Id id) noexcept { - std::string val = xapian_try([&] { return doc.get_value(id); }, std::string{""}); + const auto value_no{message_field(id).value_no()}; + std::string val = + xapian_try([&] { return doc.get_value(value_no); }, std::string{""}); if (val.empty()) return Nothing; else @@ -202,9 +198,8 @@ opt_string(const Xapian::Document& doc, MuMsgFieldId id) noexcept Option Query::Private::run_related(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const + std::optional sortfield_id, + QueryFlags qflags, size_t maxnum) const { // i.e. a query _with_ related messages and possibly with threading. // @@ -218,14 +213,14 @@ Query::Private::run_related(const std::string& expr, // Run our first, "leader" query DeciderInfo minfo{}; - auto enq{make_enquire(expr, MU_MSG_FIELD_ID_DATE, leader_qflags)}; + auto enq{make_enquire(expr, Field::Id::Date, leader_qflags)}; const auto mset{ - enq.get_mset(0, maxnum, {}, make_leader_decider(leader_qflags, minfo).get())}; + enq.get_mset(0, maxnum, {}, make_leader_decider(leader_qflags, minfo).get())}; // Gather the thread-ids we found mset.fetch(); for (auto it = mset.begin(); it != mset.end(); ++it) { - auto thread_id{opt_string(it.get_document(), MU_MSG_FIELD_ID_THREAD_ID)}; + auto thread_id{opt_string(it.get_document(), Field::Id::ThreadId)}; if (thread_id) minfo.thread_ids.emplace(std::move(*thread_id)); } @@ -235,28 +230,28 @@ Query::Private::run_related(const std::string& expr, // In the threaded-case, we search among _all_ messages, since complete // threads are preferred; no need to sort in that case since the search // is unlimited and the sorting happens during threading. - auto r_enq{make_related_enquire(minfo.thread_ids, - threading ? MU_MSG_FIELD_ID_NONE : sortfieldid, - qflags)}; - const auto r_mset{r_enq.get_mset(0, - threading ? store_size() : maxnum, - {}, - make_related_decider(qflags, minfo).get())}; + auto r_enq = std::invoke([&]{ + if (threading) + return make_related_enquire(minfo.thread_ids, std::nullopt, qflags); + else + return make_related_enquire(minfo.thread_ids, sortfield_id, qflags); + }); + + const auto r_mset{r_enq.get_mset(0, threading ? store_size() : maxnum, {}, + make_related_decider(qflags, minfo).get())}; auto qres{QueryResults{r_mset, std::move(minfo.matches)}}; return threading ? run_threaded(std::move(qres), r_enq, qflags, maxnum) : qres; } Option -Query::Private::run(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const +Query::Private::run(const std::string& expr, + std::optional sortfield_id, QueryFlags qflags, + size_t maxnum) const { const auto eff_maxnum{maxnum == 0 ? store_size() : maxnum}; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wextra" - const auto eff_sortfield{sortfieldid == MU_MSG_FIELD_ID_NONE ? MU_MSG_FIELD_ID_DATE - : sortfieldid}; + const auto eff_sortfield{sortfield_id.value_or(Field::Id::Date)}; #pragma GCC diagnostic pop if (any_of(qflags & QueryFlags::IncludeRelated)) return run_related(expr, eff_sortfield, qflags, eff_maxnum); @@ -265,21 +260,18 @@ Query::Private::run(const std::string& expr, } Option -Query::run(const std::string& expr, - MuMsgFieldId sortfieldid, - QueryFlags qflags, - size_t maxnum) const +Query::run(const std::string& expr, std::optional sortfield_id, + QueryFlags qflags, size_t maxnum) const try { // some flags are for internal use only. g_return_val_if_fail(none_of(qflags & QueryFlags::Leader), Nothing); - StopWatch sw{format("ran query '%s'; related: %s; threads: %s; max-size: %zu", - expr.c_str(), - any_of(qflags & QueryFlags::IncludeRelated) ? "yes" : "no", - any_of(qflags & QueryFlags::Threading) ? "yes" : "no", - maxnum)}; + StopWatch sw{format( + "ran query '%s'; related: %s; threads: %s; max-size: %zu", expr.c_str(), + any_of(qflags & QueryFlags::IncludeRelated) ? "yes" : "no", + any_of(qflags & QueryFlags::Threading) ? "yes" : "no", maxnum)}; - return priv_->run(expr, sortfieldid, qflags, maxnum); + return priv_->run(expr, sortfield_id, qflags, maxnum); } catch (...) { return Nothing; @@ -290,7 +282,7 @@ Query::count(const std::string& expr) const { return xapian_try( [&] { - const auto enq{priv_->make_enquire(expr, MU_MSG_FIELD_ID_NONE, {})}; + const auto enq{priv_->make_enquire(expr, {}, {})}; auto mset{enq.get_mset(0, priv_->store_size())}; mset.fetch(); return mset.size(); diff --git a/lib/mu-query.hh b/lib/mu-query.hh index 9f1ac5b6..b132b544 100644 --- a/lib/mu-query.hh +++ b/lib/mu-query.hh @@ -21,11 +21,13 @@ #define __MU_QUERY_HH__ #include +#include #include #include #include #include +#include namespace Mu { @@ -41,10 +43,11 @@ public: * * @return the query-results, or Nothing in case of error. */ - Option run(const std::string& expr = "", - MuMsgFieldId sortfieldid = MU_MSG_FIELD_ID_NONE, - QueryFlags flags = QueryFlags::None, - size_t maxnum = 0) const; + + Option run(const std::string& expr = "", + std::optional sortfield_id = {}, + QueryFlags flags = QueryFlags::None, + size_t maxnum = 0) const; /** * run a Xapian query to count the number of matches; for the syntax, please