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,2017 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"
42 #include "matcher/msetcmp.h"
43 #include "matcher/multimatch.h"
45 #include "api/omenquireinternal.h"
46 #include "roundestimate.h"
48 #include "weight/weightinternal.h"
58 using Xapian::Internal::ExpandWeight
;
59 using Xapian::Internal::Bo1EWeight
;
60 using Xapian::Internal::TradEWeight
;
64 MatchDecider::~MatchDecider() { }
66 // Methods for Xapian::RSet
68 RSet::RSet() : internal(new RSet::Internal
)
72 RSet::RSet(const RSet
&other
) : internal(other
.internal
)
77 RSet::operator=(const RSet
&other
)
79 internal
= other
.internal
;
82 RSet::RSet(RSet
&&) = default;
85 RSet::operator=(RSet
&&) = default;
94 return internal
->items
.size();
100 return internal
->items
.empty();
104 RSet::add_document(Xapian::docid did
)
106 if (did
== 0) throw Xapian::InvalidArgumentError("Docid 0 not valid");
107 internal
->items
.insert(did
);
111 RSet::remove_document(Xapian::docid did
)
113 internal
->items
.erase(did
);
117 RSet::contains(Xapian::docid did
) const
119 return internal
->items
.find(did
) != internal
->items
.end();
123 RSet::get_description() const
125 return "RSet(" + internal
->get_description() + ")";
129 RSet::Internal::get_description() const
131 string
description("RSet::Internal(");
133 set
<Xapian::docid
>::const_iterator i
;
134 for (i
= items
.begin(); i
!= items
.end(); ++i
) {
135 if (i
!= items
.begin()) description
+= ", ";
136 description
+= str(*i
);
146 // Methods for Xapian::MSetItem
149 MSetItem::get_description() const
153 description
= str(did
) + ", " + str(wt
) + ", " +
156 description
= "Xapian::MSetItem(" + description
+ ")";
163 // Methods for Xapian::MSet
165 MSet::MSet() : internal(new MSet::Internal
)
173 MSet::MSet(const MSet
& other
) : internal(other
.internal
)
178 MSet::operator=(const MSet
&other
)
180 internal
= other
.internal
;
184 MSet::MSet(MSet
&&) = default;
187 MSet::operator=(MSet
&&) = default;
190 MSet::fetch_(Xapian::doccount first
, Xapian::doccount last
) const
192 LOGCALL_VOID(API
, "Xapian::MSet::fetch_", first
| last
);
193 Assert(internal
.get() != 0);
194 internal
->fetch_items(first
, last
);
198 MSet::convert_to_percent(double wt
) const
200 LOGCALL(API
, int, "Xapian::MSet::convert_to_percent", wt
);
201 Assert(internal
.get() != 0);
202 RETURN(internal
->convert_to_percent_internal(wt
));
206 MSet::get_termfreq(const string
&tname
) const
208 LOGCALL(API
, Xapian::doccount
, "Xapian::MSet::get_termfreq", tname
);
209 Assert(internal
.get() != 0);
210 if (usual(internal
->stats
)) {
211 Xapian::doccount termfreq
;
212 if (internal
->stats
->get_stats(tname
, termfreq
))
215 if (internal
->enquire
.get() == 0) {
216 throw InvalidOperationError("Can't get termfreq from an MSet which is not derived from a query.");
218 RETURN(internal
->enquire
->get_termfreq(tname
));
222 MSet::get_termweight(const string
&tname
) const
224 LOGCALL(API
, double, "Xapian::MSet::get_termweight", tname
);
225 Assert(internal
.get() != 0);
226 if (!internal
->stats
) {
227 throw InvalidOperationError("Can't get termweight from an MSet which is not derived from a query.");
230 if (!internal
->stats
->get_termweight(tname
, termweight
)) {
232 msg
+= ": termweight not available";
233 throw InvalidArgumentError(msg
);
239 MSet::get_firstitem() const
241 Assert(internal
.get() != 0);
242 return internal
->firstitem
;
246 MSet::get_matches_lower_bound() const
248 Assert(internal
.get() != 0);
249 return internal
->matches_lower_bound
;
253 MSet::get_matches_estimated() const
255 Assert(internal
.get() != 0);
256 return round_estimate(internal
->matches_lower_bound
,
257 internal
->matches_upper_bound
,
258 internal
->matches_estimated
);
262 MSet::get_matches_upper_bound() const
264 Assert(internal
.get() != 0);
265 return internal
->matches_upper_bound
;
269 MSet::get_uncollapsed_matches_lower_bound() const
271 Assert(internal
.get() != 0);
272 return internal
->uncollapsed_lower_bound
;
276 MSet::get_uncollapsed_matches_estimated() const
278 Assert(internal
.get() != 0);
279 return internal
->uncollapsed_estimated
;
283 MSet::get_uncollapsed_matches_upper_bound() const
285 Assert(internal
.get() != 0);
286 return internal
->uncollapsed_upper_bound
;
290 MSet::get_max_possible() const
292 Assert(internal
.get() != 0);
293 return internal
->max_possible
;
297 MSet::get_max_attained() const
299 Assert(internal
.get() != 0);
300 return internal
->max_attained
;
304 MSet::snippet(const string
& text
,
306 const Xapian::Stem
& stemmer
,
308 const string
& hi_start
,
309 const string
& hi_end
,
310 const string
& omit
) const
312 Assert(internal
.get() != 0);
313 return internal
->snippet(text
, length
, stemmer
, flags
,
314 hi_start
, hi_end
, omit
);
320 Assert(internal
.get() != 0);
321 return internal
->items
.size();
325 MSet::get_description() const
327 Assert(internal
.get() != 0);
328 return "Xapian::MSet(" + internal
->get_description() + ")";
332 MSet::Internal::convert_to_percent_internal(double wt
) const
334 LOGCALL(MATCH
, int, "Xapian::MSet::Internal::convert_to_percent_internal", wt
);
335 if (percent_factor
== 0) RETURN(100);
337 // Excess precision on x86 can result in a difference here.
338 double v
= wt
* percent_factor
+ 100.0 * DBL_EPSILON
;
339 int pcent
= static_cast<int>(v
);
340 LOGLINE(MATCH
, "wt = " << wt
<< ", max_possible = " << max_possible
<<
341 " => pcent = " << pcent
);
342 if (pcent
> 100) pcent
= 100;
343 if (pcent
< 0) pcent
= 0;
344 if (pcent
== 0 && wt
> 0) pcent
= 1;
350 MSet::Internal::get_doc_by_index(Xapian::doccount index
) const
352 LOGCALL(MATCH
, Document
, "Xapian::MSet::Internal::get_doc_by_index", index
);
354 map
<Xapian::doccount
, Document
>::const_iterator doc
;
355 doc
= indexeddocs
.find(index
);
356 if (doc
!= indexeddocs
.end()) {
359 if (index
< firstitem
|| index
>= firstitem
+ items
.size()) {
360 throw RangeError("The mset returned from the match does not contain the document at index " + str(index
));
362 Assert(enquire
.get());
363 if (!requested_docs
.empty()) {
364 // There's already a pending request, so handle that.
366 // Maybe we just fetched the doc we want.
367 doc
= indexeddocs
.find(index
);
368 if (doc
!= indexeddocs
.end()) {
373 RETURN(enquire
->get_document(items
[index
- firstitem
]));
377 MSet::Internal::fetch_items(Xapian::doccount first
, Xapian::doccount last
) const
379 LOGCALL_VOID(MATCH
, "Xapian::MSet::Internal::fetch_items", first
| last
);
380 if (enquire
.get() == 0) {
381 throw InvalidOperationError("Can't fetch documents from an MSet which is not derived from a query.");
383 if (items
.empty()) return;
384 if (last
> items
.size() - 1)
385 last
= items
.size() - 1;
386 for (Xapian::doccount i
= first
; i
<= last
; ++i
) {
387 map
<Xapian::doccount
, Document
>::const_iterator doc
;
388 doc
= indexeddocs
.find(i
);
389 if (doc
== indexeddocs
.end()) {
390 /* We don't have the document cached */
391 set
<Xapian::doccount
>::const_iterator s
;
392 s
= requested_docs
.find(i
);
393 if (s
== requested_docs
.end()) {
394 /* We haven't even requested it yet - do so now. */
395 enquire
->request_doc(items
[i
- firstitem
]);
396 requested_docs
.insert(i
);
403 MSet::Internal::get_description() const
405 string description
= "Xapian::MSet::Internal(";
407 description
+= "firstitem=" + str(firstitem
) + ", " +
408 "matches_lower_bound=" + str(matches_lower_bound
) + ", " +
409 "matches_estimated=" + str(matches_estimated
) + ", " +
410 "matches_upper_bound=" + str(matches_upper_bound
) + ", " +
411 "max_possible=" + str(max_possible
) + ", " +
412 "max_attained=" + str(max_attained
);
414 for (vector
<Xapian::Internal::MSetItem
>::const_iterator i
= items
.begin();
415 i
!= items
.end(); ++i
) {
416 if (!description
.empty()) description
+= ", ";
417 description
+= i
->get_description();
426 MSet::Internal::read_docs() const
428 set
<Xapian::doccount
>::const_iterator i
;
429 for (i
= requested_docs
.begin(); i
!= requested_docs
.end(); ++i
) {
430 indexeddocs
[*i
] = enquire
->read_doc(items
[*i
- firstitem
]);
431 LOGLINE(MATCH
, "stored doc at index " << *i
<< " is " << indexeddocs
[*i
]);
433 /* Clear list of requested but not fetched documents. */
434 requested_docs
.clear();
440 MSetIterator::operator*() const
442 Assert(mset
.internal
.get());
443 Xapian::doccount size
= mset
.internal
->items
.size();
444 Xapian::doccount index
= size
- off_from_end
;
445 AssertRel(index
,<,size
);
446 return mset
.internal
->items
[index
].did
;
450 MSetIterator::get_document() const
452 Assert(mset
.internal
.get());
453 Xapian::doccount size
= mset
.internal
->items
.size();
454 Xapian::doccount index
= size
- off_from_end
;
455 AssertRel(index
,<,size
);
456 return mset
.internal
->get_doc_by_index(index
);
460 MSetIterator::get_weight() const
462 Assert(mset
.internal
.get());
463 Xapian::doccount size
= mset
.internal
->items
.size();
464 Xapian::doccount index
= size
- off_from_end
;
465 AssertRel(index
,<,size
);
466 return mset
.internal
->items
[index
].wt
;
470 MSetIterator::get_collapse_key() const
472 Assert(mset
.internal
.get());
473 Xapian::doccount size
= mset
.internal
->items
.size();
474 Xapian::doccount index
= size
- off_from_end
;
475 AssertRel(index
,<,size
);
476 return mset
.internal
->items
[index
].collapse_key
;
480 MSetIterator::get_collapse_count() const
482 Assert(mset
.internal
.get());
483 Xapian::doccount size
= mset
.internal
->items
.size();
484 Xapian::doccount index
= size
- off_from_end
;
485 AssertRel(index
,<,size
);
486 return mset
.internal
->items
[index
].collapse_count
;
490 MSetIterator::get_sort_key() const
492 Assert(mset
.internal
.get());
493 Xapian::doccount size
= mset
.internal
->items
.size();
494 Xapian::doccount index
= size
- off_from_end
;
495 AssertRel(index
,<,size
);
496 return mset
.internal
->items
[index
].sort_key
;
500 MSetIterator::get_description() const
502 return "Xapian::MSetIterator(" + str(mset
.size() - off_from_end
) + ")";
505 // Methods for Xapian::Enquire::Internal
507 Enquire::Internal::Internal(const Database
&db_
)
508 : db(db_
), query(), collapse_key(Xapian::BAD_VALUENO
), collapse_max(0),
509 order(Enquire::ASCENDING
), percent_cutoff(0), weight_cutoff(0),
510 sort_key(Xapian::BAD_VALUENO
), sort_by(REL
), sort_value_forward(true),
511 sorter(), time_limit(0.0), weight(0),
512 eweightname("trad"), expand_k(1.0)
514 if (db
.internal
.empty()) {
515 throw InvalidArgumentError("Can't make an Enquire object from an uninitialised Database object.");
519 Enquire::Internal::~Internal()
526 Enquire::Internal::set_query(const Query
&query_
, termcount qlen_
)
529 qlen
= qlen_
? qlen_
: query
.get_length();
533 Enquire::Internal::get_query() const
539 Enquire::Internal::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
540 Xapian::doccount check_at_least
, const RSet
*rset
,
541 const MatchDecider
*mdecider
) const
543 LOGCALL(MATCH
, MSet
, "Enquire::Internal::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
545 if (percent_cutoff
&& (sort_by
== VAL
|| sort_by
== VAL_REL
)) {
546 throw Xapian::UnimplementedError("Use of a percentage cutoff while sorting primary by value isn't currently supported");
550 weight
= new BM25Weight
;
553 Xapian::doccount first_orig
= first
;
555 Xapian::doccount docs
= db
.get_doccount();
556 first
= min(first
, docs
);
557 maxitems
= min(maxitems
, docs
- first
);
558 check_at_least
= min(check_at_least
, docs
);
559 check_at_least
= max(check_at_least
, first
+ maxitems
);
562 AutoPtr
<Xapian::Weight::Internal
> stats(new Xapian::Weight::Internal
);
563 ::MultiMatch
match(db
, query
, qlen
, rset
,
564 collapse_max
, collapse_key
,
565 percent_cutoff
, weight_cutoff
,
566 order
, sort_key
, sort_by
, sort_value_forward
,
567 time_limit
, *(stats
.get()), weight
, spies
,
568 (sorter
.get() != NULL
),
570 // Run query and put results into supplied Xapian::MSet object.
572 match
.get_mset(first
, maxitems
, check_at_least
, retval
,
573 *(stats
.get()), mdecider
, sorter
.get());
574 if (first_orig
!= first
&& retval
.internal
.get()) {
575 retval
.internal
->firstitem
= first_orig
;
578 Assert(weight
->name() != "bool" || retval
.get_max_possible() == 0);
580 // The Xapian::MSet needs to have a pointer to ourselves, so that it can
581 // retrieve the documents. This is set here explicitly to avoid having
582 // to pass it into the matcher, which gets messy particularly in the
584 retval
.internal
->enquire
= this;
586 if (!retval
.internal
->stats
) {
587 retval
.internal
->stats
= stats
.release();
594 Enquire::Internal::get_eset(Xapian::termcount maxitems
,
595 const RSet
& rset
, int flags
,
596 const ExpandDecider
* edecider_
,
599 LOGCALL(MATCH
, ESet
, "Enquire::Internal::get_eset", maxitems
| rset
| flags
| edecider_
| min_wt
);
601 using Xapian::Internal::opt_intrusive_ptr
;
602 opt_intrusive_ptr
<const ExpandDecider
> edecider(edecider_
);
603 if (maxitems
== 0 || rset
.empty()) {
604 // Either we were asked for no results, or wouldn't produce any
605 // because no documents were marked as relevant.
609 LOGVALUE(MATCH
, rset
.size());
611 if (!query
.empty() && !(flags
& Enquire::INCLUDE_QUERY_TERMS
)) {
612 opt_intrusive_ptr
<const ExpandDecider
> decider_noquery(
613 (new ExpandDeciderFilterTerms(query
.get_terms_begin(),
614 query
.get_terms_end()))->release());
615 if (edecider
.get()) {
616 edecider
= (new ExpandDeciderAnd(decider_noquery
.get(),
617 edecider
.get()))->release();
619 edecider
= decider_noquery
;
623 bool use_exact_termfreq(flags
& Enquire::USE_EXACT_TERMFREQ
);
625 eset
.internal
= new Xapian::ESet::Internal
;
627 if (eweightname
== "bo1") {
628 Bo1EWeight
bo1eweight(db
, rset
.size(), use_exact_termfreq
);
629 eset
.internal
->expand(maxitems
, db
, rset
, edecider
.get(), bo1eweight
,
632 TradEWeight
tradeweight(db
, rset
.size(), use_exact_termfreq
, expand_k
);
633 eset
.internal
->expand(maxitems
, db
, rset
, edecider
.get(), tradeweight
,
640 class ByQueryIndexCmp
{
642 typedef map
<string
, unsigned int> tmap_t
;
646 explicit ByQueryIndexCmp(const tmap_t
&tmap_
) : tmap(tmap_
) {}
647 bool operator()(const string
&left
,
648 const string
&right
) const {
649 tmap_t::const_iterator l
, r
;
651 r
= tmap
.find(right
);
652 Assert((l
!= tmap
.end()) && (r
!= tmap
.end()));
654 return l
->second
< r
->second
;
659 Enquire::Internal::get_matching_terms(Xapian::docid did
) const
662 return TermIterator();
664 // The ordered list of terms in the query.
665 TermIterator qt
= query
.get_terms_begin();
667 // copy the list of query terms into a map for faster access.
668 // FIXME: a hash would be faster than a map, if this becomes
670 map
<string
, unsigned int> tmap
;
671 unsigned int index
= 1;
672 for ( ; qt
!= query
.get_terms_end(); ++qt
) {
673 if (tmap
.find(*qt
) == tmap
.end())
677 vector
<string
> matching_terms
;
679 TermIterator docterms
= db
.termlist_begin(did
);
680 TermIterator docterms_end
= db
.termlist_end(did
);
681 while (docterms
!= docterms_end
) {
682 string term
= *docterms
;
683 map
<string
, unsigned int>::iterator t
= tmap
.find(term
);
684 if (t
!= tmap
.end()) matching_terms
.push_back(term
);
688 // sort the resulting list by query position.
689 sort(matching_terms
.begin(), matching_terms
.end(), ByQueryIndexCmp(tmap
));
691 return TermIterator(new VectorTermList(matching_terms
.begin(),
692 matching_terms
.end()));
696 Enquire::Internal::get_matching_terms(const MSetIterator
&it
) const
698 // FIXME: take advantage of MSetIterator to ensure that database
699 // doesn't get modified underneath us.
700 return get_matching_terms(*it
);
704 Enquire::Internal::get_termfreq(const string
&tname
) const
706 return db
.get_termfreq(tname
);
710 Enquire::Internal::get_description() const
712 string description
= db
.get_description();
714 description
+= query
.get_description();
718 // Private methods for Xapian::Enquire::Internal
721 Enquire::Internal::request_doc(const Xapian::Internal::MSetItem
&item
) const
723 unsigned int multiplier
= db
.internal
.size();
725 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
726 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
728 db
.internal
[dbnumber
]->request_document(realdid
);
732 Enquire::Internal::read_doc(const Xapian::Internal::MSetItem
&item
) const
734 unsigned int multiplier
= db
.internal
.size();
736 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
737 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
739 Xapian::Document::Internal
*doc
;
740 doc
= db
.internal
[dbnumber
]->collect_document(realdid
);
741 return Document(doc
);
745 Enquire::Internal::get_document(const Xapian::Internal::MSetItem
&item
) const
747 unsigned int multiplier
= db
.internal
.size();
749 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
750 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
752 // We know the doc exists, so open lazily.
753 return Document(db
.internal
[dbnumber
]->open_document(realdid
, true));
756 // Methods of Xapian::Enquire
758 Enquire::Enquire(const Enquire
& other
) : internal(other
.internal
)
760 LOGCALL_CTOR(API
, "Enquire", other
);
764 Enquire::operator=(const Enquire
& other
)
766 LOGCALL_VOID(API
, "Xapian::Enquire::operator=", other
);
767 internal
= other
.internal
;
770 Enquire::Enquire(Enquire
&&) = default;
773 Enquire::operator=(Enquire
&&) = default;
775 Enquire::Enquire(const Database
&databases
)
776 : internal(new Internal(databases
))
778 LOGCALL_CTOR(API
, "Enquire", databases
);
781 Enquire::Enquire(const Database
&databases
, ErrorHandler
*)
782 : internal(new Internal(databases
))
784 LOGCALL_CTOR(API
, "Enquire", databases
| Literal("errorhandler"));
789 LOGCALL_DTOR(API
, "Enquire");
793 Enquire::set_query(const Query
& query
, termcount len
)
795 LOGCALL_VOID(API
, "Xapian::Enquire::set_query", query
| len
);
796 internal
->set_query(query
, len
);
800 Enquire::get_query() const
802 LOGCALL(API
, const Xapian::Query
&, "Xapian::Enquire::get_query", NO_ARGS
);
803 RETURN(internal
->get_query());
807 Enquire::add_matchspy(MatchSpy
* spy
) {
808 LOGCALL_VOID(API
, "Xapian::Enquire::add_matchspy", spy
);
809 internal
->spies
.push_back(spy
);
813 Enquire::clear_matchspies() {
814 LOGCALL_VOID(API
, "Xapian::Enquire::clear_matchspies", NO_ARGS
);
815 internal
->spies
.clear();
819 Enquire::set_weighting_scheme(const Weight
&weight_
)
821 LOGCALL_VOID(API
, "Xapian::Enquire::set_weighting_scheme", weight_
);
822 // Clone first in case doing so throws an exception.
823 Weight
* wt
= weight_
.clone();
824 swap(wt
, internal
->weight
);
829 Enquire::set_expansion_scheme(const std::string
&eweightname_
, double expand_k_
) const
831 LOGCALL_VOID(API
, "Xapian::Enquire::set_expansion_scheme", eweightname_
| expand_k_
);
833 if (eweightname_
!= "bo1" && eweightname_
!= "trad") {
834 throw InvalidArgumentError("Invalid name for query expansion scheme.");
837 internal
->eweightname
= eweightname_
;
838 internal
->expand_k
= expand_k_
;
842 Enquire::set_collapse_key(Xapian::valueno collapse_key
, Xapian::doccount collapse_max
)
844 if (collapse_key
== Xapian::BAD_VALUENO
) collapse_max
= 0;
845 internal
->collapse_key
= collapse_key
;
846 internal
->collapse_max
= collapse_max
;
850 Enquire::set_docid_order(Enquire::docid_order order
)
852 internal
->order
= order
;
856 Enquire::set_cutoff(int percent_cutoff
, double weight_cutoff
)
858 internal
->percent_cutoff
= percent_cutoff
;
859 internal
->weight_cutoff
= weight_cutoff
;
863 Enquire::set_sort_by_relevance()
865 internal
->sort_by
= Internal::REL
;
869 Enquire::set_sort_by_value(valueno sort_key
, bool ascending
)
871 internal
->sorter
= NULL
;
872 internal
->sort_key
= sort_key
;
873 internal
->sort_by
= Internal::VAL
;
874 internal
->sort_value_forward
= ascending
;
878 Enquire::set_sort_by_value_then_relevance(valueno sort_key
, bool ascending
)
880 internal
->sorter
= NULL
;
881 internal
->sort_key
= sort_key
;
882 internal
->sort_by
= Internal::VAL_REL
;
883 internal
->sort_value_forward
= ascending
;
887 Enquire::set_sort_by_relevance_then_value(valueno sort_key
, bool ascending
)
889 internal
->sorter
= NULL
;
890 internal
->sort_key
= sort_key
;
891 internal
->sort_by
= Internal::REL_VAL
;
892 internal
->sort_value_forward
= ascending
;
896 Enquire::set_sort_by_key(KeyMaker
* sorter
, bool ascending
)
899 throw InvalidArgumentError("sorter can't be NULL");
900 internal
->sorter
= sorter
;
901 internal
->sort_by
= Internal::VAL
;
902 internal
->sort_value_forward
= ascending
;
906 Enquire::set_sort_by_key_then_relevance(KeyMaker
* sorter
, bool ascending
)
909 throw InvalidArgumentError("sorter can't be NULL");
910 internal
->sorter
= sorter
;
911 internal
->sort_by
= Internal::VAL_REL
;
912 internal
->sort_value_forward
= ascending
;
916 Enquire::set_sort_by_relevance_then_key(KeyMaker
* sorter
, bool ascending
)
919 throw Xapian::InvalidArgumentError("sorter can't be NULL");
920 internal
->sorter
= sorter
;
921 internal
->sort_by
= Internal::REL_VAL
;
922 internal
->sort_value_forward
= ascending
;
926 Enquire::set_time_limit(double time_limit
)
928 internal
->time_limit
= time_limit
;
932 Enquire::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
933 Xapian::doccount check_at_least
, const RSet
*rset
,
934 const MatchDecider
*mdecider
) const
936 LOGCALL(API
, Xapian::MSet
, "Xapian::Enquire::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
937 RETURN(internal
->get_mset(first
, maxitems
, check_at_least
, rset
, mdecider
));
941 Enquire::get_eset(Xapian::termcount maxitems
, const RSet
& rset
, int flags
,
942 const ExpandDecider
* edecider
, double min_wt
) const
944 LOGCALL(API
, Xapian::ESet
, "Xapian::Enquire::get_eset", maxitems
| rset
| flags
| edecider
| min_wt
);
945 RETURN(internal
->get_eset(maxitems
, rset
, flags
, edecider
, min_wt
));
949 Enquire::get_matching_terms_begin(const MSetIterator
&it
) const
951 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", it
);
952 RETURN(internal
->get_matching_terms(it
));
956 Enquire::get_matching_terms_begin(Xapian::docid did
) const
958 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", did
);
959 RETURN(internal
->get_matching_terms(did
));
963 Enquire::get_description() const
965 return "Xapian::Enquire(" + internal
->get_description() + ")";