Ticket #295: remotepostsource.patch

File remotepostsource.patch, 31.5 KB (added by Richard Boulton, 16 years ago)

Patch to make PostingSource work with remote databases

  • xapian-core/matcher/externalpostlist.cc

     
    2929
    3030using namespace std;
    3131
    32 ExternalPostList::ExternalPostList(Xapian::PostingSource *source_,
     32ExternalPostList::ExternalPostList(const Xapian::Database & db,
     33                                   Xapian::PostingSource *source_,
    3334                                   double factor_)
    34     : source(source_), current(0), factor(factor_)
     35    : source(source_), source_is_owned(false), current(0), factor(factor_)
    3536{
    3637    Assert(source);
    37     source->reset();
     38    Xapian::PostingSource * newsource = source->clone();
     39    if (newsource != NULL) {
     40        source = newsource;
     41        source_is_owned = true;
     42    }
     43    source->reset(db);
    3844}
    3945
     46ExternalPostList::~ExternalPostList()
     47{
     48    if (source_is_owned) {
     49        delete source;
     50    }
     51}
     52
    4053Xapian::doccount
    4154ExternalPostList::get_termfreq_min() const
    4255{
  • xapian-core/matcher/queryoptimiser.cc

     
    6464
    6565        case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
    6666            Assert(query->external_source);
    67             RETURN(new ExternalPostList(query->external_source, factor));
     67            RETURN(new ExternalPostList(
     68                Xapian::Database(const_cast<Xapian::Database::Internal *>(&db)),
     69                query->external_source, factor));
    6870
    6971        case Xapian::Query::OP_AND:
    7072        case Xapian::Query::OP_FILTER:
  • xapian-core/matcher/externalpostlist.h

     
    2929
    3030class ExternalPostList : public PostList {
    3131    Xapian::PostingSource * source;
     32    bool source_is_owned;
    3233
    3334    Xapian::docid current;
    3435
     
    4344    PostList * update_after_advance();
    4445
    4546  public:
    46     ExternalPostList(Xapian::PostingSource *source_, double factor_);
     47    ExternalPostList(const Xapian::Database & db,
     48                     Xapian::PostingSource *source_,
     49                     double factor_);
     50    ~ExternalPostList();
    4751
    4852    Xapian::doccount get_termfreq_min() const;
    4953
  • xapian-core/tests/api_db.cc

     
    18871887        : num_docs(db.get_doccount()), last_docid(db.get_lastdocid()), did(0)
    18881888    { }
    18891889
    1890     void reset() { did = 0; }
     1890    MyOddPostingSource(Xapian::doccount num_docs_,
     1891                       Xapian::doccount last_docid_)
     1892        : num_docs(num_docs_), last_docid(last_docid_), did(0)
     1893    { }
    18911894
     1895    PostingSource * clone() const { return new MyOddPostingSource(num_docs, last_docid); }
     1896
     1897    void reset(const Xapian::Database &) { did = 0; }
     1898
    18921899    // These bounds could be better, but that's not important here.
    18931900    Xapian::doccount get_termfreq_min() const { return 0; }
    18941901
     
    19251932    Xapian::Enquire enq(db);
    19261933    MyOddPostingSource src(db);
    19271934
    1928     // Check that passing NULL is rejected as intended.
    1929     TEST_EXCEPTION(Xapian::InvalidArgumentError, Xapian::Query bad(NULL));
    1930     Xapian::PostingSource * nullsrc = NULL;
    1931     TEST_EXCEPTION(Xapian::InvalidArgumentError, Xapian::Query bad(nullsrc));
    1932                
    1933     enq.set_query(Xapian::Query(&src));
     1935    enq.set_query(Xapian::Query(src));
    19341936
    19351937    Xapian::MSet mset = enq.get_mset(0, 10);
    19361938    mset_expect_order(mset, 1, 3, 5, 7, 9, 11, 13, 15, 17);
    19371939
    19381940    Xapian::Query q(Xapian::Query::OP_FILTER,
    19391941                    Xapian::Query("leav"),
    1940                     Xapian::Query(&src));
     1942                    Xapian::Query(src));
    19411943    enq.set_query(q);
    19421944
    19431945    mset = enq.get_mset(0, 10);
     
    19531955    Xapian::Enquire enq(db);
    19541956    MyOddPostingSource src(db);
    19551957
    1956     enq.set_query(Xapian::Query(&src));
     1958    enq.set_query(Xapian::Query(src));
    19571959
    19581960    TEST_EXCEPTION(Xapian::UnimplementedError,
    19591961                   Xapian::MSet mset = enq.get_mset(0, 10));
    19601962
    19611963    Xapian::Query q(Xapian::Query::OP_FILTER,
    19621964                    Xapian::Query("leav"),
    1963                     Xapian::Query(&src));
     1965                    Xapian::Query(src));
    19641966    enq.set_query(q);
    19651967
    19661968    TEST_EXCEPTION(Xapian::UnimplementedError,
     
    19811983        : num_docs(db.get_doccount()), last_docid(db.get_lastdocid()), did(0)
    19821984    { }
    19831985
    1984     void reset() { did = 0; }
     1986    MyOddWeightingPostingSource(Xapian::doccount num_docs_,
     1987                                Xapian::doccount last_docid_)
     1988        : num_docs(num_docs_), last_docid(last_docid_), did(0)
     1989    { }
    19851990
     1991    PostingSource * clone() const { return new MyOddWeightingPostingSource(num_docs, last_docid); }
     1992
     1993    void reset(const Xapian::Database &) { did = 0; }
     1994
    19861995    Xapian::weight get_weight() const {
    19871996        return (did % 2) ? 1000 : 0.001;
    19881997    }
     
    20282037    Xapian::Enquire enq(db);
    20292038    MyOddWeightingPostingSource src(db);
    20302039
    2031     enq.set_query(Xapian::Query(&src));
     2040    enq.set_query(Xapian::Query(src));
    20322041
    20332042    Xapian::MSet mset = enq.get_mset(0, 10);
    20342043    mset_expect_order(mset, 1, 3, 5, 7, 9, 11, 13, 15, 17, 2);
    20352044
    20362045    Xapian::Query q(Xapian::Query::OP_OR,
    20372046                    Xapian::Query("leav"),
    2038                     Xapian::Query(&src));
     2047                    Xapian::Query(src));
    20392048    enq.set_query(q);
    20402049
    20412050    mset = enq.get_mset(0, 5);
     
    20512060    tout << "max possible weight = " << mset.get_max_possible() << endl;
    20522061    TEST(mset.get_max_possible() > 1000);
    20532062
    2054     enq.set_query(Xapian::Query(q.OP_SCALE_WEIGHT, Xapian::Query(&src), 0.5));
     2063    enq.set_query(Xapian::Query(q.OP_SCALE_WEIGHT, Xapian::Query(src), 0.5));
    20552064    mset = enq.get_mset(0, 10);
    20562065    TEST(mset.empty());
    20572066
    20582067    TEST_EQUAL(mset.get_max_possible(), 500);
    20592068
    2060     enq.set_query(Xapian::Query(q.OP_SCALE_WEIGHT, Xapian::Query(&src), 2));
     2069    enq.set_query(Xapian::Query(q.OP_SCALE_WEIGHT, Xapian::Query(src), 2));
    20612070    mset = enq.get_mset(0, 10);
    20622071    mset_expect_order(mset, 1, 3, 5, 7, 9, 11, 13, 15, 17);
    20632072
     
    20782087        : num_docs(db.get_doccount()), last_docid(db.get_lastdocid()), did(0)
    20792088    { }
    20802089
    2081     void reset() { did = 0; }
     2090    MyDontAskWeightPostingSource(Xapian::doccount num_docs_,
     2091                                 Xapian::doccount last_docid_)
     2092        : num_docs(num_docs_), last_docid(last_docid_), did(0)
     2093    { }
    20822094
     2095    PostingSource * clone() const { return new MyDontAskWeightPostingSource(num_docs, last_docid); }
     2096
     2097    void reset(const Xapian::Database &) { did = 0; }
     2098
    20832099    Xapian::weight get_weight() const {
    20842100        FAIL_TEST("MyDontAskWeightPostingSource::get_weight() called");
    20852101    }
     
    21262142    MyDontAskWeightPostingSource src(db);
    21272143
    21282144    tout << "OP_SCALE_WEIGHT 0" << endl;
    2129     enq.set_query(Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, Xapian::Query(&src), 0));
     2145    enq.set_query(Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT, Xapian::Query(src), 0));
    21302146
    21312147    Xapian::MSet mset = enq.get_mset(0, 5);
    21322148    mset_expect_order(mset, 1, 2, 3, 4, 5);
     
    21342150    tout << "OP_FILTER" << endl;
    21352151    Xapian::Query q(Xapian::Query::OP_FILTER,
    21362152                    Xapian::Query("leav"),
    2137                     Xapian::Query(&src));
     2153                    Xapian::Query(src));
    21382154    enq.set_query(q);
    21392155
    21402156    mset = enq.get_mset(0, 5);
    21412157    mset_expect_order(mset, 8, 6, 4, 5, 7);
    21422158
    21432159    tout << "BoolWeight" << endl;
    2144     enq.set_query(Xapian::Query(&src));
     2160    enq.set_query(Xapian::Query(src));
    21452161    enq.set_weighting_scheme(Xapian::BoolWeight());
    21462162
    21472163    //mset = enq.get_mset(0, 5);
     
    21512167}
    21522168
    21532169// Check that valueweightsource works correctly.
    2154 DEFINE_TESTCASE(valueweightsource1, backend && !remote) {
    2155     // FIXME: PostingSource doesn't currently work well with multi databases
    2156     // but we should try to resolve that issue.
    2157     SKIP_TEST_FOR_BACKEND("multi");
     2170DEFINE_TESTCASE(valueweightsource1, backend) {
    21582171    Xapian::Database db(get_database("apitest_phrase"));
    21592172    Xapian::Enquire enq(db);
    2160     Xapian::ValueWeightPostingSource src(db, 11);
     2173    Xapian::ValueWeightPostingSource src(11);
    21612174
    21622175    // Should be in descending order of length
    21632176    tout << "RAW" << endl;
    2164     enq.set_query(Xapian::Query(&src));
     2177    enq.set_query(Xapian::Query(src));
    21652178    Xapian::MSet mset = enq.get_mset(0, 5);
    21662179    mset_expect_order(mset, 3, 1, 2, 8, 14);
    21672180
     
    21692182    tout << "OP_FILTER" << endl;
    21702183    Xapian::Query q(Xapian::Query::OP_FILTER,
    21712184                    Xapian::Query("leav"),
    2172                     Xapian::Query(&src));
     2185                    Xapian::Query(src));
    21732186    enq.set_query(q);
    21742187    mset = enq.get_mset(0, 5);
    21752188    mset_expect_order(mset, 8, 6, 4, 5, 7);
     
    21772190    // Should be in descending order of length
    21782191    tout << "OP_FILTER other way" << endl;
    21792192    q = Xapian::Query(Xapian::Query::OP_FILTER,
    2180                       Xapian::Query(&src),
     2193                      Xapian::Query(src),
    21812194                      Xapian::Query("leav"));
    21822195    enq.set_query(q);
    21832196    mset = enq.get_mset(0, 5);
     
    21892202// Check that valueweightsource gives the correct bounds for those databases
    21902203// which support value statistics.
    21912204DEFINE_TESTCASE(valueweightsource2, backend && valuestats) {
    2192     // FIXME: PostingSource doesn't currently work well with multi databases
    2193     // but we should try to resolve that issue.
    2194     SKIP_TEST_FOR_BACKEND("multi");
    21952205    Xapian::Database db(get_database("apitest_phrase"));
    2196     Xapian::ValueWeightPostingSource src(db, 11);
     2206    Xapian::ValueWeightPostingSource src(11);
     2207    src.reset(db);
    21972208    TEST_EQUAL(src.get_termfreq_min(), 17);
    21982209    TEST_EQUAL(src.get_termfreq_est(), 17);
    21992210    TEST_EQUAL(src.get_termfreq_max(), 17);
     
    22042215
    22052216// Check that valueweightsource skip_to() can stay in the same position.
    22062217DEFINE_TESTCASE(valueweightsource3, backend && valuestats) {
    2207     // FIXME: PostingSource doesn't currently work well with multi databases
    2208     // but we should try to resolve that issue.
    2209     SKIP_TEST_FOR_BACKEND("multi");
    22102218    Xapian::Database db(get_database("apitest_phrase"));
    2211     Xapian::ValueWeightPostingSource src(db, 11);
     2219    Xapian::ValueWeightPostingSource src(11);
     2220    src.reset(db);
    22122221    TEST(!src.at_end());
    22132222    src.skip_to(8, 0.0);
    22142223    TEST(!src.at_end());
  • xapian-core/tests/api_percentages.cc

     
    4545        : maxwt(0.0), started(false)
    4646    {}
    4747
     48    MyPostingSource(const std::vector<std::pair<Xapian::docid, Xapian::weight> > &weights_,
     49                    Xapian::weight maxwt_)
     50        : weights(weights_), maxwt(maxwt_), started(false)
     51    {}
     52
     53
     54    PostingSource * clone() const
     55    {
     56        return new MyPostingSource(weights, maxwt);
     57    }
     58
    4859    void append_docweight(Xapian::docid did, Xapian::weight wt)
    4960    {
    5061        weights.push_back(make_pair(did, wt));
     
    5566        if (wt > maxwt) maxwt = wt;
    5667    }
    5768
    58     void reset() { started = false; }
     69    void reset(const Xapian::Database &) { started = false; }
    5970
    6071    Xapian::weight get_weight() const {
    6172        return i->second;
     
    103114        MyPostingSource source;
    104115        source.append_docweight(1, 100);
    105116        source.append_docweight(2, 50 - epsilons * DBL_EPSILON);
    106         enquire.set_query(Xapian::Query(&source));
     117        enquire.set_query(Xapian::Query(source));
    107118        Xapian::MSet mset = enquire.get_mset(0, 10);
    108119        TEST_EQUAL(mset.size(), 2);
    109120        if (mset[1].get_percent() != 50) break;
     
    120131        source.append_docweight(4, 50 - epsilons * DBL_EPSILON);
    121132        source.append_docweight(5, 25);
    122133
    123         enquire.set_query(Xapian::Query(&source));
     134        enquire.set_query(Xapian::Query(source));
    124135        Xapian::MSet mset1 = enquire.get_mset(0, 10);
    125136        TEST_EQUAL(mset1.size(), 5);
    126137        TEST_EQUAL(mset1[2].get_percent(), 50);
  • xapian-core/include/xapian/postingsource.h

     
    137137    /// Return the current docid.
    138138    virtual Xapian::docid get_docid() const = 0;
    139139
     140    /** Clone the posting source.
     141     *
     142     *  The clone should inherit the configuration of the parent, but need not
     143     *  inherit the state.  ie, the clone does not need to be in the same
     144     *  iteration position as the original: the matcher will always call
     145     *  reset() on the clone before attempting to move the iterator, or read
     146     *  the information about the current position of the iterator.
     147     */
     148    virtual PostingSource * clone() const = 0;
     149
     150    /** Name of the posting source, for performing remote searches.
     151     *
     152     *  If the subclass is called FooPostingSource, this should return "Foo".
     153     *
     154     *  This should only be implemented if serialise() and unserialise() are
     155     *  also implemented.
     156     *
     157     *  The default implmenentation returns an empty string, to indicate that
     158     *  serialise() and unserialise() are not implemented.
     159     */
     160    virtual std::string name() const;
     161
     162    /** Serialise object parameters into a string.
     163     *
     164     *  The serialised parameters should represent the configuration of the
     165     *  posting source, but need not (indeed, should not) represent the current
     166     *  iteration state.
     167     */
     168    virtual std::string serialise() const;
     169
     170    /** Create object given string serialisation returned by serialise().
     171     *
     172     *  @param s A serialised instance of this PostingSource subclass.
     173     */
     174    virtual PostingSource * unserialise(const std::string &s) const;
     175
    140176    /** Reset this PostingSource to its freshly constructed state.
    141177     *
    142178     *  This is called automatically by the matcher prior to each query being
    143179     *  processed.
     180     *
     181     *  FIXME - document the db parameter.
    144182     */
    145     virtual void reset() = 0;
     183    virtual void reset(const Database & db) = 0;
    146184
    147185    /** Return a string describing this object.
    148186     *
     
    196234    /// An upper bound on the value returned.
    197235    double max_value;
    198236
     237    /// Upper bound on the value returned specified in constructor.
     238    double specified_max_value;
     239
    199240  public:
    200241    /** Construct a ValueWeightPostingSource.
    201242     *
    202243     *  @param db_ The database to read values from.
    203244     *  @param valno_ The value slot to read values from.
    204245     */
    205     ValueWeightPostingSource(Xapian::Database db_, Xapian::valueno valno_);
     246    ValueWeightPostingSource(Xapian::valueno valno_);
    206247
    207248    /** Construct a ValueWeightPostingSource.
    208249     *
     
    214255     *  constructor need only be used if more accurate information is
    215256     *  available.
    216257     */
    217     ValueWeightPostingSource(Xapian::Database db_, Xapian::valueno valno_,
    218                              double max_weight_);
     258    ValueWeightPostingSource(Xapian::valueno valno_, double max_weight_);
    219259
    220260    Xapian::doccount get_termfreq_min() const;
    221261    Xapian::doccount get_termfreq_est() const;
     
    232272
    233273    Xapian::docid get_docid() const;
    234274
    235     void reset();
     275    ValueWeightPostingSource * clone() const;
     276    std::string name() const;
     277    std::string serialise() const;
     278    PostingSource * unserialise(const std::string &s) const;
    236279
     280    void reset(const Database & db_);
     281
    237282    std::string get_description() const;
    238283};
    239284
  • xapian-core/include/xapian/query.h

     
    2525#ifndef XAPIAN_INCLUDED_QUERY_H
    2626#define XAPIAN_INCLUDED_QUERY_H
    2727
     28#include <map>
    2829#include <string>
    2930#include <vector>
    3031
     
    204205         */
    205206        Query(Query::op op_, Xapian::valueno valno, const std::string &value);
    206207
    207         /// Construct an external source query.
    208         explicit Query(Xapian::PostingSource * external_source);
     208        /** Construct an external source query.
     209         *
     210         *  The posting source will be cloned immediately, so the source
     211         *  supplied may be safely deallocated after this call.
     212         *
     213         *  @param external_source The source to use in the query.
     214         */
     215        explicit Query(const Xapian::PostingSource & external_source);
    209216
    210217        /** A query which matches all documents in the database. */
    211218        static Xapian::Query MatchAll;
     
    405412        /** Destructor. */
    406413        ~Internal();
    407414
    408         static Xapian::Query::Internal * unserialise(const std::string &s);
     415        static Xapian::Query::Internal * unserialise(const std::string &s,
     416                const std::map<std::string, Xapian::PostingSource *> &sources);
    409417
    410418        /** Add a subquery.
    411419         */
  • xapian-core/net/remoteserver.cc

     
    114114    wtschemes[weight->name()] = weight;
    115115    weight = new Xapian::TradWeight;
    116116    wtschemes[weight->name()] = weight;
     117
     118    Xapian::PostingSource * source;
     119    source = new Xapian::ValueWeightPostingSource(0);
     120    postingsources[source->name()] = source;
    117121}
    118122
    119123RemoteServer::~RemoteServer()
     
    121125    delete db;
    122126    // wdb is either NULL or equal to db, so we shouldn't delete it too!
    123127
    124     map<string, Xapian::Weight*>::const_iterator i;
    125     for (i = wtschemes.begin(); i != wtschemes.end(); ++i) {
    126         delete i->second;
     128    {
     129        map<string, Xapian::Weight*>::const_iterator i;
     130        for (i = wtschemes.begin(); i != wtschemes.end(); ++i) {
     131            delete i->second;
     132        }
    127133    }
     134
     135    {
     136        map<string, Xapian::PostingSource *>::const_iterator i;
     137        for (i = postingsources.begin(); i != postingsources.end(); ++i) {
     138            delete i->second;
     139        }
     140    }
    128141}
    129142
    130143message_type
     
    365378
    366379    // Unserialise the Query.
    367380    len = decode_length(&p, p_end, true);
    368     AutoPtr<Xapian::Query::Internal> query(Xapian::Query::Internal::unserialise(string(p, len)));
     381    AutoPtr<Xapian::Query::Internal> query(Xapian::Query::Internal::unserialise(string(p, len), postingsources));
    369382    p += len;
    370383
    371384    // Unserialise assorted Enquire settings.
     
    610623
    611624    send_message(REPLY_ADDDOCUMENT, encode_length(did));
    612625}
     626
     627
     628void
     629RemoteServer::register_posting_source(const Xapian::PostingSource &source)
     630{
     631    if (source.name().empty()) {
     632        throw Xapian::InvalidOperationError("Unable to register posting source - name() method returns empty string.");
     633    }
     634    Xapian::PostingSource * sourceclone = source.clone();
     635    if (!sourceclone) {
     636        throw Xapian::InvalidOperationError("Unable to register posting source - clone() method returns NULL.");
     637    }
     638    try {
     639        postingsources[source.name()] = sourceclone;
     640    } catch(...) {
     641        delete sourceclone;
     642        throw;
     643    }
     644}
  • xapian-core/common/remoteserver.h

     
    2424
    2525#include "xapian/database.h"
    2626#include "xapian/enquire.h"
     27#include "xapian/postingsource.h"
    2728#include "xapian/visibility.h"
    2829
    2930#include "remoteconnection.h"
     
    3132#include <map>
    3233#include <string>
    3334
    34 // Forward declaration
    35 namespace Xapian { class Weight; }
    36 
    3735using namespace std;
    3836
    3937/** Remote backend server base class. */
     
    7068    /// Registered weighting schemes.
    7169    map<string, Xapian::Weight *> wtschemes;
    7270
     71    /// Registered external posting sources.
     72    map<string, Xapian::PostingSource *> postingsources;
     73
    7374    /// Accept a message from the client.
    7475    message_type get_message(Xapian::timeout timeout, string & result,
    7576                             message_type required_type = MSG_MAX);
     
    173174    void register_weighting_scheme(const Xapian::Weight &wt) {
    174175        wtschemes[wt.name()] = wt.clone();
    175176    }
     177
     178    /** Register a user-defined posting source class.
     179     */
     180    void register_posting_source(const Xapian::PostingSource &source);
    176181};
    177182
    178183#endif // XAPIAN_INCLUDED_REMOTESERVER_H
  • xapian-core/api/omqueryinternal.cc

     
    4242#include <cfloat>
    4343#include <climits>
    4444#include <cmath>
     45#include <map>
    4546#include <set>
    4647#include <vector>
    4748
     
    142143        result += '[';
    143144        result += encode_length(tname.length());
    144145        result += tname;
    145         if (term_pos != curpos) result += '@' + om_tostring(term_pos);
    146         if (wqf != 1) result += '#' + om_tostring(wqf);
     146        if (term_pos != curpos) result += '@' + om_tostring(term_pos); // FIXME - should this use encode_length()?
     147        if (wqf != 1) result += '#' + om_tostring(wqf); // FIXME - should this use encode_length()?
    147148        ++curpos;
    148149    } else if (op == Xapian::Query::Internal::OP_EXTERNAL_SOURCE) {
    149         throw Xapian::UnimplementedError("Remote backend doesn't support PostingSource");
     150        string sourcename = external_source->name();
     151        if (sourcename.empty())
     152            throw Xapian::UnimplementedError("This PostingSource doesn't support remote use.");
     153        result += '!';
     154        result += encode_length(sourcename.length());
     155        result += sourcename;
     156        string sourcedata = external_source->serialise();
     157        result += encode_length(sourcedata.length());
     158        result += sourcedata;
    150159    } else {
    151160        result += "(";
    152161        for (subquery_list::const_iterator i = subqs.begin();
     
    158167            case Xapian::Query::Internal::OP_LEAF:
    159168                Assert(false);
    160169                break;
     170            case Xapian::Query::Internal::OP_EXTERNAL_SOURCE:
     171                Assert(false);
     172                break;
    161173            case Xapian::Query::OP_AND:
    162174                result += "&";
    163175                break;
     
    380392    const char *p;
    381393    const char *end;
    382394    Xapian::termpos curpos;
     395    const map<string, Xapian::PostingSource *> & sources;
    383396
    384397    Xapian::Query::Internal * readquery();
     398    Xapian::Query::Internal * readexternal();
    385399    Xapian::Query::Internal * readcompound();
    386400
    387401  public:
    388     QUnserial(const string & s) : p(s.c_str()), end(p + s.size()), curpos(1) { }
     402    QUnserial(const string & s,
     403              const map<string, Xapian::PostingSource *> & sources_)
     404            : p(s.c_str()), end(p + s.size()), curpos(1), sources(sources_) { }
    389405    Xapian::Query::Internal * decode();
    390406};
    391407
     
    424440            ++curpos;
    425441            return new Xapian::Query::Internal(tname, wqf, term_pos);
    426442        }
     443        case '!':
     444            return readexternal();
    427445        case '(':
    428446            return readcompound();
    429447        default:
     
    432450    }
    433451}
    434452
     453Xapian::Query::Internal *
     454QUnserial::readexternal()
     455{
     456    if (p == end)
     457        throw Xapian::InvalidArgumentError("Bad serialised query");
     458
     459    size_t length = decode_length(&p, end, true);
     460    string sourcename(p, length);
     461    map<string, Xapian::PostingSource *>::const_iterator i;
     462    i = sources.find(string(p, length));
     463    if (i == sources.end()) {
     464        throw Xapian::InvalidArgumentError("PostingSource " + string(p, length) + " not registered");
     465    }
     466
     467    p += length;
     468    length = decode_length(&p, end, true);
     469    string sourcedata(p, length);
     470    p += length;
     471
     472    return new Xapian::Query::Internal(i->second->unserialise(sourcedata));
     473}
     474
    435475static Xapian::Query::Internal *
    436476qint_from_vector(Xapian::Query::op op,
    437477                 const vector<Xapian::Query::Internal *> & vec,
     
    476516                    --p;
    477517                    subqs.push_back(readquery());
    478518                    break;
     519                case '!':
     520                    subqs.push_back(readexternal());
     521                    break;
    479522                case '(': {
    480523                    subqs.push_back(readcompound());
    481524                    break;
     
    563606}
    564607
    565608Xapian::Query::Internal *
    566 Xapian::Query::Internal::unserialise(const string &s)
     609Xapian::Query::Internal::unserialise(const string &s,
     610                        const map<string, Xapian::PostingSource *> & sources)
    567611{
    568612    Assert(s.length() > 1);
    569     QUnserial u(s);
     613    QUnserial u(s, sources);
    570614    Xapian::Query::Internal * qint = u.decode();
    571615    AssertEq(s, qint->serialise());
    572616    return qint;
     
    608652          str_parameter(copyme.str_parameter),
    609653          term_pos(copyme.term_pos),
    610654          wqf(copyme.wqf),
    611           external_source(copyme.external_source)
     655          external_source(NULL)
    612656{
    613657    for (subquery_list::const_iterator i = copyme.subqs.begin();
    614658         i != copyme.subqs.end();
    615659         ++i) {
    616660        subqs.push_back(new Xapian::Query::Internal(**i));
    617661    }
     662    if (copyme.external_source) {
     663        external_source = copyme.external_source->clone();
     664    }
    618665}
    619666
    620667//////////////////////////////////////////
     
    627674          parameter(0),
    628675          tname(tname_),
    629676          term_pos(term_pos_),
    630           wqf(wqf_)
     677          wqf(wqf_),
     678          external_source(NULL)
    631679{
    632680    validate_query();
    633681}
     
    638686          parameter(parameter_),
    639687          tname(),
    640688          term_pos(0),
    641           wqf(0)
     689          wqf(0),
     690          external_source(NULL)
    642691{
    643692    if (parameter != 0 && op != OP_PHRASE && op != OP_NEAR && op != OP_ELITE_SET)
    644693        throw Xapian::InvalidArgumentError("parameter is only meaningful for OP_NEAR, OP_PHRASE, or OP_ELITE_SET");
     
    649698        : op(op_),
    650699          parameter(Xapian::termcount(valno)),
    651700          tname(begin),
    652           str_parameter(end)
     701          str_parameter(end),
     702          external_source(NULL)
    653703{
    654704    if (op != OP_VALUE_RANGE)
    655705        throw Xapian::InvalidArgumentError("This constructor is only meaningful for OP_VALUE_RANGE");
     
    660710                                  const std::string &value)
    661711        : op(op_),
    662712          parameter(Xapian::termcount(valno)),
    663           tname(value)
     713          tname(value),
     714          external_source(NULL)
    664715{
    665716    if (op != OP_VALUE_GE && op != OP_VALUE_LE)
    666717        throw Xapian::InvalidArgumentError("This constructor is only meaningful for OP_VALUE_GE or OP_VALUE_LE");
     
    677728Xapian::Query::Internal::Internal(PostingSource * external_source_)
    678729    : op(OP_EXTERNAL_SOURCE), external_source(external_source_)
    679730{
    680     if (!external_source)
    681         throw Xapian::InvalidArgumentError("The external_source parameter can not be NULL");
     731    Assert(external_source);
    682732}
    683733
    684734Xapian::Query::Internal::~Internal()
     
    687737    for (i = subqs.begin(); i != subqs.end(); i++) {
    688738        delete *i;
    689739    }
     740    delete external_source;
    690741}
    691742
    692743Xapian::Query::Internal *
  • xapian-core/api/omquery.cc

     
    3030
    3131#include <xapian/error.h>
    3232#include <xapian/enquire.h>
    33 
     33#include <xapian/postingsource.h>
    3434#include <xapian/termiterator.h>
    3535
    3636#include <vector>
     
    161161                 op_ << ", " << valno << ", " << value);
    162162}
    163163
    164 Query::Query(PostingSource * external_source)
    165         : internal(new Query::Internal(external_source))
     164Query::Query(const PostingSource & external_source)
     165        : internal(NULL)
    166166{
    167167    DEBUGAPICALL(void, "Xapian::Query::Query", external_source);
     168    PostingSource * clone = external_source.clone();
     169    internal = new Query::Internal(clone);
    168170}
    169171
    170172// Copy constructor
  • xapian-core/api/postingsource.cc

     
    3232#include "xapian/queryparser.h" // For sortable_unserialise
    3333
    3434#include "omassert.h"
     35#include "serialise.h"
     36#include "serialise-double.h"
    3537#include "utils.h"
    3638
    3739#include <cfloat>
     
    6870}
    6971
    7072std::string
     73PostingSource::name() const
     74{
     75    return std::string();
     76}
     77
     78std::string
     79PostingSource::serialise() const
     80{
     81    throw Xapian::InvalidOperationError("serialise() not supported for this PostingSource");
     82}
     83
     84PostingSource *
     85PostingSource::unserialise(const std::string &) const
     86{
     87    throw Xapian::InvalidOperationError("unserialise() not supported for this PostingSource");
     88}
     89
     90std::string
    7191PostingSource::get_description() const
    7292{
    7393    return "Xapian::PostingSource subclass";
    7494}
    7595
    7696
    77 ValueWeightPostingSource::ValueWeightPostingSource(Xapian::Database db_,
    78                                                    Xapian::valueno valno_)
    79         : db(db_),
    80           valno(valno_),
    81           current_docid(0),
    82           last_docid(db.get_lastdocid()),
    83           current_value(0.0)
     97ValueWeightPostingSource::ValueWeightPostingSource(Xapian::valueno valno_)
     98        : valno(valno_),
     99          specified_max_value(DBL_MAX)
    84100{
    85     try {
    86         termfreq_max = db.get_value_freq(valno);
    87         termfreq_est = termfreq_max;
    88         termfreq_min = termfreq_max;
    89         max_value = sortable_unserialise(db.get_value_upper_bound(valno));
    90     } catch (const Xapian::UnimplementedError &) {
    91         termfreq_max = db.get_doccount();
    92         termfreq_est = termfreq_max / 2;
    93         termfreq_min = 0;
    94         max_value = DBL_MAX;
    95     }
    96101}
    97102
    98 ValueWeightPostingSource::ValueWeightPostingSource(Xapian::Database db_,
    99                                                    Xapian::valueno valno_,
     103ValueWeightPostingSource::ValueWeightPostingSource(Xapian::valueno valno_,
    100104                                                   double max_weight_)
    101         : db(db_),
    102           valno(valno_),
    103           current_docid(0),
    104           last_docid(db.get_lastdocid()),
    105           current_value(0.0),
    106           max_value(max_weight_)
     105        : valno(valno_),
     106          specified_max_value(max_weight_)
    107107{
    108     try {
    109         termfreq_max = db.get_value_freq(valno);
    110         termfreq_est = termfreq_max;
    111         termfreq_min = termfreq_max;
    112         max_value = std::min(max_value,
    113                              sortable_unserialise(db.get_value_upper_bound(valno)));
    114     } catch (const Xapian::UnimplementedError &) {
    115         termfreq_max = db.get_doccount();
    116         termfreq_est = termfreq_max / 2;
    117         termfreq_min = 0;
    118     }
    119108}
    120109
    121110Xapian::doccount
     
    232221    return current_docid;
    233222}
    234223
     224ValueWeightPostingSource *
     225ValueWeightPostingSource::clone() const
     226{
     227    return new ValueWeightPostingSource(valno, specified_max_value);
     228}
     229
     230std::string
     231ValueWeightPostingSource::name() const
     232{
     233    return std::string("ValueWeight");
     234}
     235
     236std::string
     237ValueWeightPostingSource::serialise() const
     238{
     239    return encode_length(valno) + serialise_double(specified_max_value);
     240}
     241
     242PostingSource *
     243ValueWeightPostingSource::unserialise(const std::string &s) const
     244{
     245    const char * p = s.data();
     246    const char * end = p + s.size();
     247
     248    Xapian::valueno new_valno = decode_length(&p, end, false);
     249    double new_spec_max = unserialise_double(&p, end);
     250
     251    return new ValueWeightPostingSource(new_valno, new_spec_max);
     252}
     253
    235254void
    236 ValueWeightPostingSource::reset()
     255ValueWeightPostingSource::reset(const Database & db_)
    237256{
     257    db = db_;
    238258    current_docid = 0;
     259    current_value = 0.0;
     260    last_docid = db.get_lastdocid();
     261    try {
     262        termfreq_max = db.get_value_freq(valno);
     263        termfreq_est = termfreq_max;
     264        termfreq_min = termfreq_max;
     265        max_value = std::min(specified_max_value,
     266                        sortable_unserialise(db.get_value_upper_bound(valno)));
     267    } catch (const Xapian::UnimplementedError &) {
     268        termfreq_max = db.get_doccount();
     269        termfreq_est = termfreq_max / 2;
     270        termfreq_min = 0;
     271        max_value = specified_max_value;
     272    }
    239273}
    240274
    241275std::string
  • xapian-bindings/python/pythontest2.py

     
    913913            xapian.PostingSource.__init__(self)
    914914            self.max = max
    915915
    916         def reset(self):
     916        def reset(self, db):
    917917            self.current = -1
    918918
    919919        def get_termfreq_min(self): return 0
     
    954954        doc.add_value(1, xapian.sortable_serialise(vals[id]))
    955955        db.add_document(doc)
    956956
    957     source = xapian.ValueWeightPostingSource(db, 1)
     957    source = xapian.ValueWeightPostingSource(1)
    958958    query = xapian.Query(source)
    959959    # del source # Check that query keeps a reference to it.
    960960
  • xapian-bindings/python/generate-python-exceptions.in

     
    4343    get_docid
    4444    get_description
    4545    reset
     46    name
     47    serialise
    4648);
    4749
    4850open FD, ">except.i" or die $!;
  • xapian-bindings/xapian.i

     
    194194
    195195#ifdef XAPIAN_SWIG_DIRECTORS
    196196%feature("director") Xapian::PostingSource;
     197%ignore Xapian::PostingSource::clone;
     198%ignore Xapian::PostingSource::unserialise;
    197199%include <xapian/postingsource.h>
    198200#else
    199201%ignore Xapian::Query(Xapian::PostingSource *);