From 81689f0af3de4befd57c830649c13c6c9b0cf62d Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Mon, 9 May 2022 21:17:03 +0300 Subject: [PATCH] contacts-cache: return most relevant contacts Return in the contacts in *reverse* rank order, i.e. the most relevant come first. This is useful since we only want the first maxnum contacts, and those should of course be the most relevant. Update mu cfind/server as well. cfind --- lib/mu-contacts-cache.cc | 29 ++++++++++++++--------------- lib/mu-contacts-cache.hh | 10 ++++++---- lib/mu-server.cc | 23 +++++++++-------------- mu/mu-cmd-cfind.cc | 38 ++++++++++++++++++++++---------------- mu/mu-config.cc | 2 ++ 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/lib/mu-contacts-cache.cc b/lib/mu-contacts-cache.cc index fc3a22ed..366d0451 100644 --- a/lib/mu-contacts-cache.cc +++ b/lib/mu-contacts-cache.cc @@ -304,7 +304,7 @@ using ContactSet = std::set, ContactLessThan>; void -ContactsCache::for_each(const EachContactFunc& each_contact, size_t max_num) const +ContactsCache::for_each(const EachContactFunc& each_contact) const { std::lock_guard l_{priv_->mtx_}; @@ -316,12 +316,10 @@ ContactsCache::for_each(const EachContactFunc& each_contact, size_t max_num) con for (const auto& item : priv_->contacts_) sorted.emplace(item.second); - // sadly, there's no set::resize - size_t n{}; - for (const auto& ci : sorted) { - if (max_num > 0 && n++ >= max_num) + // return in _reverse_ order, so we get the most relevant ones first. + for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) { + if (!each_contact(*it)) break; - each_contact(ci); } } @@ -424,6 +422,7 @@ test_mu_contacts_cache_sort() if (g_test_verbose()) g_print("\t- %s\n", contact.display_name().c_str()); str += contact.name; + return true; }); return str; }; @@ -431,38 +430,38 @@ test_mu_contacts_cache_sort() const auto now{std::time({})}; - // "first" means less relevant + // "first" means more relevant - { /* recent messages, older comes first */ + { /* recent messages, newer comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now-1, true, 1000, 0}); - assert_equal(result_chars(ccache), "ba"); + assert_equal(result_chars(ccache), "ab"); } - { /* non-recent messages, less frequent comes first */ + { /* non-recent messages, more frequent comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now-2*RecentOffset, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now-3*RecentOffset, true, 2000, 0}); - assert_equal(result_chars(ccache), "ab"); + assert_equal(result_chars(ccache), "ba"); } - { /* non-personal comes first */ + { /* personal comes first */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now-5*RecentOffset, true, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now, false, 8000, 0}); - assert_equal(result_chars(ccache), "ba"); + assert_equal(result_chars(ccache), "ab"); } - { /* if all else fails, alphabetically */ + { /* if all else fails, reverse-alphabetically */ Mu::ContactsCache ccache(""); ccache.add(Mu::Contact{"a@example.com", "a", now, false, 1000, 0}); ccache.add(Mu::Contact{"b@example.com", "b", now, false, 1000, 0}); g_assert_cmpuint(ccache.size(),==,2); - assert_equal(result_chars(ccache), "ab"); + assert_equal(result_chars(ccache), "ba"); } } diff --git a/lib/mu-contacts-cache.hh b/lib/mu-contacts-cache.hh index 5b2e2b49..7c871d75 100644 --- a/lib/mu-contacts-cache.hh +++ b/lib/mu-contacts-cache.hh @@ -136,16 +136,18 @@ public: * Prototype for a callable that receives a contact * * @param contact some contact + * + * @return to get more contacts; false otherwise */ - using EachContactFunc = std::function; + using EachContactFunc = std::function; /** - * Invoke some callable for each contact, in order of rank. + * Invoke some callable for each contact, in _descending_ order of rank (i.e., the + * highest ranked contacts come first). * * @param each_contact function invoked for each contact - * @param max_num stop after at most so many contacts, or 0 for no limit */ - void for_each(const EachContactFunc& each_contact, size_t max_num=0) const; + void for_each(const EachContactFunc& each_contact) const; private: struct Private; diff --git a/lib/mu-server.cc b/lib/mu-server.cc index 71cf41a1..8ee380eb 100644 --- a/lib/mu-server.cc +++ b/lib/mu-server.cc @@ -532,30 +532,25 @@ Server::Private::contacts_handler(const Parameters& params) time_to_string("%c", after).c_str(), static_cast(tstamp)); - auto rank{0}; + auto n{0}; Sexp::List contacts; store().contacts_cache().for_each([&](const Contact& ci) { /* since the last time we got some contacts */ if (tstamp > ci.tstamp) - return; + return true; /* (maybe) only include 'personal' contacts */ if (personal && !ci.personal) - return; + return true; /* only include newer-than-x contacts */ if (after > ci.message_date) - return; + return true; - rank++; + n++; - Sexp::List contact; - contact.add_prop(":address", - Sexp::make_string(ci.display_name(true/*encode-if-needed*/))); - contact.add_prop(":rank", Sexp::make_number(rank)); - auto contacts_sexp{Sexp::make_list(std::move(contact))}; - contacts_sexp.formatting_opts |= Sexp::FormattingOptions::SplitList; - contacts.add(std::move(contacts_sexp)); - }, static_cast(maxnum)); + contacts.add(Sexp::make_string(ci.display_name(true/*encode-if-needed*/))); + return maxnum == 0 || n < maxnum; + }); Sexp::List seq; seq.add_prop(":contacts", Sexp::make_list(std::move(contacts))); @@ -564,7 +559,7 @@ Server::Private::contacts_handler(const Parameters& params) g_get_monotonic_time()))); /* dump the contacts cache as a giant sexp */ - g_debug("sending %d of %zu contact(s)", rank, store().contacts_cache().size()); + g_debug("sending %d of %zu contact(s)", n, store().contacts_cache().size()); output_sexp(std::move(seq), Server::OutputFlags::SplitList); } diff --git a/mu/mu-cmd-cfind.cc b/mu/mu-cmd-cfind.cc index 9e5cce00..8d4938ae 100644 --- a/mu/mu-cmd-cfind.cc +++ b/mu/mu-cmd-cfind.cc @@ -265,18 +265,19 @@ print_plain(const std::string& email, const std::string& name, bool color) } struct ECData { - MuConfigFormat format; - gboolean color, personal; - time_t after; - GRegex* rx; - GHashTable* nicks; - size_t n; + MuConfigFormat format; + gboolean color, personal; + time_t after; + GRegex* rx; + GHashTable* nicks; + size_t maxnum; + size_t n; }; static void each_contact(const Mu::Contact& ci, ECData& ecdata) { - if (ecdata.personal && ci.personal) + if (ecdata.personal && !ci.personal) return; if (ci.message_date < ecdata.after) @@ -334,13 +335,14 @@ each_contact(const Mu::Contact& ci, ECData& ecdata) } static MuError -run_cmd_cfind(const Mu::Store& store, - const char* pattern, - gboolean personal, - time_t after, - const MuConfigFormat format, - gboolean color, - GError** err) +run_cmd_cfind(const Mu::Store& store, + const char* pattern, + gboolean personal, + time_t after, + int maxnum, + const MuConfigFormat format, + gboolean color, + GError** err) { ECData ecdata{}; @@ -360,14 +362,17 @@ run_cmd_cfind(const Mu::Store& store, ecdata.personal = personal; ecdata.n = 0; ecdata.after = after; + ecdata.maxnum = maxnum; ecdata.format = format; ecdata.color = color; ecdata.nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); print_header(format); - store.contacts_cache().for_each([&](const auto& ci) { each_contact(ci, ecdata); }); - + store.contacts_cache().for_each([&](const auto& ci) { + each_contact(ci, ecdata); + return ecdata.maxnum == 0 || ecdata.n < ecdata.maxnum; + }); g_hash_table_unref(ecdata.nicks); if (ecdata.rx) @@ -422,6 +427,7 @@ Mu::mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err) opts->params[1], opts->personal, opts->after, + opts->maxnum, opts->format, !opts->nocolor, err); diff --git a/mu/mu-config.cc b/mu/mu-config.cc index f8db3af3..348c5eeb 100644 --- a/mu/mu-config.cc +++ b/mu/mu-config.cc @@ -296,6 +296,8 @@ config_options_group_cfind() "whether to only get 'personal' contacts", NULL}, {"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after, "only get addresses last seen after T", ""}, + {"maxnum", 'n', 0, G_OPTION_ARG_INT, &MU_CONFIG.maxnum, + "maximum number of contacts", ""}, {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}}; og = g_option_group_new("cfind", "Options for the 'cfind' command", "", NULL,