2011-08-29 22:38:55 +02:00
|
|
|
/*
|
2020-11-03 08:58:59 +01:00
|
|
|
** Copyright (C) 2008-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
2009-11-25 21:55:06 +01:00
|
|
|
**
|
|
|
|
** 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
|
|
|
|
** the Free Software Foundation; either version 3 of the License, or
|
|
|
|
** (at your option) any later version.
|
2011-08-29 22:38:55 +02:00
|
|
|
**
|
2009-11-25 21:55:06 +01:00
|
|
|
** This program is distributed in the hope that it will be useful,
|
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
** GNU General Public License for more details.
|
2011-08-29 22:38:55 +02:00
|
|
|
**
|
2009-11-25 21:55:06 +01:00
|
|
|
** You should have received a copy of the GNU General Public License
|
|
|
|
** along with this program; if not, write to the Free Software Foundation,
|
2011-08-29 22:38:55 +02:00
|
|
|
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
**
|
2009-11-25 21:55:06 +01:00
|
|
|
*/
|
2020-11-03 08:58:59 +01:00
|
|
|
#include <mu-query.hh>
|
2009-11-25 21:55:06 +01:00
|
|
|
|
2010-11-22 23:33:15 +01:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
|
|
|
#include <cctype>
|
|
|
|
#include <cstring>
|
2017-10-24 21:57:57 +02:00
|
|
|
#include <sstream>
|
2020-11-28 09:11:07 +01:00
|
|
|
#include <cmath>
|
2010-11-22 23:33:15 +01:00
|
|
|
|
2017-10-24 21:57:57 +02:00
|
|
|
#include <stdlib.h>
|
2009-11-25 21:55:06 +01:00
|
|
|
#include <xapian.h>
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
|
2010-11-22 23:33:15 +01:00
|
|
|
#include "mu-msg-fields.h"
|
2020-11-28 09:11:07 +01:00
|
|
|
#include "mu-query-results.hh"
|
|
|
|
#include "mu-query-match-deciders.hh"
|
|
|
|
#include "mu-query-threads.hh"
|
2020-11-03 08:58:59 +01:00
|
|
|
#include <mu-xapian.hh>
|
2010-11-22 23:33:15 +01:00
|
|
|
|
2019-12-30 21:28:53 +01:00
|
|
|
using namespace Mu;
|
|
|
|
|
2020-11-03 08:58:59 +01:00
|
|
|
struct Query::Private {
|
|
|
|
Private(const Store& store): store_{store},
|
|
|
|
parser_{store_} {}
|
2020-11-28 09:11:07 +01:00
|
|
|
// 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 Xapian::Query& first_q,
|
|
|
|
const StringSet& thread_ids,
|
|
|
|
MuMsgFieldId sortfieldid, QueryFlags qflags) const;
|
|
|
|
|
2021-02-15 18:11:22 +01:00
|
|
|
Option<QueryResults> run_threaded (QueryResults&& qres, Xapian::Enquire& enq,
|
2021-02-15 21:45:33 +01:00
|
|
|
QueryFlags qflags, size_t max_size) const;
|
2020-11-28 09:11:07 +01:00
|
|
|
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;
|
2011-08-29 22:38:55 +02:00
|
|
|
|
2020-11-03 08:58:59 +01:00
|
|
|
const Store& store_;
|
|
|
|
const Parser parser_;
|
|
|
|
};
|
2011-08-29 22:38:55 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Query::Query(const Store& store):
|
|
|
|
priv_{std::make_unique<Private>(store)}
|
|
|
|
{}
|
|
|
|
|
|
|
|
Query::Query(Query&& other) = default;
|
|
|
|
|
|
|
|
Query::~Query() = default;
|
|
|
|
|
2012-10-17 17:35:23 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
static Xapian::Enquire&
|
|
|
|
maybe_sort (Xapian::Enquire& enq, MuMsgFieldId sortfieldid, QueryFlags qflags)
|
2020-11-03 08:58:59 +01:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
if (sortfieldid != MU_MSG_FIELD_ID_NONE)
|
|
|
|
enq.set_sort_by_value(static_cast<Xapian::valueno>(sortfieldid),
|
|
|
|
any_of(qflags & QueryFlags::Descending));
|
|
|
|
return enq;
|
2020-11-03 08:58:59 +01:00
|
|
|
}
|
2009-12-08 23:01:49 +01:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Xapian::Enquire
|
|
|
|
Query::Private::make_enquire (const std::string& expr,
|
|
|
|
MuMsgFieldId sortfieldid, QueryFlags qflags) const
|
|
|
|
{
|
|
|
|
Xapian::Enquire enq{store_.database()};
|
2011-08-29 22:38:55 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
if (expr.empty() || expr == R"("")")
|
|
|
|
enq.set_query(Xapian::Query::MatchAll);
|
|
|
|
else {
|
|
|
|
WarningVec warns;
|
|
|
|
const auto tree{parser_.parse(expr, warns)};
|
|
|
|
for (auto&& w: warns)
|
|
|
|
g_warning ("query warning: %s", to_string(w).c_str());
|
|
|
|
enq.set_query(xapian_query(tree));
|
2021-03-17 17:33:45 +01:00
|
|
|
g_debug ("qtree: %s", to_string(tree).c_str());
|
2020-11-28 09:11:07 +01:00
|
|
|
}
|
2017-12-03 21:16:32 +01:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
return maybe_sort (enq, sortfieldid, qflags);
|
2009-12-08 23:01:49 +01:00
|
|
|
}
|
2009-11-25 21:55:06 +01:00
|
|
|
|
2020-11-03 08:58:59 +01:00
|
|
|
Xapian::Enquire
|
2020-11-28 09:11:07 +01:00
|
|
|
Query::Private::make_related_enquire (const Xapian::Query& first_q,
|
|
|
|
const StringSet& thread_ids,
|
|
|
|
MuMsgFieldId sortfieldid, QueryFlags qflags) const
|
2012-08-30 23:22:18 +02:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
Xapian::Enquire enq{store_.database()};
|
|
|
|
static std::string pfx (1, mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_THREAD_ID));
|
2012-08-30 23:22:18 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
std::vector<Xapian::Query> qvec{first_q};
|
|
|
|
for (auto&& t: thread_ids)
|
|
|
|
qvec.emplace_back(pfx + t);
|
|
|
|
Xapian::Query qr{Xapian::Query::OP_OR, qvec.begin(), qvec.end()};
|
|
|
|
enq.set_query(qr);
|
2012-08-30 23:22:18 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
return maybe_sort (enq, sortfieldid, qflags);
|
2020-11-03 08:58:59 +01:00
|
|
|
|
2012-08-30 23:22:18 +02:00
|
|
|
}
|
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
struct ThreadKeyMaker: public Xapian::KeyMaker {
|
2021-02-15 18:11:22 +01:00
|
|
|
ThreadKeyMaker (const QueryMatches& matches): match_info_(matches) {}
|
2021-02-12 18:09:00 +01:00
|
|
|
std::string operator()(const Xapian::Document& doc) const override {
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto it{match_info_.find(doc.get_docid())};
|
2021-02-15 18:11:22 +01:00
|
|
|
return (it == match_info_.end()) ? "" : it->second.thread_path;
|
2020-11-28 09:11:07 +01:00
|
|
|
}
|
|
|
|
const QueryMatches& match_info_;
|
|
|
|
};
|
|
|
|
|
|
|
|
Option<QueryResults>
|
2021-02-15 18:11:22 +01:00
|
|
|
Query::Private::run_threaded (QueryResults&& qres, Xapian::Enquire& enq,
|
2021-02-15 21:45:33 +01:00
|
|
|
QueryFlags qflags, size_t max_size) const
|
2012-12-25 16:34:24 +01:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto descending{any_of(qflags & QueryFlags::Descending)};
|
|
|
|
|
2021-02-10 11:35:19 +01:00
|
|
|
calculate_threads(qres, descending);
|
2020-11-28 09:11:07 +01:00
|
|
|
|
2021-02-15 18:11:22 +01:00
|
|
|
ThreadKeyMaker key_maker{qres.query_matches()};
|
2020-11-28 09:11:07 +01:00
|
|
|
enq.set_sort_by_key(&key_maker, descending);
|
|
|
|
|
|
|
|
DeciderInfo minfo;
|
|
|
|
minfo.matches = qres.query_matches();
|
2021-02-15 21:45:33 +01:00
|
|
|
auto mset{enq.get_mset(0, max_size, {},
|
2021-02-15 18:11:22 +01:00
|
|
|
make_thread_decider(qflags, minfo).get())};
|
2021-02-12 18:09:00 +01:00
|
|
|
mset.fetch();
|
2020-11-28 09:11:07 +01:00
|
|
|
|
|
|
|
return QueryResults{mset, std::move(qres.query_matches())};
|
2012-12-25 16:34:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Option<QueryResults>
|
|
|
|
Query::Private::run_singular (const std::string& expr, MuMsgFieldId sortfieldid,
|
|
|
|
QueryFlags qflags, size_t maxnum) const
|
2012-12-16 14:08:34 +01:00
|
|
|
{
|
2021-02-10 21:04:42 +01:00
|
|
|
// i.e. a query _without_ related messages, but still possibly
|
|
|
|
// with threading.
|
|
|
|
//
|
|
|
|
// In the threading case, the sortfield-id is ignored, we always sort by
|
|
|
|
// date (since threading the threading results are always by date.)
|
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto singular_qflags{qflags | QueryFlags::Leader};
|
|
|
|
const auto threading{any_of(qflags & QueryFlags::Threading)};
|
|
|
|
|
|
|
|
DeciderInfo minfo{};
|
2021-02-12 23:48:07 +01:00
|
|
|
#pragma GCC diagnostic push
|
2021-02-10 21:04:42 +01:00
|
|
|
#pragma GCC diagnostic ignored "-Wextra"
|
|
|
|
auto enq{make_enquire(expr, threading ? MU_MSG_FIELD_ID_DATE : sortfieldid, qflags)};
|
|
|
|
#pragma GCC diagnostic ignored "-Wswitch-default"
|
|
|
|
#pragma GCC diagnostic pop
|
2020-11-28 09:11:07 +01:00
|
|
|
auto mset{enq.get_mset(0, maxnum, {}, make_leader_decider(singular_qflags, minfo).get())};
|
2021-02-12 18:09:00 +01:00
|
|
|
mset.fetch();
|
2020-11-28 09:11:07 +01:00
|
|
|
|
|
|
|
auto qres{QueryResults{mset, std::move(minfo.matches)}};
|
2021-02-12 23:48:07 +01:00
|
|
|
|
2021-02-15 21:45:33 +01:00
|
|
|
return threading ? run_threaded(std::move(qres), enq, qflags, maxnum) : qres;
|
2012-12-25 16:34:24 +01:00
|
|
|
}
|
2012-12-16 14:08:34 +01:00
|
|
|
|
2012-12-25 16:34:24 +01:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Option<QueryResults>
|
|
|
|
Query::Private::run_related (const std::string& expr, MuMsgFieldId sortfieldid,
|
|
|
|
QueryFlags qflags, size_t maxnum) const
|
2012-12-25 16:34:24 +01:00
|
|
|
{
|
2021-02-10 21:04:42 +01:00
|
|
|
// i.e. a query _with_ related messages and possibly with threading.
|
|
|
|
//
|
|
|
|
// In the threading case, the sortfield-id is ignored, we always sort by
|
|
|
|
// date (since threading the threading results are always by date.);
|
|
|
|
// moreover, in either threaded or non-threaded case, we sort the first
|
|
|
|
// ("leader") query by date, i.e, we prefer the newest or oldest
|
|
|
|
// (descending) messages.
|
|
|
|
const auto leader_qflags{QueryFlags::Leader | QueryFlags::GatherThreadIds};
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto threading{any_of(qflags & QueryFlags::Threading)};
|
|
|
|
|
2021-02-12 23:48:07 +01:00
|
|
|
// Run our first, "leader" query
|
2020-11-28 09:11:07 +01:00
|
|
|
DeciderInfo minfo{};
|
2021-02-12 23:48:07 +01:00
|
|
|
auto enq{make_enquire(expr, sortfieldid, leader_qflags)};
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto mset{enq.get_mset(0, maxnum, {},
|
|
|
|
make_leader_decider(leader_qflags, minfo).get())};
|
|
|
|
|
2021-02-12 23:48:07 +01:00
|
|
|
// Now, determine the "related query".
|
|
|
|
//
|
|
|
|
// 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.
|
2020-11-28 09:11:07 +01:00
|
|
|
auto r_enq{make_related_enquire(enq.get_query(), minfo.thread_ids,
|
2021-01-25 09:12:48 +01:00
|
|
|
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())};
|
2020-11-28 09:11:07 +01:00
|
|
|
auto qres{QueryResults{r_mset, std::move(minfo.matches)}};
|
2021-02-12 23:48:07 +01:00
|
|
|
|
2021-02-15 21:45:33 +01:00
|
|
|
return threading ? run_threaded(std::move(qres), r_enq, qflags, maxnum) : qres;
|
2012-12-16 14:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Option<QueryResults>
|
|
|
|
Query::Private::run (const std::string& expr, MuMsgFieldId sortfieldid,
|
|
|
|
QueryFlags qflags, size_t maxnum) const
|
|
|
|
{
|
|
|
|
const auto eff_maxnum{maxnum == 0 ? store_.size() : maxnum};
|
2021-02-12 23:48:07 +01:00
|
|
|
#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 };
|
|
|
|
#pragma GCC diagnostic pop
|
2020-11-28 09:11:07 +01:00
|
|
|
if (any_of(qflags & QueryFlags::IncludeRelated))
|
2021-02-12 23:48:07 +01:00
|
|
|
return run_related (expr, eff_sortfield, qflags, eff_maxnum);
|
2020-11-28 09:11:07 +01:00
|
|
|
else
|
2021-02-12 23:48:07 +01:00
|
|
|
return run_singular(expr, eff_sortfield, qflags, eff_maxnum);
|
2020-11-28 09:11:07 +01:00
|
|
|
}
|
2020-11-03 08:58:59 +01:00
|
|
|
|
2012-12-16 14:08:34 +01:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
Option<QueryResults>
|
|
|
|
Query::run (const std::string& expr, MuMsgFieldId sortfieldid,
|
|
|
|
QueryFlags qflags, size_t maxnum) const try
|
2009-11-25 21:55:06 +01:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
// 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::GatherThreadIds), Nothing);
|
|
|
|
|
2021-03-17 17:33:45 +01:00
|
|
|
StopWatch sw{format("ran query '%s'; related: %s; threads: %s; max-size: %zu",
|
2020-11-28 09:11:07 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
} catch (...) {
|
|
|
|
return Nothing;
|
2009-11-25 21:55:06 +01:00
|
|
|
}
|
|
|
|
|
2011-09-12 19:48:30 +02:00
|
|
|
|
2020-01-21 19:50:19 +01:00
|
|
|
size_t
|
2020-11-03 08:58:59 +01:00
|
|
|
Query::count (const std::string& expr) const try
|
2020-01-21 19:50:19 +01:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
const auto enq{priv_->make_enquire(expr, MU_MSG_FIELD_ID_NONE, {})};
|
2020-11-03 08:58:59 +01:00
|
|
|
auto mset{enq.get_mset(0, priv_->store_.size())};
|
2020-03-14 13:27:51 +01:00
|
|
|
mset.fetch();
|
|
|
|
|
|
|
|
return mset.size();
|
2020-01-21 19:50:19 +01:00
|
|
|
|
2020-11-03 08:58:59 +01:00
|
|
|
}MU_XAPIAN_CATCH_BLOCK_RETURN (0);
|
2011-08-29 22:38:55 +02:00
|
|
|
|
2017-10-24 21:57:57 +02:00
|
|
|
|
|
|
|
|
2020-11-03 08:58:59 +01:00
|
|
|
std::string
|
2020-11-28 09:11:07 +01:00
|
|
|
Query::parse (const std::string& expr, bool xapian) const
|
2017-10-24 21:57:57 +02:00
|
|
|
{
|
2020-11-28 09:11:07 +01:00
|
|
|
WarningVec warns;
|
|
|
|
const auto tree{priv_->parser_.parse(expr, warns)};
|
|
|
|
for (auto&& w: warns)
|
|
|
|
g_warning ("query warning: %s", to_string(w).c_str());
|
2017-10-24 21:57:57 +02:00
|
|
|
|
2020-11-28 09:11:07 +01:00
|
|
|
if (xapian)
|
|
|
|
return xapian_query(tree).get_description();
|
|
|
|
else
|
2020-11-03 08:58:59 +01:00
|
|
|
return to_string(tree);
|
2020-11-28 09:11:07 +01:00
|
|
|
}
|