mirror of https://github.com/djcb/mu.git
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
This commit is contained in:
parent
80d84bf635
commit
81689f0af3
|
@ -304,7 +304,7 @@ using ContactSet = std::set<std::reference_wrapper<const Contact>,
|
||||||
ContactLessThan>;
|
ContactLessThan>;
|
||||||
|
|
||||||
void
|
void
|
||||||
ContactsCache::for_each(const EachContactFunc& each_contact, size_t max_num) const
|
ContactsCache::for_each(const EachContactFunc& each_contact) const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l_{priv_->mtx_};
|
std::lock_guard<std::mutex> 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_)
|
for (const auto& item : priv_->contacts_)
|
||||||
sorted.emplace(item.second);
|
sorted.emplace(item.second);
|
||||||
|
|
||||||
// sadly, there's no set::resize
|
// return in _reverse_ order, so we get the most relevant ones first.
|
||||||
size_t n{};
|
for (auto it = sorted.rbegin(); it != sorted.rend(); ++it) {
|
||||||
for (const auto& ci : sorted) {
|
if (!each_contact(*it))
|
||||||
if (max_num > 0 && n++ >= max_num)
|
|
||||||
break;
|
break;
|
||||||
each_contact(ci);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,6 +422,7 @@ test_mu_contacts_cache_sort()
|
||||||
if (g_test_verbose())
|
if (g_test_verbose())
|
||||||
g_print("\t- %s\n", contact.display_name().c_str());
|
g_print("\t- %s\n", contact.display_name().c_str());
|
||||||
str += contact.name;
|
str += contact.name;
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
@ -431,38 +430,38 @@ test_mu_contacts_cache_sort()
|
||||||
|
|
||||||
const auto now{std::time({})};
|
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("");
|
Mu::ContactsCache ccache("");
|
||||||
ccache.add(Mu::Contact{"a@example.com", "a", now, true, 1000, 0});
|
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});
|
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("");
|
Mu::ContactsCache ccache("");
|
||||||
ccache.add(Mu::Contact{"a@example.com", "a", now-2*RecentOffset, true, 1000, 0});
|
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});
|
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("");
|
Mu::ContactsCache ccache("");
|
||||||
ccache.add(Mu::Contact{"a@example.com", "a", now-5*RecentOffset, true, 1000, 0});
|
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});
|
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("");
|
Mu::ContactsCache ccache("");
|
||||||
ccache.add(Mu::Contact{"a@example.com", "a", now, false, 1000, 0});
|
ccache.add(Mu::Contact{"a@example.com", "a", now, false, 1000, 0});
|
||||||
ccache.add(Mu::Contact{"b@example.com", "b", now, false, 1000, 0});
|
ccache.add(Mu::Contact{"b@example.com", "b", now, false, 1000, 0});
|
||||||
g_assert_cmpuint(ccache.size(),==,2);
|
g_assert_cmpuint(ccache.size(),==,2);
|
||||||
assert_equal(result_chars(ccache), "ab");
|
assert_equal(result_chars(ccache), "ba");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,16 +136,18 @@ public:
|
||||||
* Prototype for a callable that receives a contact
|
* Prototype for a callable that receives a contact
|
||||||
*
|
*
|
||||||
* @param contact some contact
|
* @param contact some contact
|
||||||
|
*
|
||||||
|
* @return to get more contacts; false otherwise
|
||||||
*/
|
*/
|
||||||
using EachContactFunc = std::function<void(const Contact& contact_info)>;
|
using EachContactFunc = std::function<bool(const Contact& contact_info)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 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:
|
private:
|
||||||
struct Private;
|
struct Private;
|
||||||
|
|
|
@ -532,30 +532,25 @@ Server::Private::contacts_handler(const Parameters& params)
|
||||||
time_to_string("%c", after).c_str(),
|
time_to_string("%c", after).c_str(),
|
||||||
static_cast<size_t>(tstamp));
|
static_cast<size_t>(tstamp));
|
||||||
|
|
||||||
auto rank{0};
|
auto n{0};
|
||||||
Sexp::List contacts;
|
Sexp::List contacts;
|
||||||
store().contacts_cache().for_each([&](const Contact& ci) {
|
store().contacts_cache().for_each([&](const Contact& ci) {
|
||||||
|
|
||||||
/* since the last time we got some contacts */
|
/* since the last time we got some contacts */
|
||||||
if (tstamp > ci.tstamp)
|
if (tstamp > ci.tstamp)
|
||||||
return;
|
return true;
|
||||||
/* (maybe) only include 'personal' contacts */
|
/* (maybe) only include 'personal' contacts */
|
||||||
if (personal && !ci.personal)
|
if (personal && !ci.personal)
|
||||||
return;
|
return true;
|
||||||
/* only include newer-than-x contacts */
|
/* only include newer-than-x contacts */
|
||||||
if (after > ci.message_date)
|
if (after > ci.message_date)
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
rank++;
|
n++;
|
||||||
|
|
||||||
Sexp::List contact;
|
contacts.add(Sexp::make_string(ci.display_name(true/*encode-if-needed*/)));
|
||||||
contact.add_prop(":address",
|
return maxnum == 0 || n < maxnum;
|
||||||
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<size_t>(maxnum));
|
|
||||||
|
|
||||||
Sexp::List seq;
|
Sexp::List seq;
|
||||||
seq.add_prop(":contacts", Sexp::make_list(std::move(contacts)));
|
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())));
|
g_get_monotonic_time())));
|
||||||
|
|
||||||
/* dump the contacts cache as a giant sexp */
|
/* 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);
|
output_sexp(std::move(seq), Server::OutputFlags::SplitList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,13 +270,14 @@ struct ECData {
|
||||||
time_t after;
|
time_t after;
|
||||||
GRegex* rx;
|
GRegex* rx;
|
||||||
GHashTable* nicks;
|
GHashTable* nicks;
|
||||||
|
size_t maxnum;
|
||||||
size_t n;
|
size_t n;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
each_contact(const Mu::Contact& ci, ECData& ecdata)
|
each_contact(const Mu::Contact& ci, ECData& ecdata)
|
||||||
{
|
{
|
||||||
if (ecdata.personal && ci.personal)
|
if (ecdata.personal && !ci.personal)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ci.message_date < ecdata.after)
|
if (ci.message_date < ecdata.after)
|
||||||
|
@ -338,6 +339,7 @@ run_cmd_cfind(const Mu::Store& store,
|
||||||
const char* pattern,
|
const char* pattern,
|
||||||
gboolean personal,
|
gboolean personal,
|
||||||
time_t after,
|
time_t after,
|
||||||
|
int maxnum,
|
||||||
const MuConfigFormat format,
|
const MuConfigFormat format,
|
||||||
gboolean color,
|
gboolean color,
|
||||||
GError** err)
|
GError** err)
|
||||||
|
@ -360,14 +362,17 @@ run_cmd_cfind(const Mu::Store& store,
|
||||||
ecdata.personal = personal;
|
ecdata.personal = personal;
|
||||||
ecdata.n = 0;
|
ecdata.n = 0;
|
||||||
ecdata.after = after;
|
ecdata.after = after;
|
||||||
|
ecdata.maxnum = maxnum;
|
||||||
ecdata.format = format;
|
ecdata.format = format;
|
||||||
ecdata.color = color;
|
ecdata.color = color;
|
||||||
ecdata.nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
ecdata.nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
|
||||||
|
|
||||||
print_header(format);
|
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);
|
g_hash_table_unref(ecdata.nicks);
|
||||||
|
|
||||||
if (ecdata.rx)
|
if (ecdata.rx)
|
||||||
|
@ -422,6 +427,7 @@ Mu::mu_cmd_cfind(const Mu::Store& store, const MuConfig* opts, GError** err)
|
||||||
opts->params[1],
|
opts->params[1],
|
||||||
opts->personal,
|
opts->personal,
|
||||||
opts->after,
|
opts->after,
|
||||||
|
opts->maxnum,
|
||||||
opts->format,
|
opts->format,
|
||||||
!opts->nocolor,
|
!opts->nocolor,
|
||||||
err);
|
err);
|
||||||
|
|
|
@ -296,6 +296,8 @@ config_options_group_cfind()
|
||||||
"whether to only get 'personal' contacts", NULL},
|
"whether to only get 'personal' contacts", NULL},
|
||||||
{"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after,
|
{"after", 0, 0, G_OPTION_ARG_INT, &MU_CONFIG.after,
|
||||||
"only get addresses last seen after T", "<timestamp>"},
|
"only get addresses last seen after T", "<timestamp>"},
|
||||||
|
{"maxnum", 'n', 0, G_OPTION_ARG_INT, &MU_CONFIG.maxnum,
|
||||||
|
"maximum number of contacts", "<number>"},
|
||||||
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}};
|
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}};
|
||||||
|
|
||||||
og = g_option_group_new("cfind", "Options for the 'cfind' command", "", NULL,
|
og = g_option_group_new("cfind", "Options for the 'cfind' command", "", NULL,
|
||||||
|
|
Loading…
Reference in New Issue