Ticket #100: customsortorders2.patch

File customsortorders2.patch, 49.0 KB (added by Richard Boulton, 18 years ago)

Updated patch to implement custom sort orders

  • xapian-maintainer-tools/win32msvc/win32_matcher.mak

     
    3232                 $(INTDIR)\multimatch.obj \
    3333                 $(INTDIR)\expand.obj \
    3434                 $(INTDIR)\stats.obj \
     35                 $(INTDIR)\matchcmp.obj \
    3536                 $(INTDIR)\mergepostlist.obj \
    3637                 $(INTDIR)\msetpostlist.obj \
    3738                 $(INTDIR)\msetcmp.obj \
     
    166167   $(CPP_PROJ) $**
    167168<<
    168169
     170"$(INTDIR)\matchcmp.obj" : ".\matchcmp.cc"
     171       $(CPP) @<<
     172   $(CPP_PROJ) $**
     173<<
    169174
    170175"$(INTDIR)\mergepostlist.obj" : ".\mergepostlist.cc"
    171176       $(CPP) @<<
  • xapian-core/matcher/matchcmp.cc

     
     1/* matchcmp.cc: C++ classes for comparing search results.
     2 *
     3 * Copyright 2006 Lemur Consulting Ltd
     4 *
     5 * This program is free software; you can redistribute it and/or
     6 * modify it under the terms of the GNU General Public License as
     7 * published by the Free Software Foundation; either version 2 of the
     8 * License, or (at your option) any later version.
     9 *
     10 * This program is distributed in the hope that it will be useful,
     11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 * GNU General Public License for more details.
     14 *
     15 * You should have received a copy of the GNU General Public License
     16 * along with this program; if not, write to the Free Software
     17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
     18 * USA
     19 */
     20
     21#include <config.h>
     22#include <xapian/enquire.h>
     23#include "serialise.h"
     24#include "serialise-double.h"
     25#include "omdebug.h"
     26#include "omenquireinternal.h"
     27
     28namespace Xapian {
     29
     30weight
     31MatchItem::get_wt() const
     32{
     33    return item.wt;
     34}
     35
     36docid
     37MatchItem::get_docid() const
     38{
     39    return item.did;
     40}
     41
     42Document
     43MatchItem::get_document() const
     44{
     45    return Document(item.get_document().get());
     46}
     47
     48
     49
     50std::string IntegerMatchCmp::serialise() const {
     51    std::string result = encode_length(sort_key);
     52    result += (direction == 1) ? 'A' : 'D';
     53    return result;
     54}
     55
     56IntegerMatchCmp * IntegerMatchCmp::unserialise(const std::string & s) const {
     57    const char *p = s.data();
     58    const char *p_end = p + s.size();
     59    size_t sort_key_ = decode_length(&p, p_end);
     60    Assert(p + 1 == p_end);
     61    return new IntegerMatchCmp(sort_key_, p[0] == 'A' ? 1 : -1);
     62}
     63
     64int
     65IntegerMatchCmp::cmp(const MatchItem & a,
     66                     const MatchItem & b) const
     67{
     68    DEBUGCALL(MATCH, bool, "IntegerMatchCmp::operator()", "");
     69    std::string s = a.get_document().get_value(sort_key);
     70    const char *p = s.data();
     71    const char *p_end = p + s.size();
     72    size_t val_a = decode_length(&p, p_end);
     73
     74    s = b.get_document().get_value(sort_key);
     75    p = s.data();
     76    p_end = p + s.size();
     77    size_t val_b = decode_length(&p, p_end);
     78
     79    if (val_a > val_b)
     80        return direction;
     81    else if (val_a < val_b)
     82        return -direction;
     83    return 0;
     84}
     85
     86std::string
     87IntegerMatchCmp::int_to_value(int num)
     88{
     89    return encode_length(num);
     90}
     91
     92int
     93IntegerMatchCmp::value_to_int(const std::string &s)
     94{
     95    const char *p = s.data();
     96    const char *p_end = p + s.size();
     97    size_t result = decode_length(&p, p_end);
     98    return result;
     99}
     100
     101
     102
     103std::string DoubleMatchCmp::serialise() const {
     104    std::string result = encode_length(sort_key);
     105    result += (direction == 1) ? 'A' : 'D';
     106    return result;
     107}
     108
     109DoubleMatchCmp * DoubleMatchCmp::unserialise(const std::string & s) const {
     110    const char *p = s.data();
     111    const char *p_end = p + s.size();
     112    size_t sort_key_ = decode_length(&p, p_end);
     113    Assert(p + 1 == p_end);
     114    return new DoubleMatchCmp(sort_key_, p[0] == 'A' ? 1 : -1);
     115}
     116
     117int
     118DoubleMatchCmp::cmp(const MatchItem & a,
     119                    const MatchItem & b) const
     120{
     121    DEBUGCALL(MATCH, bool, "DoubleMatchCmp::operator()", "");
     122    std::string s = a.get_document().get_value(sort_key);
     123    const char *p = s.data();
     124    const char *p_end = p + s.size();
     125    double val_a = unserialise_double(&p, p_end);
     126
     127    s = b.get_document().get_value(sort_key);
     128    p = s.data();
     129    p_end = p + s.size();
     130    double val_b = unserialise_double(&p, p_end);
     131
     132    if (val_a > val_b)
     133        return direction;
     134    else if (val_a < val_b)
     135        return -direction;
     136    return 0;
     137}
     138
     139std::string
     140DoubleMatchCmp::double_to_value(double num)
     141{
     142    return serialise_double(num);
     143}
     144
     145double
     146DoubleMatchCmp::value_to_double(const std::string &s)
     147{
     148    const char *p = s.data();
     149    const char *p_end = p + s.size();
     150    double result = unserialise_double(&p, p_end);
     151    return result;
     152}
     153
     154}
  • xapian-core/matcher/Makefile.mk

    Property changes on: xapian-core/matcher/matchcmp.cc
    ___________________________________________________________________
    Name: svn:eol-style
       + native
    
     
    4444        matcher/expandweight.cc\
    4545        matcher/filterpostlist.cc\
    4646        matcher/localmatch.cc\
     47        matcher/matchcmp.cc\
    4748        matcher/mergepostlist.cc\
    4849        matcher/msetcmp.cc\
    4950        matcher/msetpostlist.cc\
  • xapian-core/matcher/multimatch.cc

     
    8989                       Xapian::weight bias_weight_,
    9090                       Xapian::ErrorHandler * errorhandler_,
    9191                       StatsGatherer * gatherer_,
    92                        const Xapian::Weight * weight_)
     92                       const Xapian::Weight * weight_,
     93                       const Xapian::MatchCmp * match_cmp_)
    9394        : gatherer(gatherer_), db(db_), query(query_),
    9495          collapse_key(collapse_key_), percent_cutoff(percent_cutoff_),
    9596          weight_cutoff(weight_cutoff_), order(order_),
     
    9798          sort_value_forward(sort_value_forward_),
    9899          bias_halflife(bias_halflife_), bias_weight(bias_weight_),
    99100          errorhandler(errorhandler_), weight(weight_),
     101          match_cmp(match_cmp_),
    100102          is_remote(db.internal.size())
    101103{
    102104    DEBUGCALL(MATCH, void, "MultiMatch", db_ << ", " << query_ << ", " <<
     
    148150                }
    149151                rem_db->set_query(query, qlen, collapse_key, order, sort_key,
    150152                                  sort_by, sort_value_forward, percent_cutoff,
    151                                   weight_cutoff, weight, subrsets[i]);
     153                                  weight_cutoff, weight, match_cmp,
     154                                  subrsets[i]);
    152155                bool decreasing_relevance =
    153156                    (sort_by == REL || sort_by == REL_VAL);
    154157                smatch = new RemoteSubMatch(rem_db, gatherer.get(),
     
    202205}
    203206
    204207string
    205 MultiMatch::get_collapse_key(PostList *pl, Xapian::docid did,
    206                              Xapian::valueno keyno, Xapian::Internal::RefCntPtr<Xapian::Document::Internal> &doc)
     208MultiMatch::get_collapse_key(PostList *pl,
     209                             Xapian::valueno keyno,
     210                             const Xapian::Internal::MSetItem & item)
    207211{
    208     DEBUGCALL(MATCH, string, "MultiMatch::get_collapse_key", pl << ", " << did << ", " << keyno << ", [doc]");
     212    DEBUGCALL(MATCH, string, "MultiMatch::get_collapse_key", pl << ", " << item.did << ", " << keyno << ", [doc]");
     213
     214    // FIXME - this gets the collapse key from the postlist.  This only
     215    // actually happens for Mset postlists (or mergepostlists containing an
     216    // mset postlist), since this is the only case where the collapse key has
     217    // been read already.  Instead, we should use the document object stored in
     218    // the mset item.
     219
    209220    const string *key = pl->get_collapse_key();
    210221    if (key) RETURN(*key);
    211     if (doc.get() == 0) {
    212         unsigned int multiplier = db.internal.size();
    213         Assert(multiplier != 0);
    214         Xapian::doccount n = (did - 1) % multiplier; // which actual database
    215         Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
    216 
    217         Xapian::Internal::RefCntPtr<Xapian::Document::Internal> temp(db.internal[n]->open_document(m, true));
    218         doc = temp;
    219     }
    220     RETURN(doc->get_value(keyno));
     222    RETURN(item.get_document()->get_value(keyno));
    221223}
    222224
    223225Xapian::weight
     
    240242void
    241243MultiMatch::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
    242244                     Xapian::doccount check_at_least,
    243                      Xapian::MSet & mset, const Xapian::MatchDecider *mdecider)
     245                     Xapian::MSet & mset,
     246                     const Xapian::MatchDecider *mdecider)
    244247{
    245248    DEBUGCALL(MATCH, void, "MultiMatch::get_mset", first << ", " << maxitems
    246249              << ", " << check_at_least << ", ...");
     
    378381
    379382    /// Comparison functor for sorting MSet
    380383    bool sort_forward = (order != Xapian::Enquire::DESCENDING);
    381     MSetCmp mcmp(get_msetcmp_function(sort_by, sort_forward, sort_value_forward));
     384    mset_cmp cmpfn = get_msetcmp_function(sort_by, sort_forward, sort_value_forward);
     385    MSetCmp mcmp(cmpfn, sort_key, match_cmp);
    382386
     387    /// Number of databases making up the database we're using.
     388    const unsigned int db_internal_size = db.internal.size();
     389    Assert(db_internal_size != 0);
     390
    383391    // Perform query
    384392
    385393    // We form the mset in two stages.  In the first we fill up our working
     
    426434        if (min_item.wt > 0.0) wt = pl->get_weight();
    427435
    428436        DEBUGLINE(MATCH, "Candidate document id " << did << " wt " << wt);
    429         Xapian::Internal::MSetItem new_item(wt, did);
    430         if (sort_by != REL) {
    431             const unsigned int multiplier = db.internal.size();
    432             Assert(multiplier != 0);
    433             Xapian::doccount n = (new_item.did - 1) % multiplier; // which actual database
    434             Xapian::docid m = (new_item.did - 1) / multiplier + 1; // real docid in that database
    435             Xapian::Internal::RefCntPtr<Xapian::Document::Internal> doc(db.internal[n]->open_document(m, true));
    436             new_item.sort_key = doc->get_value(sort_key);
    437         }
     437        Xapian::Internal::RefCntPtr<Xapian::Database::Internal> internal_db = db.internal[((did - 1) % db_internal_size)]; // database holding document
     438        Xapian::docid internal_docid = (did - 1) / db_internal_size + 1; // docid in internal_db
     439        Xapian::Internal::MSetItem new_item(wt, did, internal_db, internal_docid);
    438440
    439441        // Test if item has high enough weight (or sort key) to get into
    440442        // proto-mset.
    441443        if (sort_by != REL || min_item.wt > 0.0)
    442444            if (!mcmp(new_item, min_item)) continue;
    443445
    444         Xapian::Internal::RefCntPtr<Xapian::Document::Internal> doc;
    445 
    446446        // Use the decision functor if any.
    447447        if (mdecider != NULL) {
    448448            const unsigned int multiplier = db.internal.size();
     
    451451            // If the results are from a remote database, then the functor will
    452452            // already have been applied there so we can skip this step.
    453453            if (!is_remote[n]) {
    454                 if (doc.get() == 0) {
    455                     Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
    456 
    457                     Xapian::Internal::RefCntPtr<Xapian::Document::Internal> temp(db.internal[n]->open_document(m, true));
    458                     doc = temp;
    459                 }
    460                 Xapian::Document mydoc(doc.get());
     454                Xapian::Document mydoc(new_item.get_document().get());
    461455                if (!mdecider->operator()(mydoc)) continue;
    462456            }
    463457        }
     
    473467
    474468        // Perform collapsing on key if requested.
    475469        if (collapse_key != Xapian::BAD_VALUENO) {
    476             new_item.collapse_key = get_collapse_key(pl, did, collapse_key,
    477                                                      doc);
     470            new_item.collapse_key = get_collapse_key(pl, collapse_key, new_item);
    478471
    479472            // Don't collapse on null key
    480473            if (!new_item.collapse_key.empty()) {
  • xapian-core/matcher/msetcmp.h

     
    2020
    2121#include "omenquireinternal.h"
    2222
     23class MSetCmp;
     24
    2325// typedef for MSetItem comparison function.
    2426typedef bool (* mset_cmp)(const Xapian::Internal::MSetItem &,
    25                           const Xapian::Internal::MSetItem &);
     27                          const Xapian::Internal::MSetItem &,
     28                          const MSetCmp *);
    2629
    2730/// Select the appropriate msetcmp function.
    2831mset_cmp get_msetcmp_function(Xapian::Enquire::Internal::sort_setting sort_by, bool sort_forward, bool sort_value_forward);
     
    3134class MSetCmp {
    3235    mset_cmp fn;
    3336  public:
    34     MSetCmp(mset_cmp fn_) : fn(fn_) { }
     37    Xapian::valueno sort_key;
     38    const Xapian::MatchCmp * cmp;
     39
     40    /// Construct an instance of MSetCmp using a built-in comparison function.
     41    MSetCmp(mset_cmp fn_,
     42            Xapian::valueno sort_key_,
     43            const Xapian::MatchCmp * cmp_)
     44        : fn(fn_), sort_key(sort_key_), cmp(cmp_) { }
     45
    3546    /// Return true if MSetItem a should be ranked above MSetItem b.
    3647    bool operator()(const Xapian::Internal::MSetItem &a,
    37                     const Xapian::Internal::MSetItem &b) const {
    38         return fn(a, b);
     48                    const Xapian::Internal::MSetItem &b) const {
     49        return fn(a, b, this);
    3950    }
    4051};
     52
  • xapian-core/matcher/msetcmp.cc

     
    2020
    2121#include <config.h>
    2222#include "msetcmp.h"
     23#include <string>
    2324
    2425/* We use templates to generate the 14 different comparison functions
    2526 * which we need.  This avoids having to write them all out by hand.
     
    4647// Order by relevance, then docid.
    4748template<bool FORWARD_DID> bool
    4849msetcmp_by_relevance(const Xapian::Internal::MSetItem &a,
    49                      const Xapian::Internal::MSetItem &b)
     50                     const Xapian::Internal::MSetItem &b,
     51                     const MSetCmp *)
    5052{
    5153    if (a.wt > b.wt) return true;
    5254    if (a.wt < b.wt) return false;
     
    5658// Order by value, then docid.
    5759template<bool FORWARD_VALUE, bool FORWARD_DID> bool
    5860msetcmp_by_value(const Xapian::Internal::MSetItem &a,
    59                  const Xapian::Internal::MSetItem &b)
     61                 const Xapian::Internal::MSetItem &b,
     62                 const MSetCmp * cmp)
    6063{
    6164    if (!FORWARD_VALUE) {
    6265        // We want dummy did 0 to compare worse than any other.
    6366        if (a.did == 0) return false;
    6467        if (b.did == 0) return true;
    6568    }
    66     if (a.sort_key > b.sort_key) return FORWARD_VALUE;
    67     if (a.sort_key < b.sort_key) return !FORWARD_VALUE;
     69    std::string sort_key_a = a.get_document()->get_value(cmp->sort_key);
     70    std::string sort_key_b = b.get_document()->get_value(cmp->sort_key);
     71    if (sort_key_a > sort_key_b) return FORWARD_VALUE;
     72    if (sort_key_a < sort_key_b) return !FORWARD_VALUE;
    6873    return msetcmp_by_did<FORWARD_DID, FORWARD_VALUE>(a, b);
    6974}
    7075
    7176// Order by value, then relevance, then docid.
    7277template<bool FORWARD_VALUE, bool FORWARD_DID> bool
    7378msetcmp_by_value_then_relevance(const Xapian::Internal::MSetItem &a,
    74                                 const Xapian::Internal::MSetItem &b)
     79                                const Xapian::Internal::MSetItem &b,
     80                                const MSetCmp * cmp)
    7581{
    7682    if (!FORWARD_VALUE) {
    7783        // two special cases to make min_item compares work when did == 0
    7884        if (a.did == 0) return false;
    7985        if (b.did == 0) return true;
    8086    }
    81     if (a.sort_key > b.sort_key) return FORWARD_VALUE;
    82     if (a.sort_key < b.sort_key) return !FORWARD_VALUE;
     87    std::string sort_key_a = a.get_document()->get_value(cmp->sort_key);
     88    std::string sort_key_b = b.get_document()->get_value(cmp->sort_key);
     89    if (sort_key_a > sort_key_b) return FORWARD_VALUE;
     90    if (sort_key_a < sort_key_b) return !FORWARD_VALUE;
    8391    if (a.wt > b.wt) return true;
    8492    if (a.wt < b.wt) return false;
    8593    return msetcmp_by_did<FORWARD_DID, FORWARD_VALUE>(a, b);
     
    8896// Order by relevance, then value, then docid.
    8997template<bool FORWARD_VALUE, bool FORWARD_DID> bool
    9098msetcmp_by_relevance_then_value(const Xapian::Internal::MSetItem &a,
    91                                 const Xapian::Internal::MSetItem &b)
     99                                const Xapian::Internal::MSetItem &b,
     100                                const MSetCmp * cmp)
    92101{
    93102    if (!FORWARD_VALUE) {
    94103        // two special cases to make min_item compares work when did == 0
     
    97106    }
    98107    if (a.wt > b.wt) return true;
    99108    if (a.wt < b.wt) return false;
    100     if (a.sort_key > b.sort_key) return FORWARD_VALUE;
    101     if (a.sort_key < b.sort_key) return !FORWARD_VALUE;
     109    std::string sort_key_a = a.get_document()->get_value(cmp->sort_key);
     110    std::string sort_key_b = b.get_document()->get_value(cmp->sort_key);
     111    if (sort_key_a > sort_key_b) return FORWARD_VALUE;
     112    if (sort_key_a < sort_key_b) return !FORWARD_VALUE;
    102113    return msetcmp_by_did<FORWARD_DID, FORWARD_VALUE>(a, b);
    103114}
    104115
     116template<bool FORWARD_DID> bool
     117msetcmp_by_user_cmpfn(const Xapian::Internal::MSetItem &a,
     118                      const Xapian::Internal::MSetItem &b,
     119                      const MSetCmp * cmp)
     120{
     121    // We want dummy did 0 to compare worse than any other.
     122    if (a.did == 0) return false;
     123    if (b.did == 0) return true;
     124
     125    if (a.did == b.did) return false; // FIXME - can this ever happen?  If not, convert this to Assert(a.did != b.did).
     126    Xapian::MatchItem item_a(a);
     127    Xapian::MatchItem item_b(b);
     128    int c = (cmp->cmp->cmp)(item_a, item_b);
     129    if (c < 0)
     130        return true;
     131    else if (c > 0)
     132        return false;
     133    if (FORWARD_DID) {
     134        return (a.did < b.did);
     135    } else {
     136        return (a.did > b.did);
     137    }
     138}
     139
    105140static mset_cmp mset_cmp_table[] = {
    106141    // Xapian::Enquire::Internal::REL
    107142    msetcmp_by_relevance<false>,
     
    122157    msetcmp_by_relevance_then_value<true, true>,
    123158    msetcmp_by_relevance_then_value<false, true>,
    124159    msetcmp_by_relevance_then_value<true, false>,
    125     msetcmp_by_relevance_then_value<false, false>
     160    msetcmp_by_relevance_then_value<false, false>,
     161    // Xapian::Enquire::Internal::USER
     162    msetcmp_by_user_cmpfn<false>,
     163    msetcmp_by_user_cmpfn<false>,
     164    msetcmp_by_user_cmpfn<true>,
     165    msetcmp_by_user_cmpfn<true>
    126166};
    127167
    128168mset_cmp get_msetcmp_function(Xapian::Enquire::Internal::sort_setting sort_by, bool sort_forward, bool sort_value_forward) {
  • xapian-core/tests/internaltest.cc

     
    308308        255.5,
    309309        256.125,
    310310        257.03125,
     311        0.12268031290495594321l,
     312        0.12268031290495592933l,
    311313    };
    312314
    313315    check_double_serialisation(0.0);
  • xapian-core/tests/api_db.cc

     
    11491149// set_sort_by_value
    11501150// set_sort_by_value_then_relevance
    11511151// set_sort_by_relevance_then_value
     1152// User comparison functions
    11521153static bool test_sortrel1()
    11531154{
    11541155    Xapian::Enquire enquire(get_database("apitest_sortrel"));
     
    11641165    const Xapian::docid order7[] = { 7,9,8,6,5,4,2,1,3 };
    11651166    const Xapian::docid order8[] = { 7,6,2,9,5,1,8,4,3 };
    11661167    const Xapian::docid order9[] = { 2,6,7,1,5,9,3,4,8 };
     1168    const Xapian::docid order10[] = { 7,9,4,5,6,1,2,3,8 };
     1169    const Xapian::docid order11[] = { 8,1,2,3,4,5,6,7,9 };
     1170    const Xapian::docid order12[] = { 9,7,6,5,4,3,2,1,8 };
     1171    const Xapian::docid order13[] = { 8,3,2,1,6,5,4,9,7 };
    11671172
    11681173    Xapian::MSet mset;
    11691174    size_t i;
     
    11741179        TEST_EQUAL(*mset[i], order1[i]);
    11751180    }
    11761181
     1182    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100));
     1183
     1184    mset = enquire.get_mset(0, 10);
     1185    TEST_EQUAL(mset.size(), sizeof(order10) / sizeof(Xapian::docid));
     1186    for (i = 0; i < sizeof(order10) / sizeof(Xapian::docid); ++i) {
     1187        TEST_EQUAL(*mset[i], order10[i]);
     1188    }
     1189
     1190    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100, true));
     1191
     1192    mset = enquire.get_mset(0, 10);
     1193    TEST_EQUAL(mset.size(), sizeof(order10) / sizeof(Xapian::docid));
     1194    for (i = 0; i < sizeof(order10) / sizeof(Xapian::docid); ++i) {
     1195        TEST_EQUAL(*mset[i], order10[i]);
     1196    }
     1197
     1198    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100, false));
     1199
     1200    mset = enquire.get_mset(0, 10);
     1201    TEST_EQUAL(mset.size(), sizeof(order11) / sizeof(Xapian::docid));
     1202    for (i = 0; i < sizeof(order11) / sizeof(Xapian::docid); ++i) {
     1203        TEST_EQUAL(*mset[i], order11[i]);
     1204    }
     1205
    11771206    enquire.set_sort_by_value_then_relevance(1);
    11781207
    11791208    mset = enquire.get_mset(0, 10);
     
    11901219        TEST_EQUAL(*mset[i], order1[i]);
    11911220    }
    11921221
     1222    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100));
     1223    enquire.set_docid_order(Xapian::Enquire::DESCENDING);
     1224
     1225    mset = enquire.get_mset(0, 10);
     1226    TEST_EQUAL(mset.size(), sizeof(order12) / sizeof(Xapian::docid));
     1227    for (i = 0; i < sizeof(order12) / sizeof(Xapian::docid); ++i) {
     1228        TEST_EQUAL(*mset[i], order12[i]);
     1229    }
     1230
     1231    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100, true));
     1232    enquire.set_docid_order(Xapian::Enquire::DESCENDING);
     1233
     1234    mset = enquire.get_mset(0, 10);
     1235    TEST_EQUAL(mset.size(), sizeof(order12) / sizeof(Xapian::docid));
     1236    for (i = 0; i < sizeof(order12) / sizeof(Xapian::docid); ++i) {
     1237        TEST_EQUAL(*mset[i], order12[i]);
     1238    }
     1239
     1240    enquire.set_sort_by_cmpfn(Xapian::IntegerMatchCmp(100, false));
     1241    enquire.set_docid_order(Xapian::Enquire::DESCENDING);
     1242
     1243    mset = enquire.get_mset(0, 10);
     1244    TEST_EQUAL(mset.size(), sizeof(order13) / sizeof(Xapian::docid));
     1245    for (i = 0; i < sizeof(order13) / sizeof(Xapian::docid); ++i) {
     1246        TEST_EQUAL(*mset[i], order13[i]);
     1247    }
     1248
    11931249    enquire.set_sort_by_value_then_relevance(1);
    11941250    enquire.set_docid_order(Xapian::Enquire::DESCENDING);
    11951251
  • xapian-core/tests/harness/index_utils.cc

     
    7878    for (Xapian::valueno i = min(para.length(), size_t(10)); i >= 1; --i) {
    7979        doc.add_value(i, para.substr(i, 1));
    8080    }
     81    if (para.length() > 1 && para[0] == 'V') {
     82        doc.add_value(100, Xapian::IntegerMatchCmp::int_to_value(atoi(para.c_str() + 1)));
     83    }
    8184
    8285    Xapian::termcount pos = 0;
    8386    string::const_iterator end = para.begin();
  • xapian-core/tests/testdata/apitest_sortrel.txt

     
    1212
    1313V1 woman
    1414
    15 V1 man woman fish
     15V10 man woman fish
    1616
    1717V1 man woman
  • xapian-core/include/xapian/enquire.h

     
    3131#include <xapian/error.h>
    3232#include <xapian/types.h>
    3333#include <xapian/termiterator.h>
     34#include <xapian/document.h>
    3435
    3536namespace Xapian {
    3637
     
    4041class MSetIterator;
    4142class Query;
    4243class Weight;
     44class MatchCmp;
    4345
     46namespace Internal {
     47    class MSetItem;
     48}
     49
    4450/** A match set (MSet).
    4551 *  This class represents (a portion of) the results of a query.
    4652 */
     
    839845        void set_sort_by_relevance_then_value(Xapian::valueno sort_key,
    840846                                              bool ascending = true);
    841847
     848        /** Set a comparison functor to be used when comparing matches.
     849         *
     850         *  This overrides the sort options set by set_sort_by_relevance(),
     851         *  set_sort_by_value(), set_sort_by_relevance_then_value() and
     852         *  set_sort_by_value_then_relevance().  For matches which the
     853         *  comparison functor considers equal (ie, for which the functor
     854         *  returns 0 when comparing them), the matches will be compared in
     855         *  document ID order, as set by set_docid_order();
     856         *
     857         *  The comparison function will be called many times in the process of
     858         *  performing the search.  Therefore, it is important that it is
     859         *  implemented as efficiently as possible.
     860         *
     861         *  @param cmpfn_ The comparion function to use to compare matches.
     862         */
     863        void set_sort_by_cmpfn(const MatchCmp &cmpfn_);
     864
    842865        /** Set the bias functor parameters.
    843866         *
    844867         * NB this is a temporary API for this feature.
     
    12601283        bool get_sumpart_needs_doclength() const;
    12611284};
    12621285
     1286/// An item matching a search.
     1287class MatchItem {
     1288    private:
     1289        const Xapian::Internal::MSetItem & item;
     1290
     1291    public:
     1292        MatchItem(const Xapian::Internal::MSetItem & item_)
     1293            : item(item_) {}
     1294
     1295        /** Get the weight calculated. */
     1296        Xapian::weight get_wt() const;
     1297
     1298        /** Get the document id. */
     1299        Xapian::docid get_docid() const;
     1300
     1301        /** Get the document object, for access to document data, values,
     1302         *  and terms. */
     1303        Xapian::Document get_document() const;
     1304};
     1305
     1306/// Abstract base class for match comparison functors.
     1307class MatchCmp {
     1308    friend class Enquire; // So Enquire can clone us
     1309    friend class ::RemoteServer; // So RemoteServer can clone us - FIXME
     1310    protected:
     1311        MatchCmp(const MatchCmp &);
     1312    private:
     1313        void operator=(MatchCmp &);
     1314
     1315        /** Return a new MatchCmp object of this type.
     1316         *
     1317         * A subclass called FooMatchCmp taking parameters param1 and param2
     1318         * should implement this as:
     1319         *
     1320         * virtual FooMatchCmp * clone() const {
     1321         *     return new FooMatchCmp(param1, param2);
     1322         * }
     1323         */
     1324        virtual MatchCmp * clone() const = 0;
     1325
     1326    public:
     1327        MatchCmp() { }
     1328        virtual ~MatchCmp() { }
     1329
     1330        /** Name of the comparison functor.
     1331         *
     1332         *  If the subclass is called FooMatchCmp, this should return "Foo".
     1333         */
     1334        virtual std::string name() const = 0;
     1335
     1336        /// Serialise object parameters into a string.
     1337        virtual std::string serialise() const = 0;
     1338
     1339        /** Create object given string serialisation returned by serialise().
     1340         *
     1341         *  If the string is not a valid serialised form, this should raise a
     1342         *  Xapian::NetworkError;
     1343         */
     1344        virtual MatchCmp * unserialise(const std::string &s) const = 0;
     1345
     1346        /** Compare a potential Mset entry a to an entry b.
     1347         *
     1348         *  This function should return an integer less than 0 if MatchItem a
     1349         *  should be ranked below MatchItem b, and greater than 0 if MatchItem
     1350         *  a should be ranked above MatchItem b.  The function may return 0 if
     1351         *  it considers the two match items to be equal.
     1352         *
     1353         *  The function will never be called with items which have identical
     1354         *  document IDs.
     1355         *
     1356         *  The comparison function must be well behaved, such that:
     1357         *
     1358         *   - it must produce the same result if called a second time on a
     1359         *     particular pair of documents
     1360         *   - if cmp(x,y) is less than 0 and cmp(y,z) is less than 0, cmp(x,z)
     1361         *     must be less than 0.
     1362         *   - if cmp(x,y) is less than 0, cmp(y,x) must be greater than 0.
     1363         *
     1364         *  Bear in mind that the comparison will be performed very frequently
     1365         *  when performing a search, so must be as fast as possible.  In
     1366         *  particular, although the MatchItem provides access to the document
     1367         *  object, it is recommended that only the value items stored in the
     1368         *  document object are accessed.
     1369         */
     1370        virtual int cmp(const MatchItem & a,
     1371                        const MatchItem & b) const = 0;
     1372};
     1373
     1374/** Comparison function for comparing by value as integer.
     1375 */
     1376class IntegerMatchCmp : public MatchCmp {
     1377        Xapian::valueno sort_key;
     1378        int direction;
     1379    public:
     1380        /** Return a new IntegerMatchCmp with the same parameters as this one.
     1381         */
     1382        IntegerMatchCmp * clone() const {
     1383            return new IntegerMatchCmp(sort_key, direction == 1);
     1384        }
     1385        IntegerMatchCmp(Xapian::valueno sort_key_,
     1386                        bool ascending = true)
     1387                : sort_key(sort_key_),
     1388                  direction(ascending ? 1 : -1) { }
     1389        IntegerMatchCmp()
     1390                : sort_key(0),
     1391                  direction(1) { }
     1392        ~IntegerMatchCmp() { }
     1393        std::string name() const { return "Integer"; }
     1394
     1395        std::string serialise() const;
     1396        IntegerMatchCmp * unserialise(const std::string &s) const;
     1397
     1398        int cmp(const MatchItem & a,
     1399                const MatchItem & b) const;
     1400
     1401        /** Convert an integer to a string, suitable for comparing with this
     1402         *  comparison functor.
     1403         */
     1404        static std::string int_to_value(int num);
     1405
     1406        /** Convert a string suitable for comparison with this comparison
     1407         *  functor to an integer.
     1408         */
     1409        static int value_to_int(const std::string &s);
     1410};
     1411
     1412/** Comparison function for comparing by value as a double.
     1413 */
     1414class DoubleMatchCmp : public MatchCmp {
     1415        Xapian::valueno sort_key;
     1416        int direction;
     1417    public:
     1418        /** Return a new DoubleMatchCmp with the same parameters as this one.
     1419         */
     1420        DoubleMatchCmp * clone() const {
     1421            return new DoubleMatchCmp(sort_key, direction == 1);
     1422        }
     1423        DoubleMatchCmp(Xapian::valueno sort_key_,
     1424                       bool ascending = true)
     1425                : sort_key(sort_key_),
     1426                  direction(ascending ? 1 : -1) { }
     1427        DoubleMatchCmp()
     1428                : sort_key(0),
     1429                  direction(1) { }
     1430        ~DoubleMatchCmp() { }
     1431        std::string name() const { return "Double"; }
     1432
     1433        std::string serialise() const;
     1434        DoubleMatchCmp * unserialise(const std::string &s) const;
     1435
     1436        int cmp(const MatchItem & a,
     1437                const MatchItem & b) const;
     1438
     1439        /** Convert a double to a string, suitable for comparing with this
     1440         *  comparison functor.
     1441         */
     1442        static std::string double_to_value(double num);
     1443
     1444        /** Convert a string suitable for comparison with this comparison
     1445         *  functor to a double.
     1446         */
     1447        static double value_to_double(const std::string &s);
     1448};
     1449
    12631450}
    12641451
    12651452#endif /* XAPIAN_INCLUDED_ENQUIRE_H */
  • xapian-core/net/serialise.cc

     
    217217}
    218218
    219219Xapian::MSet
    220 unserialise_mset(const string &s)
     220unserialise_mset(const string &s,
     221                 Xapian::Internal::RefCntPtr<Xapian::Database::Internal> internal_db)
    221222{
    222223    const char * p = s.data();
    223224    const char * p_end = p + s.size();
     
    236237        size_t len = decode_length(&p, p_end);
    237238        string key(p, len);
    238239        p += len;
    239         items.push_back(Xapian::Internal::MSetItem(wt, did, key,
     240        items.push_back(Xapian::Internal::MSetItem(wt, did,
     241                                                   internal_db,
     242                                                   did,
     243                                                   key,
    240244                                                   decode_length(&p, p_end)));
    241245    }
    242246
  • xapian-core/net/remoteserver.cc

     
    9090    wtschemes[weight->name()] = weight;
    9191    weight = new Xapian::TradWeight();
    9292    wtschemes[weight->name()] = weight;
     93
     94    // Register match comparison functions.
     95    Xapian::MatchCmp * match_cmp;
     96    match_cmp = new Xapian::IntegerMatchCmp();
     97    match_cmps[match_cmp->name()] = match_cmp;
     98    match_cmp = new Xapian::DoubleMatchCmp();
     99    match_cmps[match_cmp->name()] = match_cmp;
    93100}
    94101
    95102RemoteServer::~RemoteServer()
     
    98105    for (i = wtschemes.begin(); i != wtschemes.end(); ++i) {
    99106        delete i->second;
    100107    }
     108    map<string, Xapian::MatchCmp*>::const_iterator j;
     109    for (j = match_cmps.begin(); j != match_cmps.end(); ++j) {
     110        delete j->second;
     111    }
    101112}
    102113
    103114message_type
     
    278289
    279290    Xapian::valueno sort_key = decode_length(&p, p_end);
    280291
    281     if (*p < '0' || *p > '3') {
     292    if (*p < '0' || *p > '4') {
    282293        throw Xapian::NetworkError("bad message (sort_by)");
    283294    }
    284295    Xapian::Enquire::Internal::sort_setting sort_by;
     
    312323    AutoPtr<Xapian::Weight> wt(i->second->unserialise(string(p, len)));
    313324    p += len;
    314325
     326    // Unserialise the MatchCmp object
     327    len = decode_length(&p, p_end);
     328    AutoPtr<Xapian::MatchCmp> mcmp;
     329    if (len > 0) {
     330        map<string, Xapian::MatchCmp *>::const_iterator j;
     331        j = match_cmps.find(string(p, len));
     332        if (j == match_cmps.end()) {
     333            throw Xapian::InvalidArgumentError("MatchCmp type " + string(p, len) + " not registered");
     334        }
     335        p += len;
     336
     337        len = decode_length(&p, p_end);
     338        mcmp = j->second->unserialise(string(p, len));
     339        p += len;
     340    }
     341
    315342    // Unserialise the RSet object.
    316343    Xapian::RSet rset = unserialise_rset(string(p, p_end - p));
    317344
     
    320347    MultiMatch match(*db, query.get(), qlen, rset, collapse_key,
    321348                     percent_cutoff, weight_cutoff, order,
    322349                     sort_key, sort_by, sort_value_forward,
    323                      0, 0, NULL, gatherer, wt.get());
     350                     0, 0, NULL, gatherer, wt.get(), mcmp.get());
    324351
    325352    send_message(REPLY_STATS, serialise_stats(gatherer->get_local_stats()));
    326353
  • xapian-core/common/remote-database.h

     
    122122     * @param percent_cutoff            Percentage cutoff.
    123123     * @param weight_cutoff             Weight cutoff.
    124124     * @param wtscheme                  Weighting scheme.
     125     * @param sort_cmpfn                Comparison functor for sorting.
    125126     * @param omrset                    The rset.
    126127     */
    127128    void set_query(const Xapian::Query::Internal *query,
     
    133134                   bool sort_value_forward,
    134135                   int percent_cutoff, Xapian::weight weight_cutoff,
    135136                   const Xapian::Weight *wtscheme,
     137                   const Xapian::MatchCmp *sort_cmpfn,
    136138                   const Xapian::RSet &omrset);
    137139
    138140    /** Get the Stats from the remote server.
  • xapian-core/common/omenquireinternal.h

     
    3131#include <math.h>
    3232#include <map>
    3333#include <set>
     34#include "document.h"
    3435
    3536using namespace std;
    3637
     
    6465};
    6566
    6667/** An item resulting from a query.
     68 *
    6769 *  This item contains the document id, and the weight calculated for
    6870 *  the document.
     71 *
     72 *  It also provides access to the underlying document,
     73 *  and stores information needed to support collapse operations.
    6974 */
    7075class MSetItem {
    7176    public:
    7277        MSetItem(Xapian::weight wt_, Xapian::docid did_)
    73                 : wt(wt_), did(did_), collapse_count(0) {}
     78                : wt(wt_),
     79                  did(did_),
     80                  collapse_count(0),
     81                  internal_docid(0) {}
    7482
    75         MSetItem(Xapian::weight wt_, Xapian::docid did_, const string &key_)
    76                 : wt(wt_), did(did_), collapse_key(key_), collapse_count(0) {}
     83        MSetItem(Xapian::weight wt_,
     84                 Xapian::docid did_,
     85                 const Xapian::Internal::RefCntPtr<Xapian::Database::Internal> & internal_db_,
     86                 Xapian::docid internal_docid_)
     87                : wt(wt_),
     88                  did(did_),
     89                  collapse_count(0),
     90                  internal_db(internal_db_),
     91                  internal_docid(internal_docid_) {}
    7792
    78         MSetItem(Xapian::weight wt_, Xapian::docid did_, const string &key_,
     93        MSetItem(Xapian::weight wt_,
     94                 Xapian::docid did_,
     95                 const Xapian::Internal::RefCntPtr<Xapian::Database::Internal> & internal_db_,
     96                 Xapian::docid internal_docid_,
     97                 const string &collapse_key_,
    7998                 Xapian::doccount collapse_count_)
    80                 : wt(wt_), did(did_), collapse_key(key_),
    81                   collapse_count(collapse_count_) {}
     99                : wt(wt_),
     100                  did(did_),
     101                  collapse_key(collapse_key_),
     102                  collapse_count(collapse_count_),
     103                  internal_db(internal_db_),
     104                  internal_docid(internal_docid_) {}
    82105
    83106        /** Weight calculated. */
    84107        Xapian::weight wt;
    85108
    86         /** Document id. */
     109        /** Document id (as publically displayed). */
    87110        Xapian::docid did;
    88111
    89112        /** Value which was used to collapse upon.
     
    96119         *  for this item, the value will be a null string.  Only one instance
    97120         *  of each key value (apart from the null string) will be present in
    98121         *  the items in the returned Xapian::MSet.
     122         *
     123         *  FIXME - just use doc.
    99124         */
    100125        string collapse_key;
    101126
    102         /** Count of collapses done on collapse_key so far
     127        /** Count of collapses done on collapse_key so far.
    103128         *
    104          * This is normally 0, and goes up for each collapse done
    105          * It is not neccessarily an indication of how many collapses
    106          * might be done if an exhaustive match was done
     129         *  This is normally 0, and goes up for each collapse done
     130         *  It is not neccessarily an indication of how many collapses
     131         *  might be done if an exhaustive match was done
    107132         */
    108133        Xapian::doccount collapse_count;
    109134
    110         /** Used when sorting by value. */
    111         /* FIXME: why not just cache the Xapian::Document here!?! */
    112         string sort_key;
     135        /** Get the document.
     136         *
     137         *  This will cache the document from previous requests, and will
     138         *  return a pointer to NULL if the MSetItem was not initialised with
     139         *  and internal_db and internal_docid.
     140         */
     141        const Xapian::Internal::RefCntPtr<Xapian::Document::Internal> &
     142        get_document() const
     143        {
     144            if (doc.get() == NULL)
     145            {
     146                if (internal_db.get() == NULL)
     147                {
     148                    Xapian::Internal::RefCntPtr<Xapian::Document::Internal> temp(new Xapian::Document::Internal());
     149                    doc = temp;
     150                } else {
     151                    Xapian::Internal::RefCntPtr<Xapian::Document::Internal> temp(internal_db->open_document(internal_docid, true));
     152                    doc = temp;
     153                }
     154            }
     155            return doc;
     156        }
    113157
    114158        /** Returns a string representing the mset item.
    115159         *  Introspection method.
    116160         */
    117161        string get_description() const;
     162
     163    private:
     164        /** Database containing the document. */
     165        Xapian::Internal::RefCntPtr<Xapian::Database::Internal> internal_db;
     166
     167        /** Document id in database containing the document. */
     168        Xapian::docid internal_docid;
     169
     170        /** Document object.  Points to NULL before the document has
     171         *  been requested (with get_document()). */
     172        mutable Xapian::Internal::RefCntPtr<Xapian::Document::Internal> doc;
    118173};
    119174
    120175}
     
    140195        void operator=(const Internal &);
    141196
    142197    public:
    143         typedef enum { REL, VAL, VAL_REL, REL_VAL } sort_setting;
     198        typedef enum { REL, VAL, VAL_REL, REL_VAL, USER } sort_setting;
    144199
    145200        Xapian::valueno collapse_key;
    146201
     
    153208        Xapian::valueno sort_key;
    154209        sort_setting sort_by;
    155210        bool sort_value_forward;
     211        MatchCmp * sort_cmpfn;
    156212
    157213        time_t bias_halflife;
    158214        Xapian::weight bias_weight;
  • xapian-core/common/multimatch.h

     
    6868        /// Weighting scheme
    6969        const Xapian::Weight * weight;
    7070
     71        /// User supplied match comparison function
     72        const Xapian::MatchCmp * match_cmp;
     73
    7174        /** Internal flag to note that w_max needs to be recalculated
    7275         *  while query is running.
    7376         */
     
    7881
    7982        /// get the collapse key
    8083        string get_collapse_key(PostList *pl,
    81                                 Xapian::docid did, Xapian::valueno keyno,
    82                                 Xapian::Internal::RefCntPtr<Xapian::Document::Internal> &doc);
     84                                Xapian::valueno keyno,
     85                                const Xapian::Internal::MSetItem & item);
    8386
    8487        /** get the maxweight that the postlist pl may return, calling
    8588         *  recalc_maxweight if recalculate_w_max is set, and unsetting it.
     
    121124                   Xapian::weight bias_weight_,
    122125                   Xapian::ErrorHandler * errorhandler,
    123126                   StatsGatherer * gatherer_,
    124                    const Xapian::Weight *wtscheme);
     127                   const Xapian::Weight *wtscheme,
     128                   const Xapian::MatchCmp * match_cmp_);
    125129
    126130        void get_mset(Xapian::doccount first,
    127131                      Xapian::doccount maxitems,
  • xapian-core/common/remoteprotocol.h

     
    2626// 22: Lossless double serialisation
    2727// 23: Support get_lastdocid() on remote databases
    2828// 24: Support for OP_VALUE_RANGE in query serialisation
    29 #define XAPIAN_REMOTE_PROTOCOL_VERSION 24
     29// 25: MatchCmp serialisation
     30#define XAPIAN_REMOTE_PROTOCOL_VERSION 25
    3031
    3132/// Message types (client -> server).
    3233enum message_type {
  • xapian-core/common/remoteserver.h

     
    6666    /// Registered weighting schemes.
    6767    map<string, Xapian::Weight *> wtschemes;
    6868
     69    /// Registered match cmpfns.
     70    map<string, Xapian::MatchCmp *> match_cmps;
     71
    6972    /// Initialisation code needed by both ctors.
    7073    void initialise();
    7174
     
    175178    void register_weighting_scheme(const Xapian::Weight &wt) {
    176179        wtschemes[wt.name()] = wt.clone();
    177180    }
     181
     182    /// Register a user-defined match comparision class.
     183    void register_match_cmp(const Xapian::MatchCmp &match_cmp) {
     184        match_cmps[match_cmp.name()] = match_cmp.clone();
     185    }
    178186};
    179187
    180188#endif // XAPIAN_INCLUDED_REMOTESERVER_H
  • xapian-core/common/serialise.h

     
    2222#define XAPIAN_INCLUDED_SERIALISE_H
    2323
    2424#include <string>
     25#include <xapian/database.h>
    2526
    2627// Forward class declarations:
    2728
     
    108109 *
    109110 *  @return     The unserialised Xapian::MSet object.
    110111 */
    111 Xapian::MSet unserialise_mset(const std::string &s);
     112Xapian::MSet unserialise_mset(const std::string &s,
     113                              Xapian::Internal::RefCntPtr<Xapian::Database::Internal> internal_db);
    112114
    113115/** Serialise a Xapian::RSet object.
    114116 *
  • xapian-core/api/omenquire.cc

     
    644644  : db(db_), query(), collapse_key(Xapian::BAD_VALUENO),
    645645    order(Enquire::ASCENDING), percent_cutoff(0), weight_cutoff(0),
    646646    sort_key(Xapian::BAD_VALUENO), sort_by(REL), sort_value_forward(true),
     647    sort_cmpfn(0),
    647648    bias_halflife(0), bias_weight(0), errorhandler(errorhandler_), weight(0)
    648649{
    649650}
     
    651652Enquire::Internal::~Internal()
    652653{
    653654    delete weight;
     655    delete sort_cmpfn;
    654656    weight = 0;
    655657}
    656658
     
    686688                       percent_cutoff, weight_cutoff,
    687689                       order, sort_key, sort_by, sort_value_forward,
    688690                       bias_halflife, bias_weight, errorhandler,
    689                        new LocalStatsGatherer(), weight);
     691                       new LocalStatsGatherer(), weight, sort_cmpfn);
    690692        // Run query and put results into supplied Xapian::MSet object.
    691693        match.get_mset(first, maxitems, check_at_least, retval, mdecider);
    692694    } else {
     
    694696                       percent_cutoff, weight_cutoff,
    695697                       order, sort_key, sort_by, sort_value_forward,
    696698                       bias_halflife, bias_weight, errorhandler,
    697                        new LocalStatsGatherer(), weight);
     699                       new LocalStatsGatherer(), weight, sort_cmpfn);
    698700        // Run query and put results into supplied Xapian::MSet object.
    699701        match.get_mset(first, maxitems, check_at_least, retval, mdecider);
    700702    }
     
    911913{
    912914    DEBUGAPICALL(void, "Xapian::Enquire::set_weighting_scheme", "[Weight]");
    913915    delete internal->weight;
     916    internal->weight = NULL; // Set to NULL before cloning, to avoid double free if clone() throws an exception.
    914917    internal->weight = weight_.clone();
    915918}
    916919
     
    943946Enquire::set_sort_by_relevance()
    944947{
    945948    internal->sort_by = Internal::REL;
     949    delete internal->sort_cmpfn;
     950    internal->sort_cmpfn = NULL;
    946951}
    947952
    948953void
     
    951956    internal->sort_key = sort_key;
    952957    internal->sort_by = Internal::VAL;
    953958    internal->sort_value_forward = ascending;
     959    delete internal->sort_cmpfn;
     960    internal->sort_cmpfn = NULL;
    954961}
    955962
    956963void
     
    960967    internal->sort_key = sort_key;
    961968    internal->sort_by = Internal::VAL_REL;
    962969    internal->sort_value_forward = ascending;
     970    delete internal->sort_cmpfn;
     971    internal->sort_cmpfn = NULL;
    963972}
    964973
    965974void
     
    969978    internal->sort_key = sort_key;
    970979    internal->sort_by = Internal::REL_VAL;
    971980    internal->sort_value_forward = ascending;
     981    delete internal->sort_cmpfn;
     982    internal->sort_cmpfn = NULL;
    972983}
    973984
    974985void
     986Enquire::set_sort_by_cmpfn(const MatchCmp &cmpfn_)
     987{
     988    DEBUGAPICALL(void, "Xapian::Enquire::set_sort_by_cmpfn", "[cmpfn:" << cmpfn_.name() << "(" << cmpfn_.serialise() << ")]");
     989    delete internal->sort_cmpfn;
     990    internal->sort_by = Internal::USER;
     991    internal->sort_cmpfn = NULL; // Set to NULL before cloning, to avoid double free if clone() throws an exception.
     992    internal->sort_cmpfn = cmpfn_.clone();
     993}
     994
     995void
    975996Enquire::set_sorting(Xapian::valueno sort_key, int sort_bands,
    976997                     bool sort_by_relevance)
    977998{
  • xapian-core/backends/remote/remote-database.cc

     
    379379                         bool sort_value_forward,
    380380                         int percent_cutoff, Xapian::weight weight_cutoff,
    381381                         const Xapian::Weight *wtscheme,
     382                         const Xapian::MatchCmp *sort_cmpfn,
    382383                         const Xapian::RSet &omrset)
    383384{
    384385    string tmp = query->serialise();
     
    395396    message += char(percent_cutoff);
    396397    message += serialise_double(weight_cutoff);
    397398
     399    // Serialise the weight scheme
    398400    tmp = wtscheme->name();
    399401    message += encode_length(tmp.size());
    400402    message += tmp;
    401 
    402403    tmp = wtscheme->serialise();
    403404    message += encode_length(tmp.size());
    404405    message += tmp;
    405406
     407    // Serialise the match cmp object
     408    if (sort_cmpfn == NULL) {
     409        message += encode_length(0);
     410    } else {
     411        tmp = sort_cmpfn->name();
     412        message += encode_length(tmp.size());
     413        message += tmp;
     414        tmp = sort_cmpfn->serialise();
     415        message += encode_length(tmp.size());
     416        message += tmp;
     417    }
     418
    406419    message += serialise_rset(omrset);
    407420
    408421    send_message(MSG_QUERY, message);
     
    436449{
    437450    string message;
    438451    get_message(message, REPLY_RESULTS);
    439     mset = unserialise_mset(message);
     452    mset = unserialise_mset(message, this);
    440453}
    441454
    442455void
  • xapian-bindings/xapian.i

     
    149149int xapian_revision();
    150150
    151151class Weight;
     152class MatchCmp;
    152153class Stopper;
    153154
    154155// from xapian/positioniterator.h
     
    488489                                          bool ascending = true);
    489490    void set_sort_by_relevance_then_value(Xapian::valueno sort_key,
    490491                                          bool ascending = true);
     492    void set_sort_by_cmpfn(const MatchCmp& cmpfn);
    491493
    492494    void set_bias(weight bias_weight, time_t bias_halflife);
    493495
     
    643645        bool get_sumpart_needs_doclength() const;
    644646};
    645647
     648class MatchItem {
     649    public:
     650        MatchItem(const Xapian::Internal::MSetItem & item_);
     651        Xapian::weight get_wt() const;
     652        Xapian::docid get_docid() const;
     653        Xapian::Document get_document() const;
     654};
     655
     656class MatchCmp {
     657/* SWIG doesn't handle this:
     658    private:
     659        virtual MatchCmp * clone() const = 0; */
     660    public:
     661        virtual ~MatchCmp();
     662
     663        virtual std::string name() const = 0;
     664        virtual std::string serialise() const = 0;
     665        virtual MatchCmp * unserialise(const std::string &s) const = 0;
     666
     667        virtual int cmp(const MatchItem & a,
     668                        const MatchItem & b) const = 0;
     669};
     670
     671%warnfilter(842) IntegerMatchCmp::unserialise;
     672class IntegerMatchCmp : public MatchCmp {
     673    public:
     674        IntegerMatchCmp * clone() const;
     675        IntegerMatchCmp(Xapian::valueno sort_key_,
     676                        bool ascending = true);
     677        IntegerMatchCmp();
     678        ~IntegerMatchCmp();
     679
     680        std::string name() const;
     681        std::string serialise() const;
     682        IntegerMatchCmp * unserialise(const std::string &s) const;
     683
     684        int cmp(const MatchItem & a,
     685                const MatchItem & b) const;
     686
     687        static std::string int_to_value(int num);
     688        static int value_to_int(const std::string &s);
     689};
     690
     691%warnfilter(842) DoubleMatchCmp::unserialise;
     692class DoubleMatchCmp : public MatchCmp {
     693    public:
     694        DoubleMatchCmp * clone() const;
     695        DoubleMatchCmp(Xapian::valueno sort_key_,
     696                        bool ascending = true);
     697        DoubleMatchCmp();
     698        ~DoubleMatchCmp();
     699
     700        std::string name() const;
     701        std::string serialise() const;
     702        DoubleMatchCmp * unserialise(const std::string &s) const;
     703
     704        int cmp(const MatchItem & a,
     705                const MatchItem & b) const;
     706
     707        static std::string double_to_value(double num);
     708        static double value_to_double(const std::string &s);
     709};
     710
     711
    646712// xapian/database.h
    647713
    648714class Database {