query-results: remove GatherThreadIds

We can't really do that in the match-decider, since we get _all_ messages
there, not the <n>-limited.

And some whitespace changes.
This commit is contained in:
Dirk-Jan C. Binnema
2021-06-13 18:36:24 +03:00
parent 6caa9acb34
commit 619509eb56
3 changed files with 136 additions and 131 deletions

View File

@ -39,7 +39,8 @@
#include "mu-msg.hh" #include "mu-msg.hh"
namespace Mu { namespace Mu
{
/** /**
* This implements a QueryResults structure, which capture the results of a * This implements a QueryResults structure, which capture the results of a
@ -50,104 +51,97 @@ namespace Mu {
/// Flags that influence now matches are presented (or skipped) /// Flags that influence now matches are presented (or skipped)
enum struct QueryFlags { enum struct QueryFlags {
None = 0, /**< no flags */ None = 0, /**< no flags */
Descending = 1 << 0, /**< sort z->a */ Descending = 1 << 0, /**< sort z->a */
SkipUnreadable = 1 << 1, /**< skip unreadable msgs */ SkipUnreadable = 1 << 1, /**< skip unreadable msgs */
SkipDuplicates = 1 << 2, /**< skip duplicate msgs */ SkipDuplicates = 1 << 2, /**< skip duplicate msgs */
IncludeRelated = 1 << 3, /**< include related msgs */ IncludeRelated = 1 << 3, /**< include related msgs */
Threading = 1 << 4, /**< calculate threading info */ Threading = 1 << 4, /**< calculate threading info */
// internal // internal
Leader = 1 << 5, /**< This is the leader query (for internal use Leader = 1 << 5, /**< This is the leader query (for internal use
* only)*/ * only)*/
GatherThreadIds = 1 << 6, /**< Gather thread info */
}; };
MU_ENABLE_BITOPS(QueryFlags); MU_ENABLE_BITOPS (QueryFlags);
/// Stores all the essential information for sorting the results. /// Stores all the essential information for sorting the results.
struct QueryMatch { struct QueryMatch {
/// Flags for a match (message) found /// Flags for a match (message) found
enum struct Flags { enum struct Flags {
None = 0, /**< No Flags */ None = 0, /**< No Flags */
Leader = 1 << 0, /**< Mark direct matches as leader */ Leader = 1 << 0, /**< Mark direct matches as leader */
Related = 1 << 1, /**< A related message */ Related = 1 << 1, /**< A related message */
Unreadable = 1 << 2, /**< No readable file */ Unreadable = 1 << 2, /**< No readable file */
Duplicate = 1 << 3, /**< Message-id seen before */ Duplicate = 1 << 3, /**< Message-id seen before */
Root = 1 << 10, /**< Is this the thread-root? */ Root = 1 << 10, /**< Is this the thread-root? */
First = 1 << 11, /**< Is this the first message in a thread? */ First = 1 << 11, /**< Is this the first message in a thread? */
Last = 1 << 12, /**< Is this the last message in a thread? */ Last = 1 << 12, /**< Is this the last message in a thread? */
Orphan = 1 << 13, /**< Is this message without a parent? */ Orphan = 1 << 13, /**< Is this message without a parent? */
HasChild = 1 << 14, /**< Does this message have a child? */ HasChild = 1 << 14, /**< Does this message have a child? */
ThreadSubject = 1 << 20, /**< Message holds subject for (sub)thread */ ThreadSubject = 1 << 20, /**< Message holds subject for (sub)thread */
}; };
Flags flags{Flags::None}; /**< Flags */ Flags flags{Flags::None}; /**< Flags */
std::string date_key; /**< The date-key (for sorting all sub-root levels) */ std::string date_key; /**< The date-key (for sorting all sub-root levels) */
// the thread subject is the subject of the first message in a thread, // the thread subject is the subject of the first message in a thread,
// and any message that has a different subject compared to its predecessor // and any message that has a different subject compared to its predecessor
// (ignoring prefixes such as Re:) // (ignoring prefixes such as Re:)
// //
// otherwise, it is empty. // otherwise, it is empty.
std::string subject; /**< subject for this message */ std::string subject; /**< subject for this message */
size_t thread_level{}; /**< The thread level */ size_t thread_level{}; /**< The thread level */
std::string thread_path; /**< The hex-numerial path in the thread, ie. '00:01:0a' */ std::string thread_path; /**< The hex-numerial path in the thread, ie. '00:01:0a' */
std::string thread_date; /**< date of newest message in thread */ std::string thread_date; /**< date of newest message in thread */
bool operator<(const QueryMatch& rhs) const { bool operator< (const QueryMatch &rhs) const { return date_key < rhs.date_key; }
return date_key < rhs.date_key;
}
bool has_flag (Flags flag) const; bool has_flag (Flags flag) const;
}; };
MU_ENABLE_BITOPS(QueryMatch::Flags); MU_ENABLE_BITOPS (QueryMatch::Flags);
inline bool inline bool
QueryMatch::has_flag(QueryMatch::Flags flag) const QueryMatch::has_flag (QueryMatch::Flags flag) const
{ {
return any_of(flags & flag); return any_of (flags & flag);
} }
inline std::ostream &
inline std::ostream& operator<< (std::ostream &os, QueryMatch::Flags mflags)
operator<<(std::ostream& os, QueryMatch::Flags mflags)
{ {
if (mflags == QueryMatch::Flags::None) { if (mflags == QueryMatch::Flags::None) {
os << "<none>"; os << "<none>";
return os; return os;
} }
if (any_of(mflags & QueryMatch::Flags::Leader)) if (any_of (mflags & QueryMatch::Flags::Leader))
os << "leader "; os << "leader ";
if (any_of(mflags & QueryMatch::Flags::Unreadable)) if (any_of (mflags & QueryMatch::Flags::Unreadable))
os << "unreadable "; os << "unreadable ";
if (any_of(mflags & QueryMatch::Flags::Duplicate)) if (any_of (mflags & QueryMatch::Flags::Duplicate))
os << "dup "; os << "dup ";
if (any_of(mflags & QueryMatch::Flags::Root)) if (any_of (mflags & QueryMatch::Flags::Root))
os << "root "; os << "root ";
if (any_of(mflags & QueryMatch::Flags::Related)) if (any_of (mflags & QueryMatch::Flags::Related))
os << "related "; os << "related ";
if (any_of(mflags & QueryMatch::Flags::First)) if (any_of (mflags & QueryMatch::Flags::First))
os << "first "; os << "first ";
if (any_of(mflags & QueryMatch::Flags::Last)) if (any_of (mflags & QueryMatch::Flags::Last))
os << "last "; os << "last ";
if (any_of(mflags & QueryMatch::Flags::Orphan)) if (any_of (mflags & QueryMatch::Flags::Orphan))
os << "orphan "; os << "orphan ";
if (any_of(mflags & QueryMatch::Flags::HasChild)) if (any_of (mflags & QueryMatch::Flags::HasChild))
os << "has-child "; os << "has-child ";
return os; return os;
} }
using QueryMatches = std::unordered_map<Xapian::docid, QueryMatch>; using QueryMatches = std::unordered_map<Xapian::docid, QueryMatch>;
inline std::ostream& inline std::ostream &
operator<<(std::ostream& os, const QueryMatch& qmatch) operator<< (std::ostream &os, const QueryMatch &qmatch)
{ {
os << "qm:[" << qmatch.thread_path << "]: " // " (" << qmatch.thread_level << "): " os << "qm:[" << qmatch.thread_path << "]: " // " (" << qmatch.thread_level << "): "
<< "> date:<" << qmatch.date_key << "> " << "> date:<" << qmatch.date_key << "> "
@ -163,26 +157,31 @@ operator<<(std::ostream& os, const QueryMatch& qmatch)
/// Note, we internally skip unreadable/duplicate messages (when asked too); those /// Note, we internally skip unreadable/duplicate messages (when asked too); those
/// skipped ones do _not_ count towards the max_size /// skipped ones do _not_ count towards the max_size
/// ///
class QueryResultsIterator { class QueryResultsIterator
public: {
public:
using iterator_category = std::output_iterator_tag; using iterator_category = std::output_iterator_tag;
using value_type = MuMsg*; using value_type = MuMsg *;
using difference_type = void; using difference_type = void;
using pointer = void; using pointer = void;
using reference = void; using reference = void;
QueryResultsIterator(Xapian::MSetIterator mset_it, QueryMatches& query_matches): QueryResultsIterator (Xapian::MSetIterator mset_it, QueryMatches &query_matches)
mset_it_{mset_it}, query_matches_{query_matches} : mset_it_{mset_it}, query_matches_{query_matches}
{} {
}
~QueryResultsIterator() { g_clear_pointer (&msg_, mu_msg_unref); } ~QueryResultsIterator() { g_clear_pointer (&msg_, mu_msg_unref); }
/** /**
* Increment the iterator (we don't support post-increment) * Increment the iterator (we don't support post-increment)
* *
* @return an updated iterator, or end() if we were already at end() * @return an updated iterator, or end() if we were already at end()
*/ */
QueryResultsIterator& operator++() { ++mset_it_; return *this; } QueryResultsIterator &operator++()
{
++mset_it_;
return *this;
}
/** /**
* (Non)Equivalence operators * (Non)Equivalence operators
@ -191,11 +190,11 @@ public:
* *
* @return true or false * @return true or false
*/ */
bool operator==(const QueryResultsIterator& rhs) const { return mset_it_ == rhs.mset_it_; } bool operator== (const QueryResultsIterator &rhs) const { return mset_it_ == rhs.mset_it_; }
bool operator!=(const QueryResultsIterator& rhs) const { return mset_it_ != rhs.mset_it_; } bool operator!= (const QueryResultsIterator &rhs) const { return mset_it_ != rhs.mset_it_; }
QueryResultsIterator& operator*() { return *this; } QueryResultsIterator & operator*() { return *this; }
const QueryResultsIterator& operator*() const { return *this; } const QueryResultsIterator &operator*() const { return *this; }
/** /**
* Get the Xapian document this iterator is pointing at, * Get the Xapian document this iterator is pointing at,
@ -219,7 +218,10 @@ public:
* *
* @return a message-id * @return a message-id
*/ */
Option<std::string> message_id() const noexcept { return opt_string(MU_MSG_FIELD_ID_MSGID); } Option<std::string> message_id() const noexcept
{
return opt_string (MU_MSG_FIELD_ID_MSGID);
}
/** /**
* Get the thread-id for the document (message) this iterator is * Get the thread-id for the document (message) this iterator is
@ -227,7 +229,10 @@ public:
* *
* @return a message-id * @return a message-id
*/ */
Option<std::string> thread_id() const noexcept { return opt_string(MU_MSG_FIELD_ID_THREAD_ID); } Option<std::string> thread_id() const noexcept
{
return opt_string (MU_MSG_FIELD_ID_THREAD_ID);
}
/** /**
* Get the file-system path for the document (message) this iterator is * Get the file-system path for the document (message) this iterator is
@ -235,7 +240,7 @@ public:
* *
* @return a filesystem path * @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 (MU_MSG_FIELD_ID_PATH); }
/** /**
* Get the date for the document (message) the iterator is pointing at. * Get the date for the document (message) the iterator is pointing at.
@ -243,7 +248,7 @@ public:
* *
* @return a filesystem path * @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 (MU_MSG_FIELD_ID_DATE); }
/** /**
* Get the file-system path for the document (message) this iterator is * Get the file-system path for the document (message) this iterator is
@ -251,8 +256,10 @@ public:
* *
* @return the subject * @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 (MU_MSG_FIELD_ID_SUBJECT);
}
/** /**
* Get the references for the document (messages) this is iterator is * Get the references for the document (messages) this is iterator is
@ -261,8 +268,9 @@ public:
* *
* @return references * @return references
*/ */
std::vector<std::string> references() const noexcept { std::vector<std::string> references() const noexcept
return split(document().get_value(MU_MSG_FIELD_ID_REFS), ","); {
return split (document().get_value (MU_MSG_FIELD_ID_REFS), ",");
} }
/** /**
@ -272,23 +280,27 @@ public:
* *
* @return the value * @return the value
*/ */
Option<std::string> opt_string(MuMsgFieldId id) const noexcept try { Option<std::string> opt_string (MuMsgFieldId id) const noexcept
auto&& val{document().get_value(id)}; try {
return val.empty() ? Nothing : Some(val); auto &&val{document().get_value (id)};
} MU_XAPIAN_CATCH_BLOCK_RETURN (Nothing); return val.empty() ? Nothing : Some (val);
}
MU_XAPIAN_CATCH_BLOCK_RETURN (Nothing);
/** /**
* Get the Query match info for this message. * Get the Query match info for this message.
* *
* @return the match info. * @return the match info.
*/ */
QueryMatch& query_match() { QueryMatch &query_match()
g_assert(query_matches_.find(document().get_docid()) != query_matches_.end()); {
return query_matches_.find(document().get_docid())->second; g_assert (query_matches_.find (document().get_docid()) != query_matches_.end());
return query_matches_.find (document().get_docid())->second;
} }
const QueryMatch& query_match() const { const QueryMatch &query_match() const
g_assert(query_matches_.find(document().get_docid()) != query_matches_.end()); {
return query_matches_.find(document().get_docid())->second; g_assert (query_matches_.find (document().get_docid()) != query_matches_.end());
return query_matches_.find (document().get_docid())->second;
} }
/** /**
@ -298,33 +310,34 @@ k * destroyed.; it's a 'floating' reference.
* *
* @return a MuMsg* or NUL in case of error * @return a MuMsg* or NUL in case of error
*/ */
MuMsg* floating_msg () MuMsg *floating_msg() G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT try { try {
auto docp{reinterpret_cast<XapianDocument*>( auto docp{reinterpret_cast<XapianDocument *> (new Xapian::Document (document()))};
new Xapian::Document(document()))};
GError *err{}; GError *err{};
g_clear_pointer(&msg_, mu_msg_unref); g_clear_pointer (&msg_, mu_msg_unref);
if (!(msg_ = mu_msg_new_from_doc(docp, &err))) { if (!(msg_ = mu_msg_new_from_doc (docp, &err))) {
delete docp; delete docp;
g_warning ("failed to crate message for %s: %s", g_warning ("failed to crate message for %s: %s",
path().value_or("<none>").c_str(), path().value_or ("<none>").c_str(),
err ? err->message : "somethng went wrong"); err ? err->message : "somethng went wrong");
g_clear_error(&err); g_clear_error (&err);
} }
return msg_; return msg_;
}
MU_XAPIAN_CATCH_BLOCK_RETURN (NULL);
} MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); private:
private:
Xapian::MSetIterator mset_it_; Xapian::MSetIterator mset_it_;
QueryMatches& query_matches_; QueryMatches & query_matches_;
MuMsg *msg_{}; MuMsg * msg_{};
}; };
constexpr auto MaxQueryResultsSize = std::numeric_limits<size_t>::max(); constexpr auto MaxQueryResultsSize = std::numeric_limits<size_t>::max();
class QueryResults { class QueryResults
public: {
public:
/// Helper types /// Helper types
using iterator = QueryResultsIterator; using iterator = QueryResultsIterator;
using const_iterator = const iterator; using const_iterator = const iterator;
@ -334,34 +347,33 @@ public:
* *
* @param mset an Xapian::MSet with matches * @param mset an Xapian::MSet with matches
*/ */
QueryResults (const Xapian::MSet& mset, QueryMatches&& query_matches): QueryResults (const Xapian::MSet &mset, QueryMatches &&query_matches)
mset_{mset}, : mset_{mset}, query_matches_{std::move (query_matches)}
query_matches_{std::move(query_matches)} {
{} }
/** /**
* Is this QueryResults object empty (ie., no matches)? * Is this QueryResults object empty (ie., no matches)?
* *
* @return true are false * @return true are false
*/ */
bool empty() const { return mset_.empty(); } bool empty() const { return mset_.empty(); }
/** /**
* Get the number of matches in this QueryResult * Get the number of matches in this QueryResult
* *
* @return number of matches * @return number of matches
*/ */
size_t size() const { return mset_.size(); } size_t size() const { return mset_.size(); }
/** /**
* Get the begin iterator to the results. * Get the begin iterator to the results.
* *
* @return iterator * @return iterator
*/ */
iterator begin() { iterator begin() { return QueryResultsIterator (mset_.begin(), query_matches_); }
return QueryResultsIterator(mset_.begin(), query_matches_); const iterator begin() const
} {
const iterator begin() const { return QueryResultsIterator (mset_.begin(), query_matches_);
return QueryResultsIterator(mset_.begin(), query_matches_);
} }
/** /**
@ -369,12 +381,8 @@ public:
* *
* @return iterator * @return iterator
*/ */
iterator end() { iterator end() { return QueryResultsIterator (mset_.end(), query_matches_); }
return QueryResultsIterator(mset_.end(), query_matches_); const_iterator end() const { return QueryResultsIterator (mset_.end(), query_matches_); }
}
const_iterator end() const {
return QueryResultsIterator(mset_.end(), query_matches_);
}
/** /**
* Get the query-matches for these QueryResults. The non-const * Get the query-matches for these QueryResults. The non-const
@ -383,15 +391,14 @@ public:
* *
* @return query-matches * @return query-matches
*/ */
const QueryMatches& query_matches() const { return query_matches_; } const QueryMatches &query_matches() const { return query_matches_; }
QueryMatches& query_matches() { return query_matches_; } QueryMatches & query_matches() { return query_matches_; }
private: private:
const Xapian::MSet mset_; const Xapian::MSet mset_;
mutable QueryMatches query_matches_; mutable QueryMatches query_matches_;
}; };
} // namespace Mu } // namespace Mu
#endif /* MU_QUERY_RESULTS_HH__ */ #endif /* MU_QUERY_RESULTS_HH__ */

View File

@ -192,7 +192,7 @@ Query::Private::run_related (const std::string& expr, MuMsgFieldId sortfieldid,
// moreover, in either threaded or non-threaded case, we sort the first // moreover, in either threaded or non-threaded case, we sort the first
// ("leader") query by date, i.e, we prefer the newest or oldest // ("leader") query by date, i.e, we prefer the newest or oldest
// (descending) messages. // (descending) messages.
const auto leader_qflags{QueryFlags::Leader | QueryFlags::GatherThreadIds}; const auto leader_qflags{QueryFlags::Leader};
const auto threading{any_of(qflags & QueryFlags::Threading)}; const auto threading{any_of(qflags & QueryFlags::Threading)};
// Run our first, "leader" query // Run our first, "leader" query
@ -247,7 +247,6 @@ Query::run (const std::string& expr, MuMsgFieldId sortfieldid,
{ {
// some flags are for internal use only. // some flags are for internal use only.
g_return_val_if_fail (none_of(qflags & QueryFlags::Leader), Nothing); g_return_val_if_fail (none_of(qflags & QueryFlags::Leader), Nothing);
g_return_val_if_fail (none_of(qflags & QueryFlags::GatherThreadIds), Nothing);
StopWatch sw{format("ran query '%s'; related: %s; threads: %s; max-size: %zu", StopWatch sw{format("ran query '%s'; related: %s; threads: %s; max-size: %zu",
expr.c_str(), expr.c_str(),

View File

@ -1,5 +1,5 @@
/* /*
** Copyright (C) 2008-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** Copyright (C) 2008-2021 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
** **
** This program is free software; you can redistribute it and/or modify ** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by ** it under the terms of the GNU General Public License as published by
@ -27,30 +27,30 @@
#include <mu-query-results.hh> #include <mu-query-results.hh>
#include <utils/mu-utils.hh> #include <utils/mu-utils.hh>
namespace Mu
{
namespace Mu { class Query
{
class Query { public:
public:
/** /**
* Construct a new Query instance. * Construct a new Query instance.
* *
* @param store a MuStore object * @param store a MuStore object
*/ */
Query (const Store& store); Query (const Store &store);
/** /**
* DTOR * DTOR
* *
*/ */
~Query (); ~Query();
/** /**
* Move CTOR * Move CTOR
* *
* @param other * @param other
*/ */
Query(Query&& other); Query (Query &&other);
/** /**
* Run a query on the store * Run a query on the store
@ -62,10 +62,9 @@ public:
* *
* @return the query-results, or Nothing in case of error. * @return the query-results, or Nothing in case of error.
*/ */
Option<QueryResults> run(const std::string& expr="", Option<QueryResults> run (const std::string &expr = "",
MuMsgFieldId sortfieldid=MU_MSG_FIELD_ID_NONE, MuMsgFieldId sortfieldid = MU_MSG_FIELD_ID_NONE,
QueryFlags flags=QueryFlags::None, QueryFlags flags = QueryFlags::None, size_t maxnum = 0) const;
size_t maxnum=0) const;
/** /**
* run a Xapian query to count the number of matches; for the syntax, please * run a Xapian query to count the number of matches; for the syntax, please
@ -75,7 +74,7 @@ public:
* *
* @return the number of matches * @return the number of matches
*/ */
size_t count (const std::string& expr="") const; size_t count (const std::string &expr = "") const;
/** /**
* For debugging, get the internal string representation of the parsed * For debugging, get the internal string representation of the parsed
@ -87,12 +86,12 @@ public:
* @return the string representation of the query * @return the string representation of the query
*/ */
std::string parse (const std::string& expr, bool xapian) const; std::string parse (const std::string &expr, bool xapian) const;
private:
private:
struct Private; struct Private;
std::unique_ptr<Private> priv_; std::unique_ptr<Private> priv_;
}; };
} } // namespace Mu
#endif /*__MU_QUERY_HH__*/ #endif /*__MU_QUERY_HH__*/