Website now in git not CVS
[xapian.git] / xapian-core / api / omenquire.cc
blobad04eff31f33c5b3a18ae5f17a85472eb0ecec6a
1 /* omenquire.cc: External interface for running queries
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2001,2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2013,2014,2015,2016 Olly Betts
6 * Copyright 2007,2009 Lemur Consulting Ltd
7 * Copyright 2011, Action Without Borders
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
25 #include <config.h>
26 #include "xapian/enquire.h"
28 #include "xapian/document.h"
29 #include "xapian/error.h"
30 #include "xapian/expanddecider.h"
31 #include "xapian/matchspy.h"
32 #include "xapian/termiterator.h"
33 #include "xapian/weight.h"
35 #include "vectortermlist.h"
37 #include "backends/database.h"
38 #include "debuglog.h"
39 #include "expand/esetinternal.h"
40 #include "expand/expandweight.h"
41 #include "matcher/multimatch.h"
42 #include "omassert.h"
43 #include "api/omenquireinternal.h"
44 #include "str.h"
45 #include "weight/weightinternal.h"
47 #include <algorithm>
48 #include "autoptr.h"
49 #include <cfloat>
50 #include <cmath>
51 #include <vector>
53 using namespace std;
55 using Xapian::Internal::ExpandWeight;
56 using Xapian::Internal::Bo1EWeight;
57 using Xapian::Internal::TradEWeight;
59 namespace Xapian {
61 MatchDecider::~MatchDecider() { }
63 // Methods for Xapian::RSet
65 RSet::RSet() : internal(new RSet::Internal)
69 RSet::RSet(const RSet &other) : internal(other.internal)
73 void
74 RSet::operator=(const RSet &other)
76 internal = other.internal;
79 RSet::~RSet()
83 Xapian::doccount
84 RSet::size() const
86 return internal->items.size();
89 bool
90 RSet::empty() const
92 return internal->items.empty();
95 void
96 RSet::add_document(Xapian::docid did)
98 if (did == 0) throw Xapian::InvalidArgumentError("Docid 0 not valid");
99 internal->items.insert(did);
102 void
103 RSet::remove_document(Xapian::docid did)
105 internal->items.erase(did);
108 bool
109 RSet::contains(Xapian::docid did) const
111 return internal->items.find(did) != internal->items.end();
114 string
115 RSet::get_description() const
117 return "RSet(" + internal->get_description() + ")";
120 string
121 RSet::Internal::get_description() const
123 string description("RSet::Internal(");
125 set<Xapian::docid>::const_iterator i;
126 for (i = items.begin(); i != items.end(); ++i) {
127 if (i != items.begin()) description += ", ";
128 description += str(*i);
131 description += ')';
133 return description;
136 namespace Internal {
138 // Methods for Xapian::MSetItem
140 string
141 MSetItem::get_description() const
143 string description;
145 description = str(did) + ", " + str(wt) + ", " +
146 collapse_key;
148 description = "Xapian::MSetItem(" + description + ")";
150 return description;
155 // Methods for Xapian::MSet
157 MSet::MSet() : internal(new MSet::Internal)
161 MSet::~MSet()
165 MSet::MSet(const MSet & other) : internal(other.internal)
169 MSet &
170 MSet::operator=(const MSet &other)
172 internal = other.internal;
173 return *this;
176 void
177 MSet::fetch_(Xapian::doccount first, Xapian::doccount last) const
179 LOGCALL_VOID(API, "Xapian::MSet::fetch_", first | last);
180 Assert(internal.get() != 0);
181 internal->fetch_items(first, last);
185 MSet::convert_to_percent(double wt) const
187 LOGCALL(API, int, "Xapian::MSet::convert_to_percent", wt);
188 Assert(internal.get() != 0);
189 RETURN(internal->convert_to_percent_internal(wt));
193 MSet::convert_to_percent(const MSetIterator & it) const
195 LOGCALL(API, int, "Xapian::MSet::convert_to_percent", it);
196 Assert(internal.get() != 0);
197 RETURN(internal->convert_to_percent_internal(it.get_weight()));
200 Xapian::doccount
201 MSet::get_termfreq(const string &tname) const
203 LOGCALL(API, Xapian::doccount, "Xapian::MSet::get_termfreq", tname);
204 Assert(internal.get() != 0);
205 if (usual(internal->stats)) {
206 Xapian::doccount termfreq;
207 if (internal->stats->get_stats(tname, termfreq))
208 RETURN(termfreq);
210 if (internal->enquire.get() == 0) {
211 throw InvalidOperationError("Can't get termfreq from an MSet which is not derived from a query.");
213 RETURN(internal->enquire->get_termfreq(tname));
216 double
217 MSet::get_termweight(const string &tname) const
219 LOGCALL(API, double, "Xapian::MSet::get_termweight", tname);
220 Assert(internal.get() != 0);
221 if (!internal->stats) {
222 throw InvalidOperationError("Can't get termweight from an MSet which is not derived from a query.");
224 double termweight;
225 if (!internal->stats->get_termweight(tname, termweight)) {
226 string msg = tname;
227 msg += ": termweight not available";
228 throw InvalidArgumentError(msg);
230 RETURN(termweight);
233 Xapian::doccount
234 MSet::get_firstitem() const
236 Assert(internal.get() != 0);
237 return internal->firstitem;
240 Xapian::doccount
241 MSet::get_matches_lower_bound() const
243 Assert(internal.get() != 0);
244 return internal->matches_lower_bound;
247 Xapian::doccount
248 MSet::get_matches_estimated() const
250 Assert(internal.get() != 0);
251 return internal->matches_estimated;
254 Xapian::doccount
255 MSet::get_matches_upper_bound() const
257 Assert(internal.get() != 0);
258 return internal->matches_upper_bound;
261 Xapian::doccount
262 MSet::get_uncollapsed_matches_lower_bound() const
264 Assert(internal.get() != 0);
265 return internal->uncollapsed_lower_bound;
268 Xapian::doccount
269 MSet::get_uncollapsed_matches_estimated() const
271 Assert(internal.get() != 0);
272 return internal->uncollapsed_estimated;
275 Xapian::doccount
276 MSet::get_uncollapsed_matches_upper_bound() const
278 Assert(internal.get() != 0);
279 return internal->uncollapsed_upper_bound;
282 double
283 MSet::get_max_possible() const
285 Assert(internal.get() != 0);
286 return internal->max_possible;
289 double
290 MSet::get_max_attained() const
292 Assert(internal.get() != 0);
293 return internal->max_attained;
296 string
297 MSet::snippet(const string & text,
298 size_t length,
299 const Xapian::Stem & stemmer,
300 unsigned flags,
301 const string & hi_start,
302 const string & hi_end,
303 const string & omit) const
305 Assert(internal.get() != 0);
306 return internal->snippet(text, length, stemmer, flags,
307 hi_start, hi_end, omit);
310 Xapian::doccount
311 MSet::size() const
313 Assert(internal.get() != 0);
314 return internal->items.size();
317 string
318 MSet::get_description() const
320 Assert(internal.get() != 0);
321 return "Xapian::MSet(" + internal->get_description() + ")";
325 MSet::Internal::convert_to_percent_internal(double wt) const
327 LOGCALL(MATCH, int, "Xapian::MSet::Internal::convert_to_percent_internal", wt);
328 if (percent_factor == 0) RETURN(100);
330 // Excess precision on x86 can result in a difference here.
331 double v = wt * percent_factor + 100.0 * DBL_EPSILON;
332 int pcent = static_cast<int>(v);
333 LOGLINE(MATCH, "wt = " << wt << ", max_possible = " << max_possible <<
334 " => pcent = " << pcent);
335 if (pcent > 100) pcent = 100;
336 if (pcent < 0) pcent = 0;
337 if (pcent == 0 && wt > 0) pcent = 1;
339 RETURN(pcent);
342 Document
343 MSet::Internal::get_doc_by_index(Xapian::doccount index) const
345 LOGCALL(MATCH, Document, "Xapian::MSet::Internal::get_doc_by_index", index);
346 index += firstitem;
347 map<Xapian::doccount, Document>::const_iterator doc;
348 doc = indexeddocs.find(index);
349 if (doc != indexeddocs.end()) {
350 RETURN(doc->second);
352 if (index < firstitem || index >= firstitem + items.size()) {
353 throw RangeError("The mset returned from the match does not contain the document at index " + str(index));
355 Assert(enquire.get());
356 if (!requested_docs.empty()) {
357 // There's already a pending request, so handle that.
358 read_docs();
359 // Maybe we just fetched the doc we want.
360 doc = indexeddocs.find(index);
361 if (doc != indexeddocs.end()) {
362 RETURN(doc->second);
366 RETURN(enquire->get_document(items[index - firstitem]));
369 void
370 MSet::Internal::fetch_items(Xapian::doccount first, Xapian::doccount last) const
372 LOGCALL_VOID(MATCH, "Xapian::MSet::Internal::fetch_items", first | last);
373 if (enquire.get() == 0) {
374 throw InvalidOperationError("Can't fetch documents from an MSet which is not derived from a query.");
376 if (items.empty()) return;
377 if (last > items.size() - 1)
378 last = items.size() - 1;
379 for (Xapian::doccount i = first; i <= last; ++i) {
380 map<Xapian::doccount, Document>::const_iterator doc;
381 doc = indexeddocs.find(i);
382 if (doc == indexeddocs.end()) {
383 /* We don't have the document cached */
384 set<Xapian::doccount>::const_iterator s;
385 s = requested_docs.find(i);
386 if (s == requested_docs.end()) {
387 /* We haven't even requested it yet - do so now. */
388 enquire->request_doc(items[i - firstitem]);
389 requested_docs.insert(i);
395 string
396 MSet::Internal::get_description() const
398 string description = "Xapian::MSet::Internal(";
400 description += "firstitem=" + str(firstitem) + ", " +
401 "matches_lower_bound=" + str(matches_lower_bound) + ", " +
402 "matches_estimated=" + str(matches_estimated) + ", " +
403 "matches_upper_bound=" + str(matches_upper_bound) + ", " +
404 "max_possible=" + str(max_possible) + ", " +
405 "max_attained=" + str(max_attained);
407 for (vector<Xapian::Internal::MSetItem>::const_iterator i = items.begin();
408 i != items.end(); ++i) {
409 if (!description.empty()) description += ", ";
410 description += i->get_description();
413 description += ")";
415 return description;
418 void
419 MSet::Internal::read_docs() const
421 set<Xapian::doccount>::const_iterator i;
422 for (i = requested_docs.begin(); i != requested_docs.end(); ++i) {
423 indexeddocs[*i] = enquire->read_doc(items[*i - firstitem]);
424 LOGLINE(MATCH, "stored doc at index " << *i << " is " << indexeddocs[*i]);
426 /* Clear list of requested but not fetched documents. */
427 requested_docs.clear();
430 // Methods for Xapian::ESet
432 ESet::ESet() : internal(new Internal) { }
434 ESet::~ESet()
438 ESet::ESet(const ESet & other) : internal(other.internal)
442 void
443 ESet::operator=(const ESet &other)
445 internal = other.internal;
448 Xapian::termcount
449 ESet::get_ebound() const
451 return internal->ebound;
454 Xapian::termcount
455 ESet::size() const
457 return internal->items.size();
460 bool
461 ESet::empty() const
463 return internal->items.empty();
466 void
467 ESet::swap(ESet & other)
469 std::swap(internal, other.internal);
472 ESetIterator
473 ESet::begin() const
475 return ESetIterator(0, *this);
478 ESetIterator
479 ESet::end() const
481 Assert(internal.get() != 0);
482 return ESetIterator(internal->items.size(), *this);
485 ESetIterator
486 ESet::operator[](Xapian::termcount i) const
488 // Don't test 0 <= i - that gives a compiler warning if i is unsigned
489 Assert(0 < (i + 1) && i < size());
490 return ESetIterator(i, *this);
493 ESetIterator
494 ESet::back() const
496 Assert(!empty());
497 Assert(internal.get() != 0);
498 return ESetIterator(internal->items.size() - 1, *this);
501 string
502 ESet::get_description() const
504 Assert(internal.get() != 0);
505 return "Xapian::ESet(" + internal->get_description() + ")";
508 // Xapian::ESetIterator
510 const string &
511 ESetIterator::operator *() const
513 Assert(eset.internal.get());
514 AssertRel(index,<,eset.internal->items.size());
515 return eset.internal->items[index].term;
518 double
519 ESetIterator::get_weight() const
521 Assert(eset.internal.get());
522 AssertRel(index,<,eset.internal->items.size());
523 return eset.internal->items[index].wt;
526 string
527 ESetIterator::get_description() const
529 return "Xapian::ESetIterator(" + str(index) + ")";
532 // MSetIterator
534 Xapian::docid
535 MSetIterator::operator *() const
537 Assert(mset.internal.get());
538 Xapian::doccount size = mset.internal->items.size();
539 Xapian::doccount index = size - off_from_end;
540 AssertRel(index,<,size);
541 return mset.internal->items[index].did;
544 Document
545 MSetIterator::get_document() const
547 Assert(mset.internal.get());
548 Xapian::doccount size = mset.internal->items.size();
549 Xapian::doccount index = size - off_from_end;
550 AssertRel(index,<,size);
551 return mset.internal->get_doc_by_index(index);
554 double
555 MSetIterator::get_weight() const
557 Assert(mset.internal.get());
558 Xapian::doccount size = mset.internal->items.size();
559 Xapian::doccount index = size - off_from_end;
560 AssertRel(index,<,size);
561 return mset.internal->items[index].wt;
564 std::string
565 MSetIterator::get_collapse_key() const
567 Assert(mset.internal.get());
568 Xapian::doccount size = mset.internal->items.size();
569 Xapian::doccount index = size - off_from_end;
570 AssertRel(index,<,size);
571 return mset.internal->items[index].collapse_key;
574 Xapian::doccount
575 MSetIterator::get_collapse_count() const
577 Assert(mset.internal.get());
578 Xapian::doccount size = mset.internal->items.size();
579 Xapian::doccount index = size - off_from_end;
580 AssertRel(index,<,size);
581 return mset.internal->items[index].collapse_count;
584 string
585 MSetIterator::get_description() const
587 return "Xapian::MSetIterator(" + str(mset.size() - off_from_end) + ")";
590 // Methods for Xapian::Enquire::Internal
592 Enquire::Internal::Internal(const Database &db_)
593 : db(db_), query(), collapse_key(Xapian::BAD_VALUENO), collapse_max(0),
594 order(Enquire::ASCENDING), percent_cutoff(0), weight_cutoff(0),
595 sort_key(Xapian::BAD_VALUENO), sort_by(REL), sort_value_forward(true),
596 sorter(0), time_limit(0.0), weight(0),
597 eweightname("trad"), expand_k(1.0)
599 if (db.internal.empty()) {
600 throw InvalidArgumentError("Can't make an Enquire object from an uninitialised Database object.");
604 Enquire::Internal::~Internal()
606 delete weight;
607 weight = 0;
610 void
611 Enquire::Internal::set_query(const Query &query_, termcount qlen_)
613 query = query_;
614 qlen = qlen_ ? qlen_ : query.get_length();
617 const Query &
618 Enquire::Internal::get_query() const
620 return query;
623 MSet
624 Enquire::Internal::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
625 Xapian::doccount check_at_least, const RSet *rset,
626 const MatchDecider *mdecider) const
628 LOGCALL(MATCH, MSet, "Enquire::Internal::get_mset", first | maxitems | check_at_least | rset | mdecider);
630 if (percent_cutoff && (sort_by == VAL || sort_by == VAL_REL)) {
631 throw Xapian::UnimplementedError("Use of a percentage cutoff while sorting primary by value isn't currently supported");
634 if (weight == 0) {
635 weight = new BM25Weight;
638 Xapian::doccount first_orig = first;
640 Xapian::doccount docs = db.get_doccount();
641 first = min(first, docs);
642 maxitems = min(maxitems, docs);
643 check_at_least = min(check_at_least, docs);
644 check_at_least = max(check_at_least, maxitems);
647 AutoPtr<Xapian::Weight::Internal> stats(new Xapian::Weight::Internal);
648 ::MultiMatch match(db, query, qlen, rset,
649 collapse_max, collapse_key,
650 percent_cutoff, weight_cutoff,
651 order, sort_key, sort_by, sort_value_forward,
652 time_limit, *(stats.get()), weight, spies,
653 (sorter.get() != NULL),
654 (mdecider != NULL));
655 // Run query and put results into supplied Xapian::MSet object.
656 MSet retval;
657 match.get_mset(first, maxitems, check_at_least, retval,
658 *(stats.get()), mdecider, sorter.get());
659 if (first_orig != first && retval.internal.get()) {
660 retval.internal->firstitem = first_orig;
663 Assert(weight->name() != "bool" || retval.get_max_possible() == 0);
665 // The Xapian::MSet needs to have a pointer to ourselves, so that it can
666 // retrieve the documents. This is set here explicitly to avoid having
667 // to pass it into the matcher, which gets messy particularly in the
668 // networked case.
669 retval.internal->enquire = this;
671 if (!retval.internal->stats) {
672 retval.internal->stats = stats.release();
675 RETURN(retval);
678 ESet
679 Enquire::Internal::get_eset(Xapian::termcount maxitems,
680 const RSet & rset, int flags,
681 const ExpandDecider * edecider_,
682 double min_wt) const
684 LOGCALL(MATCH, ESet, "Enquire::Internal::get_eset", maxitems | rset | flags | edecider_ | min_wt);
686 using Xapian::Internal::opt_intrusive_ptr;
687 opt_intrusive_ptr<const ExpandDecider> edecider(edecider_);
688 if (maxitems == 0 || rset.empty()) {
689 // Either we were asked for no results, or wouldn't produce any
690 // because no documents were marked as relevant.
691 RETURN(ESet());
694 LOGVALUE(MATCH, rset.size());
696 if (!query.empty() && !(flags & Enquire::INCLUDE_QUERY_TERMS)) {
697 opt_intrusive_ptr<const ExpandDecider> decider_noquery(
698 (new ExpandDeciderFilterTerms(query.get_terms_begin(),
699 query.get_terms_end()))->release());
700 if (edecider.get()) {
701 edecider = (new ExpandDeciderAnd(decider_noquery.get(),
702 edecider.get()))->release();
703 } else {
704 edecider = decider_noquery;
708 bool use_exact_termfreq(flags & Enquire::USE_EXACT_TERMFREQ);
709 Xapian::ESet eset;
711 if (eweightname == "bo1") {
712 Bo1EWeight bo1eweight(db, rset.size(), use_exact_termfreq);
713 eset.internal->expand(maxitems, db, rset, edecider.get(), bo1eweight,
714 min_wt);
715 } else {
716 TradEWeight tradeweight(db, rset.size(), use_exact_termfreq, expand_k);
717 eset.internal->expand(maxitems, db, rset, edecider.get(), tradeweight,
718 min_wt);
721 RETURN(eset);
724 class ByQueryIndexCmp {
725 private:
726 typedef map<string, unsigned int> tmap_t;
727 const tmap_t &tmap;
728 public:
729 ByQueryIndexCmp(const tmap_t &tmap_) : tmap(tmap_) {}
730 bool operator()(const string &left,
731 const string &right) const {
732 tmap_t::const_iterator l, r;
733 l = tmap.find(left);
734 r = tmap.find(right);
735 Assert((l != tmap.end()) && (r != tmap.end()));
737 return l->second < r->second;
741 TermIterator
742 Enquire::Internal::get_matching_terms(Xapian::docid did) const
744 if (query.empty())
745 return TermIterator();
747 // The ordered list of terms in the query.
748 TermIterator qt = query.get_terms_begin();
750 // copy the list of query terms into a map for faster access.
751 // FIXME: a hash would be faster than a map, if this becomes
752 // a problem.
753 map<string, unsigned int> tmap;
754 unsigned int index = 1;
755 for ( ; qt != query.get_terms_end(); ++qt) {
756 if (tmap.find(*qt) == tmap.end())
757 tmap[*qt] = index++;
760 vector<string> matching_terms;
762 TermIterator docterms = db.termlist_begin(did);
763 TermIterator docterms_end = db.termlist_end(did);
764 while (docterms != docterms_end) {
765 string term = *docterms;
766 map<string, unsigned int>::iterator t = tmap.find(term);
767 if (t != tmap.end()) matching_terms.push_back(term);
768 ++docterms;
771 // sort the resulting list by query position.
772 sort(matching_terms.begin(), matching_terms.end(), ByQueryIndexCmp(tmap));
774 return TermIterator(new VectorTermList(matching_terms.begin(),
775 matching_terms.end()));
778 TermIterator
779 Enquire::Internal::get_matching_terms(const MSetIterator &it) const
781 // FIXME: take advantage of MSetIterator to ensure that database
782 // doesn't get modified underneath us.
783 return get_matching_terms(*it);
786 Xapian::doccount
787 Enquire::Internal::get_termfreq(const string &tname) const
789 return db.get_termfreq(tname);
792 string
793 Enquire::Internal::get_description() const
795 string description = db.get_description();
796 description += ", ";
797 description += query.get_description();
798 return description;
801 // Private methods for Xapian::Enquire::Internal
803 void
804 Enquire::Internal::request_doc(const Xapian::Internal::MSetItem &item) const
806 unsigned int multiplier = db.internal.size();
808 Xapian::docid realdid = (item.did - 1) / multiplier + 1;
809 Xapian::doccount dbnumber = (item.did - 1) % multiplier;
811 db.internal[dbnumber]->request_document(realdid);
814 Document
815 Enquire::Internal::read_doc(const Xapian::Internal::MSetItem &item) const
817 unsigned int multiplier = db.internal.size();
819 Xapian::docid realdid = (item.did - 1) / multiplier + 1;
820 Xapian::doccount dbnumber = (item.did - 1) % multiplier;
822 Xapian::Document::Internal *doc;
823 doc = db.internal[dbnumber]->collect_document(realdid);
824 return Document(doc);
827 Document
828 Enquire::Internal::get_document(const Xapian::Internal::MSetItem &item) const
830 unsigned int multiplier = db.internal.size();
832 Xapian::docid realdid = (item.did - 1) / multiplier + 1;
833 Xapian::doccount dbnumber = (item.did - 1) % multiplier;
835 // We know the doc exists, so open lazily.
836 return Document(db.internal[dbnumber]->open_document(realdid, true));
839 // Methods of Xapian::Enquire
841 Enquire::Enquire(const Enquire & other) : internal(other.internal)
843 LOGCALL_CTOR(API, "Enquire", other);
846 void
847 Enquire::operator=(const Enquire & other)
849 LOGCALL_VOID(API, "Xapian::Enquire::operator=", other);
850 internal = other.internal;
853 Enquire::Enquire(const Database &databases)
854 : internal(new Internal(databases))
856 LOGCALL_CTOR(API, "Enquire", databases);
859 Enquire::Enquire(const Database &databases, ErrorHandler *)
860 : internal(new Internal(databases))
862 LOGCALL_CTOR(API, "Enquire", databases | Literal("errorhandler"));
865 Enquire::~Enquire()
867 LOGCALL_DTOR(API, "Enquire");
870 void
871 Enquire::set_query(const Query & query, termcount len)
873 LOGCALL_VOID(API, "Xapian::Enquire::set_query", query | len);
874 internal->set_query(query, len);
877 const Query &
878 Enquire::get_query() const
880 LOGCALL(API, const Xapian::Query &, "Xapian::Enquire::get_query", NO_ARGS);
881 RETURN(internal->get_query());
884 void
885 Enquire::add_matchspy(MatchSpy * spy) {
886 LOGCALL_VOID(API, "Xapian::Enquire::add_matchspy", spy);
887 internal->spies.push_back(spy);
890 void
891 Enquire::clear_matchspies() {
892 LOGCALL_VOID(API, "Xapian::Enquire::clear_matchspies", NO_ARGS);
893 internal->spies.clear();
896 void
897 Enquire::set_weighting_scheme(const Weight &weight_)
899 LOGCALL_VOID(API, "Xapian::Enquire::set_weighting_scheme", weight_);
900 // Clone first in case doing so throws an exception.
901 Weight * wt = weight_.clone();
902 swap(wt, internal->weight);
903 delete wt;
906 void
907 Enquire::set_expansion_scheme(const std::string &eweightname_, double expand_k_) const
909 LOGCALL_VOID(API, "Xapian::Enquire::set_expansion_scheme", eweightname_ | expand_k_);
911 if (eweightname_ != "bo1" && eweightname_ != "trad") {
912 throw InvalidArgumentError("Invalid name for query expansion scheme.");
915 internal->eweightname = eweightname_;
916 internal->expand_k = expand_k_;
919 void
920 Enquire::set_collapse_key(Xapian::valueno collapse_key, Xapian::doccount collapse_max)
922 if (collapse_key == Xapian::BAD_VALUENO) collapse_max = 0;
923 internal->collapse_key = collapse_key;
924 internal->collapse_max = collapse_max;
927 void
928 Enquire::set_docid_order(Enquire::docid_order order)
930 internal->order = order;
933 void
934 Enquire::set_cutoff(int percent_cutoff, double weight_cutoff)
936 internal->percent_cutoff = percent_cutoff;
937 internal->weight_cutoff = weight_cutoff;
940 void
941 Enquire::set_sort_by_relevance()
943 internal->sort_by = Internal::REL;
946 void
947 Enquire::set_sort_by_value(valueno sort_key, bool ascending)
949 internal->sorter = NULL;
950 internal->sort_key = sort_key;
951 internal->sort_by = Internal::VAL;
952 internal->sort_value_forward = ascending;
955 void
956 Enquire::set_sort_by_value_then_relevance(valueno sort_key, bool ascending)
958 internal->sorter = NULL;
959 internal->sort_key = sort_key;
960 internal->sort_by = Internal::VAL_REL;
961 internal->sort_value_forward = ascending;
964 void
965 Enquire::set_sort_by_relevance_then_value(valueno sort_key, bool ascending)
967 internal->sorter = NULL;
968 internal->sort_key = sort_key;
969 internal->sort_by = Internal::REL_VAL;
970 internal->sort_value_forward = ascending;
973 void
974 Enquire::set_sort_by_key(KeyMaker * sorter, bool ascending)
976 if (sorter == NULL)
977 throw InvalidArgumentError("sorter can't be NULL");
978 internal->sorter = sorter;
979 internal->sort_by = Internal::VAL;
980 internal->sort_value_forward = ascending;
983 void
984 Enquire::set_sort_by_key_then_relevance(KeyMaker * sorter, bool ascending)
986 if (sorter == NULL)
987 throw InvalidArgumentError("sorter can't be NULL");
988 internal->sorter = sorter;
989 internal->sort_by = Internal::VAL_REL;
990 internal->sort_value_forward = ascending;
993 void
994 Enquire::set_sort_by_relevance_then_key(KeyMaker * sorter, bool ascending)
996 if (sorter == NULL)
997 throw Xapian::InvalidArgumentError("sorter can't be NULL");
998 internal->sorter = sorter;
999 internal->sort_by = Internal::REL_VAL;
1000 internal->sort_value_forward = ascending;
1003 void
1004 Enquire::set_time_limit(double time_limit)
1006 internal->time_limit = time_limit;
1009 MSet
1010 Enquire::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
1011 Xapian::doccount check_at_least, const RSet *rset,
1012 const MatchDecider *mdecider) const
1014 LOGCALL(API, Xapian::MSet, "Xapian::Enquire::get_mset", first | maxitems | check_at_least | rset | mdecider);
1015 RETURN(internal->get_mset(first, maxitems, check_at_least, rset, mdecider));
1018 ESet
1019 Enquire::get_eset(Xapian::termcount maxitems, const RSet & rset, int flags,
1020 const ExpandDecider * edecider, double min_wt) const
1022 LOGCALL(API, Xapian::ESet, "Xapian::Enquire::get_eset", maxitems | rset | flags | edecider | min_wt);
1023 RETURN(internal->get_eset(maxitems, rset, flags, edecider, min_wt));
1026 TermIterator
1027 Enquire::get_matching_terms_begin(const MSetIterator &it) const
1029 LOGCALL(API, Xapian::TermIterator, "Xapian::Enquire::get_matching_terms_begin", it);
1030 RETURN(internal->get_matching_terms(it));
1033 TermIterator
1034 Enquire::get_matching_terms_begin(Xapian::docid did) const
1036 LOGCALL(API, Xapian::TermIterator, "Xapian::Enquire::get_matching_terms_begin", did);
1037 RETURN(internal->get_matching_terms(did));
1040 string
1041 Enquire::get_description() const
1043 return "Xapian::Enquire(" + internal->get_description() + ")";