lib/query,parser: update to use mu-message-fields

This commit is contained in:
Dirk-Jan C. Binnema 2022-03-04 00:06:31 +02:00
parent 7c185590e4
commit 0df7a6959a
8 changed files with 229 additions and 250 deletions

View File

@ -24,6 +24,7 @@
#include <iostream>
#include <regex>
#include <mu-message.hh>
#include <utils/mu-utils.hh>
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)

View File

@ -17,12 +17,18 @@
** 02110-1301, USA.
*/
#include "mu-parser.hh"
#include <algorithm>
#include <optional>
#include "mu-tokenizer.hh"
#include "utils/mu-utils.hh"
#include "utils/mu-error.hh"
#include <algorithm>
#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<FieldInfo>;
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<std::string> 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<FieldInfo>& fields, MuMsgFieldId id)
add_field(std::vector<FieldInfo>& 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<FieldInfo>
process_field(const std::string& field, Flags flags)
process_field(const std::string& field_str, Parser::Flags flags)
{
std::vector<FieldInfo> 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<std::string>
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<std::string> 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<Value>(item.field,
item.prefix,
item.id,
process_value(item.field, val),
item.supports_phrase)});
std::make_unique<Value>(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<Value>(item.field,
item.prefix,
item.id,
process_value(item.field, val),
item.supports_phrase)}));
std::make_unique<Value>(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<Range>(field.field,
field.prefix,
field.id,
prange.lower,
prange.upper)});
std::make_unique<Range>(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<Private>(store, flags)}
Mu::Parser::Parser(const Store& store, Parser::Flags flags) :
priv_{std::make_unique<Private>(store, flags)}
{
}

View File

@ -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<std::string> opt_string(const Xapian::Document& doc, MuMsgFieldId id) const noexcept
{
std::string val = xapian_try([&] { return doc.get_value(id); }, std::string{""});
private:
Option<std::string> 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

View File

@ -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<std::string> 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:

View File

@ -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<std::string> 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<std::string> 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<std::string> path() const noexcept { return opt_string(MU_MSG_FIELD_ID_PATH); }
Option<std::string> 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<std::string> date() const noexcept { return opt_string(MU_MSG_FIELD_ID_DATE); }
Option<std::string> 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<std::string> subject() const noexcept { return opt_string(MU_MSG_FIELD_ID_SUBJECT); }
Option<std::string> 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<std::string> 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<std::string> opt_string(MuMsgFieldId id) const noexcept
Option<std::string> 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<XapianDocument*>(
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("<none>").c_str(),
err ? err->message : "somethng went wrong");
path().value_or("<none>").c_str(),
err ? err->message : "somethng went wrong");
g_clear_error(&err);
}
return msg_;

View File

@ -18,7 +18,7 @@
*/
#include "mu-query-threads.hh"
#include "mu-msg-fields.h"
#include "mu-message-fields.hh"
#include <set>
#include <unordered_set>

View File

@ -29,72 +29,64 @@
#include <xapian.h>
#include <glib/gstdio.h>
#include "mu-msg-fields.h"
#include "mu-query-results.hh"
#include "mu-query-match-deciders.hh"
#include "mu-query-threads.hh"
#include <mu-xapian.hh>
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<Field::Id> sortfield_id,
QueryFlags qflags) const;
Xapian::Enquire make_related_enquire(const StringSet& thread_ids,
std::optional<Field::Id> sortfield_id,
QueryFlags qflags) const;
Option<QueryResults> run_threaded(QueryResults&& qres,
Xapian::Enquire& enq,
QueryFlags qflags,
size_t max_size) const;
Option<QueryResults> run_singular(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const;
Option<QueryResults> run_related(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const;
Option<QueryResults> run(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const;
Option<QueryResults> run_threaded(QueryResults&& qres, Xapian::Enquire& enq,
QueryFlags qflags, size_t max_size) const;
Option<QueryResults> run_singular(const std::string& expr,
std::optional<Field::Id> sortfield_id,
QueryFlags qflags, size_t maxnum) const;
Option<QueryResults> run_related(const std::string& expr,
std::optional<Field::Id> sortfield_id,
QueryFlags qflags, size_t maxnum) const;
size_t store_size() const
{
return store_.database().get_doccount();
}
Option<QueryResults> run(const std::string& expr,
std::optional<Field::Id> 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<Private>(store)} {}
Query::Query(const Store& store) : priv_{std::make_unique<Private>(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<Xapian::valueno>(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<Field::Id> 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<Field::Id> 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<Xapian::Query> 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<QueryResults>
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<QueryResults>
Query::Private::run_singular(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const
std::optional<Field::Id> 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<std::string>
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<QueryResults>
Query::Private::run_related(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const
std::optional<Field::Id> 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<QueryResults>
Query::Private::run(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const
Query::Private::run(const std::string& expr,
std::optional<Message::Field::Id> 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<QueryResults>
Query::run(const std::string& expr,
MuMsgFieldId sortfieldid,
QueryFlags qflags,
size_t maxnum) const
Query::run(const std::string& expr, std::optional<Message::Field::Id> 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();

View File

@ -21,11 +21,13 @@
#define __MU_QUERY_HH__
#include <memory>
#include <optional>
#include <glib.h>
#include <mu-store.hh>
#include <mu-query-results.hh>
#include <utils/mu-utils.hh>
#include <mu-message.hh>
namespace Mu {
@ -41,10 +43,11 @@ public:
*
* @return the query-results, or Nothing in case of error.
*/
Option<QueryResults> run(const std::string& expr = "",
MuMsgFieldId sortfieldid = MU_MSG_FIELD_ID_NONE,
QueryFlags flags = QueryFlags::None,
size_t maxnum = 0) const;
Option<QueryResults> run(const std::string& expr = "",
std::optional<Message::Field::Id> 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