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
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"
39 #include "expand/esetinternal.h"
40 #include "expand/expandweight.h"
41 #include "matcher/multimatch.h"
43 #include "api/omenquireinternal.h"
45 #include "weight/weightinternal.h"
55 using Xapian::Internal::ExpandWeight
;
56 using Xapian::Internal::Bo1EWeight
;
57 using Xapian::Internal::TradEWeight
;
61 MatchDecider::~MatchDecider() { }
63 // Methods for Xapian::RSet
65 RSet::RSet() : internal(new RSet::Internal
)
69 RSet::RSet(const RSet
&other
) : internal(other
.internal
)
74 RSet::operator=(const RSet
&other
)
76 internal
= other
.internal
;
86 return internal
->items
.size();
92 return internal
->items
.empty();
96 RSet::add_document(Xapian::docid did
)
98 if (did
== 0) throw Xapian::InvalidArgumentError("Docid 0 not valid");
99 internal
->items
.insert(did
);
103 RSet::remove_document(Xapian::docid did
)
105 internal
->items
.erase(did
);
109 RSet::contains(Xapian::docid did
) const
111 return internal
->items
.find(did
) != internal
->items
.end();
115 RSet::get_description() const
117 return "RSet(" + internal
->get_description() + ")";
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
);
138 // Methods for Xapian::MSetItem
141 MSetItem::get_description() const
145 description
= str(did
) + ", " + str(wt
) + ", " +
148 description
= "Xapian::MSetItem(" + description
+ ")";
155 // Methods for Xapian::MSet
157 MSet::MSet() : internal(new MSet::Internal
)
165 MSet::MSet(const MSet
& other
) : internal(other
.internal
)
170 MSet::operator=(const MSet
&other
)
172 internal
= other
.internal
;
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::get_termfreq(const string
&tname
) const
195 LOGCALL(API
, Xapian::doccount
, "Xapian::MSet::get_termfreq", tname
);
196 Assert(internal
.get() != 0);
197 if (usual(internal
->stats
)) {
198 Xapian::doccount termfreq
;
199 if (internal
->stats
->get_stats(tname
, termfreq
))
202 if (internal
->enquire
.get() == 0) {
203 throw InvalidOperationError("Can't get termfreq from an MSet which is not derived from a query.");
205 RETURN(internal
->enquire
->get_termfreq(tname
));
209 MSet::get_termweight(const string
&tname
) const
211 LOGCALL(API
, double, "Xapian::MSet::get_termweight", tname
);
212 Assert(internal
.get() != 0);
213 if (!internal
->stats
) {
214 throw InvalidOperationError("Can't get termweight from an MSet which is not derived from a query.");
217 if (!internal
->stats
->get_termweight(tname
, termweight
)) {
219 msg
+= ": termweight not available";
220 throw InvalidArgumentError(msg
);
226 MSet::get_firstitem() const
228 Assert(internal
.get() != 0);
229 return internal
->firstitem
;
233 MSet::get_matches_lower_bound() const
235 Assert(internal
.get() != 0);
236 return internal
->matches_lower_bound
;
240 MSet::get_matches_estimated() const
242 Assert(internal
.get() != 0);
243 return internal
->matches_estimated
;
247 MSet::get_matches_upper_bound() const
249 Assert(internal
.get() != 0);
250 return internal
->matches_upper_bound
;
254 MSet::get_uncollapsed_matches_lower_bound() const
256 Assert(internal
.get() != 0);
257 return internal
->uncollapsed_lower_bound
;
261 MSet::get_uncollapsed_matches_estimated() const
263 Assert(internal
.get() != 0);
264 return internal
->uncollapsed_estimated
;
268 MSet::get_uncollapsed_matches_upper_bound() const
270 Assert(internal
.get() != 0);
271 return internal
->uncollapsed_upper_bound
;
275 MSet::get_max_possible() const
277 Assert(internal
.get() != 0);
278 return internal
->max_possible
;
282 MSet::get_max_attained() const
284 Assert(internal
.get() != 0);
285 return internal
->max_attained
;
289 MSet::snippet(const string
& text
,
291 const Xapian::Stem
& stemmer
,
293 const string
& hi_start
,
294 const string
& hi_end
,
295 const string
& omit
) const
297 Assert(internal
.get() != 0);
298 return internal
->snippet(text
, length
, stemmer
, flags
,
299 hi_start
, hi_end
, omit
);
305 Assert(internal
.get() != 0);
306 return internal
->items
.size();
310 MSet::get_description() const
312 Assert(internal
.get() != 0);
313 return "Xapian::MSet(" + internal
->get_description() + ")";
317 MSet::Internal::convert_to_percent_internal(double wt
) const
319 LOGCALL(MATCH
, int, "Xapian::MSet::Internal::convert_to_percent_internal", wt
);
320 if (percent_factor
== 0) RETURN(100);
322 // Excess precision on x86 can result in a difference here.
323 double v
= wt
* percent_factor
+ 100.0 * DBL_EPSILON
;
324 int pcent
= static_cast<int>(v
);
325 LOGLINE(MATCH
, "wt = " << wt
<< ", max_possible = " << max_possible
<<
326 " => pcent = " << pcent
);
327 if (pcent
> 100) pcent
= 100;
328 if (pcent
< 0) pcent
= 0;
329 if (pcent
== 0 && wt
> 0) pcent
= 1;
335 MSet::Internal::get_doc_by_index(Xapian::doccount index
) const
337 LOGCALL(MATCH
, Document
, "Xapian::MSet::Internal::get_doc_by_index", index
);
339 map
<Xapian::doccount
, Document
>::const_iterator doc
;
340 doc
= indexeddocs
.find(index
);
341 if (doc
!= indexeddocs
.end()) {
344 if (index
< firstitem
|| index
>= firstitem
+ items
.size()) {
345 throw RangeError("The mset returned from the match does not contain the document at index " + str(index
));
347 Assert(enquire
.get());
348 if (!requested_docs
.empty()) {
349 // There's already a pending request, so handle that.
351 // Maybe we just fetched the doc we want.
352 doc
= indexeddocs
.find(index
);
353 if (doc
!= indexeddocs
.end()) {
358 RETURN(enquire
->get_document(items
[index
- firstitem
]));
362 MSet::Internal::fetch_items(Xapian::doccount first
, Xapian::doccount last
) const
364 LOGCALL_VOID(MATCH
, "Xapian::MSet::Internal::fetch_items", first
| last
);
365 if (enquire
.get() == 0) {
366 throw InvalidOperationError("Can't fetch documents from an MSet which is not derived from a query.");
368 if (items
.empty()) return;
369 if (last
> items
.size() - 1)
370 last
= items
.size() - 1;
371 for (Xapian::doccount i
= first
; i
<= last
; ++i
) {
372 map
<Xapian::doccount
, Document
>::const_iterator doc
;
373 doc
= indexeddocs
.find(i
);
374 if (doc
== indexeddocs
.end()) {
375 /* We don't have the document cached */
376 set
<Xapian::doccount
>::const_iterator s
;
377 s
= requested_docs
.find(i
);
378 if (s
== requested_docs
.end()) {
379 /* We haven't even requested it yet - do so now. */
380 enquire
->request_doc(items
[i
- firstitem
]);
381 requested_docs
.insert(i
);
388 MSet::Internal::get_description() const
390 string description
= "Xapian::MSet::Internal(";
392 description
+= "firstitem=" + str(firstitem
) + ", " +
393 "matches_lower_bound=" + str(matches_lower_bound
) + ", " +
394 "matches_estimated=" + str(matches_estimated
) + ", " +
395 "matches_upper_bound=" + str(matches_upper_bound
) + ", " +
396 "max_possible=" + str(max_possible
) + ", " +
397 "max_attained=" + str(max_attained
);
399 for (vector
<Xapian::Internal::MSetItem
>::const_iterator i
= items
.begin();
400 i
!= items
.end(); ++i
) {
401 if (!description
.empty()) description
+= ", ";
402 description
+= i
->get_description();
411 MSet::Internal::read_docs() const
413 set
<Xapian::doccount
>::const_iterator i
;
414 for (i
= requested_docs
.begin(); i
!= requested_docs
.end(); ++i
) {
415 indexeddocs
[*i
] = enquire
->read_doc(items
[*i
- firstitem
]);
416 LOGLINE(MATCH
, "stored doc at index " << *i
<< " is " << indexeddocs
[*i
]);
418 /* Clear list of requested but not fetched documents. */
419 requested_docs
.clear();
425 MSetIterator::operator*() const
427 Assert(mset
.internal
.get());
428 Xapian::doccount size
= mset
.internal
->items
.size();
429 Xapian::doccount index
= size
- off_from_end
;
430 AssertRel(index
,<,size
);
431 return mset
.internal
->items
[index
].did
;
435 MSetIterator::get_document() const
437 Assert(mset
.internal
.get());
438 Xapian::doccount size
= mset
.internal
->items
.size();
439 Xapian::doccount index
= size
- off_from_end
;
440 AssertRel(index
,<,size
);
441 return mset
.internal
->get_doc_by_index(index
);
445 MSetIterator::get_weight() const
447 Assert(mset
.internal
.get());
448 Xapian::doccount size
= mset
.internal
->items
.size();
449 Xapian::doccount index
= size
- off_from_end
;
450 AssertRel(index
,<,size
);
451 return mset
.internal
->items
[index
].wt
;
455 MSetIterator::get_collapse_key() const
457 Assert(mset
.internal
.get());
458 Xapian::doccount size
= mset
.internal
->items
.size();
459 Xapian::doccount index
= size
- off_from_end
;
460 AssertRel(index
,<,size
);
461 return mset
.internal
->items
[index
].collapse_key
;
465 MSetIterator::get_collapse_count() const
467 Assert(mset
.internal
.get());
468 Xapian::doccount size
= mset
.internal
->items
.size();
469 Xapian::doccount index
= size
- off_from_end
;
470 AssertRel(index
,<,size
);
471 return mset
.internal
->items
[index
].collapse_count
;
475 MSetIterator::get_description() const
477 return "Xapian::MSetIterator(" + str(mset
.size() - off_from_end
) + ")";
480 // Methods for Xapian::Enquire::Internal
482 Enquire::Internal::Internal(const Database
&db_
)
483 : db(db_
), query(), collapse_key(Xapian::BAD_VALUENO
), collapse_max(0),
484 order(Enquire::ASCENDING
), percent_cutoff(0), weight_cutoff(0),
485 sort_key(Xapian::BAD_VALUENO
), sort_by(REL
), sort_value_forward(true),
486 sorter(), time_limit(0.0), weight(0),
487 eweightname("trad"), expand_k(1.0)
489 if (db
.internal
.empty()) {
490 throw InvalidArgumentError("Can't make an Enquire object from an uninitialised Database object.");
494 Enquire::Internal::~Internal()
501 Enquire::Internal::set_query(const Query
&query_
, termcount qlen_
)
504 qlen
= qlen_
? qlen_
: query
.get_length();
508 Enquire::Internal::get_query() const
514 Enquire::Internal::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
515 Xapian::doccount check_at_least
, const RSet
*rset
,
516 const MatchDecider
*mdecider
) const
518 LOGCALL(MATCH
, MSet
, "Enquire::Internal::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
520 if (percent_cutoff
&& (sort_by
== VAL
|| sort_by
== VAL_REL
)) {
521 throw Xapian::UnimplementedError("Use of a percentage cutoff while sorting primary by value isn't currently supported");
525 weight
= new BM25Weight
;
528 Xapian::doccount first_orig
= first
;
530 Xapian::doccount docs
= db
.get_doccount();
531 first
= min(first
, docs
);
532 maxitems
= min(maxitems
, docs
);
533 check_at_least
= min(check_at_least
, docs
);
534 check_at_least
= max(check_at_least
, maxitems
);
537 AutoPtr
<Xapian::Weight::Internal
> stats(new Xapian::Weight::Internal
);
538 ::MultiMatch
match(db
, query
, qlen
, rset
,
539 collapse_max
, collapse_key
,
540 percent_cutoff
, weight_cutoff
,
541 order
, sort_key
, sort_by
, sort_value_forward
,
542 time_limit
, *(stats
.get()), weight
, spies
,
543 (sorter
.get() != NULL
),
545 // Run query and put results into supplied Xapian::MSet object.
547 match
.get_mset(first
, maxitems
, check_at_least
, retval
,
548 *(stats
.get()), mdecider
, sorter
.get());
549 if (first_orig
!= first
&& retval
.internal
.get()) {
550 retval
.internal
->firstitem
= first_orig
;
553 Assert(weight
->name() != "bool" || retval
.get_max_possible() == 0);
555 // The Xapian::MSet needs to have a pointer to ourselves, so that it can
556 // retrieve the documents. This is set here explicitly to avoid having
557 // to pass it into the matcher, which gets messy particularly in the
559 retval
.internal
->enquire
= this;
561 if (!retval
.internal
->stats
) {
562 retval
.internal
->stats
= stats
.release();
569 Enquire::Internal::get_eset(Xapian::termcount maxitems
,
570 const RSet
& rset
, int flags
,
571 const ExpandDecider
* edecider_
,
574 LOGCALL(MATCH
, ESet
, "Enquire::Internal::get_eset", maxitems
| rset
| flags
| edecider_
| min_wt
);
576 using Xapian::Internal::opt_intrusive_ptr
;
577 opt_intrusive_ptr
<const ExpandDecider
> edecider(edecider_
);
578 if (maxitems
== 0 || rset
.empty()) {
579 // Either we were asked for no results, or wouldn't produce any
580 // because no documents were marked as relevant.
584 LOGVALUE(MATCH
, rset
.size());
586 if (!query
.empty() && !(flags
& Enquire::INCLUDE_QUERY_TERMS
)) {
587 opt_intrusive_ptr
<const ExpandDecider
> decider_noquery(
588 (new ExpandDeciderFilterTerms(query
.get_terms_begin(),
589 query
.get_terms_end()))->release());
590 if (edecider
.get()) {
591 edecider
= (new ExpandDeciderAnd(decider_noquery
.get(),
592 edecider
.get()))->release();
594 edecider
= decider_noquery
;
598 bool use_exact_termfreq(flags
& Enquire::USE_EXACT_TERMFREQ
);
600 eset
.internal
= new Xapian::ESet::Internal
;
602 if (eweightname
== "bo1") {
603 Bo1EWeight
bo1eweight(db
, rset
.size(), use_exact_termfreq
);
604 eset
.internal
->expand(maxitems
, db
, rset
, edecider
.get(), bo1eweight
,
607 TradEWeight
tradeweight(db
, rset
.size(), use_exact_termfreq
, expand_k
);
608 eset
.internal
->expand(maxitems
, db
, rset
, edecider
.get(), tradeweight
,
615 class ByQueryIndexCmp
{
617 typedef map
<string
, unsigned int> tmap_t
;
620 ByQueryIndexCmp(const tmap_t
&tmap_
) : tmap(tmap_
) {}
621 bool operator()(const string
&left
,
622 const string
&right
) const {
623 tmap_t::const_iterator l
, r
;
625 r
= tmap
.find(right
);
626 Assert((l
!= tmap
.end()) && (r
!= tmap
.end()));
628 return l
->second
< r
->second
;
633 Enquire::Internal::get_matching_terms(Xapian::docid did
) const
636 return TermIterator();
638 // The ordered list of terms in the query.
639 TermIterator qt
= query
.get_terms_begin();
641 // copy the list of query terms into a map for faster access.
642 // FIXME: a hash would be faster than a map, if this becomes
644 map
<string
, unsigned int> tmap
;
645 unsigned int index
= 1;
646 for ( ; qt
!= query
.get_terms_end(); ++qt
) {
647 if (tmap
.find(*qt
) == tmap
.end())
651 vector
<string
> matching_terms
;
653 TermIterator docterms
= db
.termlist_begin(did
);
654 TermIterator docterms_end
= db
.termlist_end(did
);
655 while (docterms
!= docterms_end
) {
656 string term
= *docterms
;
657 map
<string
, unsigned int>::iterator t
= tmap
.find(term
);
658 if (t
!= tmap
.end()) matching_terms
.push_back(term
);
662 // sort the resulting list by query position.
663 sort(matching_terms
.begin(), matching_terms
.end(), ByQueryIndexCmp(tmap
));
665 return TermIterator(new VectorTermList(matching_terms
.begin(),
666 matching_terms
.end()));
670 Enquire::Internal::get_matching_terms(const MSetIterator
&it
) const
672 // FIXME: take advantage of MSetIterator to ensure that database
673 // doesn't get modified underneath us.
674 return get_matching_terms(*it
);
678 Enquire::Internal::get_termfreq(const string
&tname
) const
680 return db
.get_termfreq(tname
);
684 Enquire::Internal::get_description() const
686 string description
= db
.get_description();
688 description
+= query
.get_description();
692 // Private methods for Xapian::Enquire::Internal
695 Enquire::Internal::request_doc(const Xapian::Internal::MSetItem
&item
) const
697 unsigned int multiplier
= db
.internal
.size();
699 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
700 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
702 db
.internal
[dbnumber
]->request_document(realdid
);
706 Enquire::Internal::read_doc(const Xapian::Internal::MSetItem
&item
) const
708 unsigned int multiplier
= db
.internal
.size();
710 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
711 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
713 Xapian::Document::Internal
*doc
;
714 doc
= db
.internal
[dbnumber
]->collect_document(realdid
);
715 return Document(doc
);
719 Enquire::Internal::get_document(const Xapian::Internal::MSetItem
&item
) const
721 unsigned int multiplier
= db
.internal
.size();
723 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
724 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
726 // We know the doc exists, so open lazily.
727 return Document(db
.internal
[dbnumber
]->open_document(realdid
, true));
730 // Methods of Xapian::Enquire
732 Enquire::Enquire(const Enquire
& other
) : internal(other
.internal
)
734 LOGCALL_CTOR(API
, "Enquire", other
);
738 Enquire::operator=(const Enquire
& other
)
740 LOGCALL_VOID(API
, "Xapian::Enquire::operator=", other
);
741 internal
= other
.internal
;
744 Enquire::Enquire(const Database
&databases
)
745 : internal(new Internal(databases
))
747 LOGCALL_CTOR(API
, "Enquire", databases
);
750 Enquire::Enquire(const Database
&databases
, ErrorHandler
*)
751 : internal(new Internal(databases
))
753 LOGCALL_CTOR(API
, "Enquire", databases
| Literal("errorhandler"));
758 LOGCALL_DTOR(API
, "Enquire");
762 Enquire::set_query(const Query
& query
, termcount len
)
764 LOGCALL_VOID(API
, "Xapian::Enquire::set_query", query
| len
);
765 internal
->set_query(query
, len
);
769 Enquire::get_query() const
771 LOGCALL(API
, const Xapian::Query
&, "Xapian::Enquire::get_query", NO_ARGS
);
772 RETURN(internal
->get_query());
776 Enquire::add_matchspy(MatchSpy
* spy
) {
777 LOGCALL_VOID(API
, "Xapian::Enquire::add_matchspy", spy
);
778 internal
->spies
.push_back(spy
);
782 Enquire::clear_matchspies() {
783 LOGCALL_VOID(API
, "Xapian::Enquire::clear_matchspies", NO_ARGS
);
784 internal
->spies
.clear();
788 Enquire::set_weighting_scheme(const Weight
&weight_
)
790 LOGCALL_VOID(API
, "Xapian::Enquire::set_weighting_scheme", weight_
);
791 // Clone first in case doing so throws an exception.
792 Weight
* wt
= weight_
.clone();
793 swap(wt
, internal
->weight
);
798 Enquire::set_expansion_scheme(const std::string
&eweightname_
, double expand_k_
) const
800 LOGCALL_VOID(API
, "Xapian::Enquire::set_expansion_scheme", eweightname_
| expand_k_
);
802 if (eweightname_
!= "bo1" && eweightname_
!= "trad") {
803 throw InvalidArgumentError("Invalid name for query expansion scheme.");
806 internal
->eweightname
= eweightname_
;
807 internal
->expand_k
= expand_k_
;
811 Enquire::set_collapse_key(Xapian::valueno collapse_key
, Xapian::doccount collapse_max
)
813 if (collapse_key
== Xapian::BAD_VALUENO
) collapse_max
= 0;
814 internal
->collapse_key
= collapse_key
;
815 internal
->collapse_max
= collapse_max
;
819 Enquire::set_docid_order(Enquire::docid_order order
)
821 internal
->order
= order
;
825 Enquire::set_cutoff(int percent_cutoff
, double weight_cutoff
)
827 internal
->percent_cutoff
= percent_cutoff
;
828 internal
->weight_cutoff
= weight_cutoff
;
832 Enquire::set_sort_by_relevance()
834 internal
->sort_by
= Internal::REL
;
838 Enquire::set_sort_by_value(valueno sort_key
, bool ascending
)
840 internal
->sorter
= NULL
;
841 internal
->sort_key
= sort_key
;
842 internal
->sort_by
= Internal::VAL
;
843 internal
->sort_value_forward
= ascending
;
847 Enquire::set_sort_by_value_then_relevance(valueno sort_key
, bool ascending
)
849 internal
->sorter
= NULL
;
850 internal
->sort_key
= sort_key
;
851 internal
->sort_by
= Internal::VAL_REL
;
852 internal
->sort_value_forward
= ascending
;
856 Enquire::set_sort_by_relevance_then_value(valueno sort_key
, bool ascending
)
858 internal
->sorter
= NULL
;
859 internal
->sort_key
= sort_key
;
860 internal
->sort_by
= Internal::REL_VAL
;
861 internal
->sort_value_forward
= ascending
;
865 Enquire::set_sort_by_key(KeyMaker
* sorter
, bool ascending
)
868 throw InvalidArgumentError("sorter can't be NULL");
869 internal
->sorter
= sorter
;
870 internal
->sort_by
= Internal::VAL
;
871 internal
->sort_value_forward
= ascending
;
875 Enquire::set_sort_by_key_then_relevance(KeyMaker
* sorter
, bool ascending
)
878 throw InvalidArgumentError("sorter can't be NULL");
879 internal
->sorter
= sorter
;
880 internal
->sort_by
= Internal::VAL_REL
;
881 internal
->sort_value_forward
= ascending
;
885 Enquire::set_sort_by_relevance_then_key(KeyMaker
* sorter
, bool ascending
)
888 throw Xapian::InvalidArgumentError("sorter can't be NULL");
889 internal
->sorter
= sorter
;
890 internal
->sort_by
= Internal::REL_VAL
;
891 internal
->sort_value_forward
= ascending
;
895 Enquire::set_time_limit(double time_limit
)
897 internal
->time_limit
= time_limit
;
901 Enquire::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
902 Xapian::doccount check_at_least
, const RSet
*rset
,
903 const MatchDecider
*mdecider
) const
905 LOGCALL(API
, Xapian::MSet
, "Xapian::Enquire::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
906 RETURN(internal
->get_mset(first
, maxitems
, check_at_least
, rset
, mdecider
));
910 Enquire::get_eset(Xapian::termcount maxitems
, const RSet
& rset
, int flags
,
911 const ExpandDecider
* edecider
, double min_wt
) const
913 LOGCALL(API
, Xapian::ESet
, "Xapian::Enquire::get_eset", maxitems
| rset
| flags
| edecider
| min_wt
);
914 RETURN(internal
->get_eset(maxitems
, rset
, flags
, edecider
, min_wt
));
918 Enquire::get_matching_terms_begin(const MSetIterator
&it
) const
920 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", it
);
921 RETURN(internal
->get_matching_terms(it
));
925 Enquire::get_matching_terms_begin(Xapian::docid did
) const
927 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", did
);
928 RETURN(internal
->get_matching_terms(did
));
932 Enquire::get_description() const
934 return "Xapian::Enquire(" + internal
->get_description() + ")";