/* -*- mode: c++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2008-2011 Dirk-Jan C. Binnema ** ** 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. ** ** 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. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software Foundation, ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ #include #include #include #include #include #include #include #include "mu-util.h" #include "mu-msg.h" #include "mu-msg-iter.h" #include "mu-threader.h" /* just a guess... */ #define MAX_FETCH_SIZE 10000 class ThreadKeyMaker: public Xapian::KeyMaker { public: ThreadKeyMaker (GHashTable *threadinfo): _threadinfo(threadinfo) {} virtual std::string operator()(const Xapian::Document &doc) const { MuMsgIterThreadInfo *ti; ti = (MuMsgIterThreadInfo*)g_hash_table_lookup (_threadinfo, GUINT_TO_POINTER(doc.get_docid())); return std::string (ti && ti->threadpath ? ti->threadpath : ""); } private: GHashTable *_threadinfo; }; struct _MuMsgIter { public: _MuMsgIter (Xapian::Enquire &enq, size_t maxnum, gboolean threads, MuMsgFieldId sortfield, bool revert): _enq(enq), _thread_hash (0), _msg(0) { _matches = _enq.get_mset (0, maxnum); if (threads && !_matches.empty()) { _matches.fetch(); _thread_hash = mu_threader_calculate (this, _matches.size(), sortfield, revert ? TRUE: FALSE); ThreadKeyMaker keymaker(_thread_hash); enq.set_sort_by_key (&keymaker, false); _matches = _enq.get_mset (0, maxnum); } _cursor = _matches.begin(); /* this seems to make search slightly faster, some * non-scientific testing suggests. 5-10% or so */ if (_matches.size() <= MAX_FETCH_SIZE) _matches.fetch (); } ~_MuMsgIter () { if (_thread_hash) g_hash_table_destroy (_thread_hash); set_msg (NULL); } const Xapian::Enquire& enquire() const { return _enq; } Xapian::MSet& matches() { return _matches; } Xapian::MSet::const_iterator cursor () const { return _cursor; } void set_cursor (Xapian::MSetIterator cur) { _cursor = cur; } void cursor_next () { ++_cursor; } GHashTable *thread_hash () { return _thread_hash; } MuMsg *msg() { return _msg; } MuMsg *set_msg (MuMsg *msg) { if (_msg) mu_msg_unref (_msg); return _msg = msg; } private: const Xapian::Enquire _enq; Xapian::MSet _matches; Xapian::MSet::const_iterator _cursor; GHashTable *_thread_hash; MuMsg *_msg; }; MuMsgIter* mu_msg_iter_new (XapianEnquire *enq, size_t maxnum, gboolean threads, MuMsgFieldId sortfield, gboolean revert) { g_return_val_if_fail (enq, NULL); /* sortfield should be set to .._NONE when we're not threading */ g_return_val_if_fail (threads || sortfield == MU_MSG_FIELD_ID_NONE, NULL); g_return_val_if_fail (mu_msg_field_id_is_valid (sortfield) || sortfield == MU_MSG_FIELD_ID_NONE, FALSE); try { return new MuMsgIter ((Xapian::Enquire&)*enq, maxnum, threads, sortfield, revert ? true : false); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } void mu_msg_iter_destroy (MuMsgIter *iter) { try { delete iter; } MU_XAPIAN_CATCH_BLOCK; } MuMsg* mu_msg_iter_get_msg_floating (MuMsgIter *iter) { g_return_val_if_fail (iter, NULL); g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); try { MuMsg *msg; GError *err; Xapian::Document *docp; docp = new Xapian::Document(iter->cursor().get_document()); err = NULL; msg = iter->set_msg (mu_msg_new_from_doc((XapianDocument*)docp, &err)); if (!msg) MU_HANDLE_G_ERROR(err); return msg; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); } gboolean mu_msg_iter_reset (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); try { iter->set_cursor(iter->matches().begin()); } MU_XAPIAN_CATCH_BLOCK_RETURN (FALSE); return TRUE; } gboolean mu_msg_iter_next (MuMsgIter *iter) { g_return_val_if_fail (iter, FALSE); iter->set_msg (NULL); if (mu_msg_iter_is_done(iter)) return FALSE; try { iter->cursor_next(); return iter->cursor() == iter->matches().end() ? FALSE:TRUE; } MU_XAPIAN_CATCH_BLOCK_RETURN(FALSE); } gboolean mu_msg_iter_is_done (MuMsgIter *iter) { g_return_val_if_fail (iter, TRUE); try { return iter->cursor() == iter->matches().end() ? TRUE : FALSE; } MU_XAPIAN_CATCH_BLOCK_RETURN (TRUE); } /* hmmm.... is it impossible to get a 0 docid, or just very improbable? */ unsigned int mu_msg_iter_get_docid (MuMsgIter *iter) { g_return_val_if_fail (!mu_msg_iter_is_done(iter), (unsigned int)-1); try { return iter->cursor().get_document().get_docid(); } MU_XAPIAN_CATCH_BLOCK_RETURN (0); } const MuMsgIterThreadInfo* mu_msg_iter_get_thread_info (MuMsgIter *iter) { g_return_val_if_fail (!mu_msg_iter_is_done(iter), NULL); g_return_val_if_fail (iter->thread_hash(), NULL); try { const MuMsgIterThreadInfo *ti; unsigned int docid; docid = mu_msg_iter_get_docid (iter); ti = (const MuMsgIterThreadInfo*)g_hash_table_lookup (iter->thread_hash(), GUINT_TO_POINTER(docid)); if (!ti) g_printerr ("no ti for %u\n", docid); return ti; } MU_XAPIAN_CATCH_BLOCK_RETURN (NULL); }