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 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/errorhandler.h"
31 #include "xapian/expanddecider.h"
32 #include "xapian/matchspy.h"
33 #include "xapian/termiterator.h"
34 #include "xapian/weight.h"
36 #include "vectortermlist.h"
38 #include "backends/database.h"
40 #include "expand/esetinternal.h"
41 #include "expand/expandweight.h"
42 #include "matcher/multimatch.h"
44 #include "api/omenquireinternal.h"
46 #include "weight/weightinternal.h"
56 using Xapian::Internal::ExpandWeight
;
57 using Xapian::Internal::Bo1EWeight
;
58 using Xapian::Internal::TradEWeight
;
62 MatchDecider::~MatchDecider() { }
64 // Methods for Xapian::RSet
66 RSet::RSet() : internal(new RSet::Internal
)
70 RSet::RSet(const RSet
&other
) : internal(other
.internal
)
75 RSet::operator=(const RSet
&other
)
77 internal
= other
.internal
;
87 return internal
->items
.size();
93 return internal
->items
.empty();
97 RSet::add_document(Xapian::docid did
)
99 if (did
== 0) throw Xapian::InvalidArgumentError("Docid 0 not valid");
100 internal
->items
.insert(did
);
104 RSet::remove_document(Xapian::docid did
)
106 internal
->items
.erase(did
);
110 RSet::contains(Xapian::docid did
) const
112 return internal
->items
.find(did
) != internal
->items
.end();
116 RSet::get_description() const
118 return "RSet(" + internal
->get_description() + ")";
122 RSet::Internal::get_description() const
124 string
description("RSet::Internal(");
126 set
<Xapian::docid
>::const_iterator i
;
127 for (i
= items
.begin(); i
!= items
.end(); ++i
) {
128 if (i
!= items
.begin()) description
+= ", ";
129 description
+= str(*i
);
139 // Methods for Xapian::MSetItem
142 MSetItem::get_description() const
146 description
= str(did
) + ", " + str(wt
) + ", " +
149 description
= "Xapian::MSetItem(" + description
+ ")";
156 // Methods for Xapian::MSet
158 MSet::MSet() : internal(new MSet::Internal
)
162 MSet::MSet(MSet::Internal
* internal_
) : internal(internal_
)
170 MSet::MSet(const MSet
& other
) : internal(other
.internal
)
175 MSet::operator=(const MSet
&other
)
177 internal
= other
.internal
;
181 MSet::fetch(const MSetIterator
& beginiter
, const MSetIterator
& enditer
) const
183 LOGCALL_VOID(API
, "Xapian::MSet::fetch", beginiter
| enditer
);
184 Assert(internal
.get() != 0);
185 if (beginiter
!= enditer
)
186 internal
->fetch_items(beginiter
.index
, enditer
.index
- 1);
190 MSet::fetch(const MSetIterator
& beginiter
) const
192 LOGCALL_VOID(API
, "Xapian::MSet::fetch", beginiter
);
193 Assert(internal
.get() != 0);
194 internal
->fetch_items(beginiter
.index
, beginiter
.index
);
200 LOGCALL_VOID(API
, "Xapian::MSet::fetch", NO_ARGS
);
201 Assert(internal
.get() != 0);
202 if (!internal
->items
.empty())
203 internal
->fetch_items(0, internal
->items
.size() - 1);
207 MSet::convert_to_percent(double wt
) const
209 LOGCALL(API
, int, "Xapian::MSet::convert_to_percent", wt
);
210 Assert(internal
.get() != 0);
211 RETURN(internal
->convert_to_percent_internal(wt
));
215 MSet::convert_to_percent(const MSetIterator
& it
) const
217 LOGCALL(API
, int, "Xapian::MSet::convert_to_percent", it
);
218 Assert(internal
.get() != 0);
219 RETURN(internal
->convert_to_percent_internal(it
.get_weight()));
223 MSet::get_termfreq(const string
&tname
) const
225 LOGCALL(API
, Xapian::doccount
, "Xapian::MSet::get_termfreq", tname
);
226 Assert(internal
.get() != 0);
227 if (usual(internal
->stats
)) {
228 Xapian::doccount termfreq
;
229 if (internal
->stats
->get_stats(tname
, termfreq
))
232 if (internal
->enquire
.get() == 0) {
233 throw InvalidOperationError("Can't get termfreq from an MSet which is not derived from a query.");
235 RETURN(internal
->enquire
->get_termfreq(tname
));
239 MSet::get_termweight(const string
&tname
) const
241 LOGCALL(API
, double, "Xapian::MSet::get_termweight", tname
);
242 Assert(internal
.get() != 0);
243 if (!internal
->stats
) {
244 throw InvalidOperationError("Can't get termweight from an MSet which is not derived from a query.");
247 if (!internal
->stats
->get_termweight(tname
, termweight
)) {
249 msg
+= ": termweight not available";
250 throw InvalidArgumentError(msg
);
256 MSet::get_firstitem() const
258 Assert(internal
.get() != 0);
259 return internal
->firstitem
;
263 MSet::get_matches_lower_bound() const
265 Assert(internal
.get() != 0);
266 return internal
->matches_lower_bound
;
270 MSet::get_matches_estimated() const
272 Assert(internal
.get() != 0);
273 return internal
->matches_estimated
;
277 MSet::get_matches_upper_bound() const
279 Assert(internal
.get() != 0);
280 return internal
->matches_upper_bound
;
284 MSet::get_uncollapsed_matches_lower_bound() const
286 Assert(internal
.get() != 0);
287 return internal
->uncollapsed_lower_bound
;
291 MSet::get_uncollapsed_matches_estimated() const
293 Assert(internal
.get() != 0);
294 return internal
->uncollapsed_estimated
;
298 MSet::get_uncollapsed_matches_upper_bound() const
300 Assert(internal
.get() != 0);
301 return internal
->uncollapsed_upper_bound
;
305 MSet::get_max_possible() const
307 Assert(internal
.get() != 0);
308 return internal
->max_possible
;
312 MSet::get_max_attained() const
314 Assert(internal
.get() != 0);
315 return internal
->max_attained
;
321 Assert(internal
.get() != 0);
322 return internal
->items
.size();
328 Assert(internal
.get() != 0);
329 return internal
->items
.empty();
333 MSet::swap(MSet
& other
)
335 std::swap(internal
, other
.internal
);
341 return MSetIterator(0, *this);
347 Assert(internal
.get() != 0);
348 return MSetIterator(internal
->items
.size(), *this);
352 MSet::operator[](Xapian::doccount i
) const
354 // Don't test 0 <= i - that gives a compiler warning if i is unsigned
355 Assert(0 < (i
+ 1) && i
< size());
356 return MSetIterator(i
, *this);
363 Assert(internal
.get() != 0);
364 return MSetIterator(internal
->items
.size() - 1, *this);
368 MSet::get_description() const
370 Assert(internal
.get() != 0);
371 return "Xapian::MSet(" + internal
->get_description() + ")";
375 MSet::Internal::convert_to_percent_internal(double wt
) const
377 LOGCALL(MATCH
, int, "Xapian::MSet::Internal::convert_to_percent_internal", wt
);
378 if (percent_factor
== 0) RETURN(100);
380 // Excess precision on x86 can result in a difference here.
381 double v
= wt
* percent_factor
+ 100.0 * DBL_EPSILON
;
382 int pcent
= static_cast<int>(v
);
383 LOGLINE(MATCH
, "wt = " << wt
<< ", max_possible = " << max_possible
<<
384 " => pcent = " << pcent
);
385 if (pcent
> 100) pcent
= 100;
386 if (pcent
< 0) pcent
= 0;
387 if (pcent
== 0 && wt
> 0) pcent
= 1;
393 MSet::Internal::get_doc_by_index(Xapian::doccount index
) const
395 LOGCALL(MATCH
, Document
, "Xapian::MSet::Internal::get_doc_by_index", index
);
397 map
<Xapian::doccount
, Document
>::const_iterator doc
;
398 doc
= indexeddocs
.find(index
);
399 if (doc
!= indexeddocs
.end()) {
402 if (index
< firstitem
|| index
>= firstitem
+ items
.size()) {
403 throw RangeError("The mset returned from the match does not contain the document at index " + str(index
));
405 Assert(enquire
.get());
406 if (!requested_docs
.empty()) {
407 // There's already a pending request, so handle that.
409 // Maybe we just fetched the doc we want.
410 doc
= indexeddocs
.find(index
);
411 if (doc
!= indexeddocs
.end()) {
416 // Don't cache unless fetch() was called by the API user.
417 // FIXME: This is needed for the remote case, but for a local disk-based
418 // database it means we try to pre-read right before we actually read,
419 // which probably isn't useful.
420 enquire
->request_doc(items
[index
- firstitem
]);
421 RETURN(enquire
->read_doc(items
[index
- firstitem
]));
425 MSet::Internal::fetch_items(Xapian::doccount first
, Xapian::doccount last
) const
427 LOGCALL_VOID(MATCH
, "Xapian::MSet::Internal::fetch_items", first
| last
);
428 if (enquire
.get() == 0) {
429 throw InvalidOperationError("Can't fetch documents from an MSet which is not derived from a query.");
431 for (Xapian::doccount i
= first
; i
<= last
; ++i
) {
432 map
<Xapian::doccount
, Document
>::const_iterator doc
;
433 doc
= indexeddocs
.find(i
);
434 if (doc
== indexeddocs
.end()) {
435 /* We don't have the document cached */
436 set
<Xapian::doccount
>::const_iterator s
;
437 s
= requested_docs
.find(i
);
438 if (s
== requested_docs
.end()) {
439 /* We haven't even requested it yet - do so now. */
440 enquire
->request_doc(items
[i
- firstitem
]);
441 requested_docs
.insert(i
);
448 MSet::Internal::get_description() const
450 string description
= "Xapian::MSet::Internal(";
452 description
+= "firstitem=" + str(firstitem
) + ", " +
453 "matches_lower_bound=" + str(matches_lower_bound
) + ", " +
454 "matches_estimated=" + str(matches_estimated
) + ", " +
455 "matches_upper_bound=" + str(matches_upper_bound
) + ", " +
456 "max_possible=" + str(max_possible
) + ", " +
457 "max_attained=" + str(max_attained
);
459 for (vector
<Xapian::Internal::MSetItem
>::const_iterator i
= items
.begin();
460 i
!= items
.end(); ++i
) {
461 if (!description
.empty()) description
+= ", ";
462 description
+= i
->get_description();
471 MSet::Internal::read_docs() const
473 set
<Xapian::doccount
>::const_iterator i
;
474 for (i
= requested_docs
.begin(); i
!= requested_docs
.end(); ++i
) {
475 indexeddocs
[*i
] = enquire
->read_doc(items
[*i
- firstitem
]);
476 LOGLINE(MATCH
, "stored doc at index " << *i
<< " is " << indexeddocs
[*i
]);
478 /* Clear list of requested but not fetched documents. */
479 requested_docs
.clear();
482 // Methods for Xapian::ESet
484 ESet::ESet() : internal(new Internal
) { }
490 ESet::ESet(const ESet
& other
) : internal(other
.internal
)
495 ESet::operator=(const ESet
&other
)
497 internal
= other
.internal
;
501 ESet::get_ebound() const
503 return internal
->ebound
;
509 return internal
->items
.size();
515 return internal
->items
.empty();
519 ESet::swap(ESet
& other
)
521 std::swap(internal
, other
.internal
);
527 return ESetIterator(0, *this);
533 Assert(internal
.get() != 0);
534 return ESetIterator(internal
->items
.size(), *this);
538 ESet::operator[](Xapian::termcount i
) const
540 // Don't test 0 <= i - that gives a compiler warning if i is unsigned
541 Assert(0 < (i
+ 1) && i
< size());
542 return ESetIterator(i
, *this);
549 Assert(internal
.get() != 0);
550 return ESetIterator(internal
->items
.size() - 1, *this);
554 ESet::get_description() const
556 Assert(internal
.get() != 0);
557 return "Xapian::ESet(" + internal
->get_description() + ")";
560 // Xapian::ESetIterator
563 ESetIterator::operator *() const
565 Assert(eset
.internal
.get());
566 AssertRel(index
,<,eset
.internal
->items
.size());
567 return eset
.internal
->items
[index
].term
;
571 ESetIterator::get_weight() const
573 Assert(eset
.internal
.get());
574 AssertRel(index
,<,eset
.internal
->items
.size());
575 return eset
.internal
->items
[index
].wt
;
579 ESetIterator::get_description() const
581 return "Xapian::ESetIterator(" + str(index
) + ")";
587 MSetIterator::operator *() const
589 Assert(mset
.internal
.get());
590 AssertRel(index
,<,mset
.internal
->items
.size());
591 return mset
.internal
->items
[index
].did
;
595 MSetIterator::get_document() const
597 Assert(mset
.internal
.get());
598 AssertRel(index
,<,mset
.internal
->items
.size());
599 return mset
.internal
->get_doc_by_index(index
);
603 MSetIterator::get_weight() const
605 Assert(mset
.internal
.get());
606 AssertRel(index
,<,mset
.internal
->items
.size());
607 return mset
.internal
->items
[index
].wt
;
611 MSetIterator::get_collapse_key() const
613 Assert(mset
.internal
.get());
614 AssertRel(index
,<,mset
.internal
->items
.size());
615 return mset
.internal
->items
[index
].collapse_key
;
619 MSetIterator::get_collapse_count() const
621 Assert(mset
.internal
.get());
622 AssertRel(index
,<,mset
.internal
->items
.size());
623 return mset
.internal
->items
[index
].collapse_count
;
627 MSetIterator::get_percent() const
629 LOGCALL(API
, int, "MSetIterator::get_percent", NO_ARGS
);
630 RETURN(mset
.internal
->convert_to_percent_internal(get_weight()));
634 MSetIterator::get_description() const
636 return "Xapian::MSetIterator(" + str(index
) + ")";
639 // Methods for Xapian::Enquire::Internal
641 Enquire::Internal::Internal(const Database
&db_
, ErrorHandler
* errorhandler_
)
642 : db(db_
), query(), collapse_key(Xapian::BAD_VALUENO
), collapse_max(0),
643 order(Enquire::ASCENDING
), percent_cutoff(0), weight_cutoff(0),
644 sort_key(Xapian::BAD_VALUENO
), sort_by(REL
), sort_value_forward(true),
645 sorter(0), time_limit(0.0), errorhandler(errorhandler_
), weight(0),
646 eweightname("trad"), expand_k(1.0)
648 if (db
.internal
.empty()) {
649 throw InvalidArgumentError("Can't make an Enquire object from an uninitialised Database object.");
653 Enquire::Internal::~Internal()
660 Enquire::Internal::set_query(const Query
&query_
, termcount qlen_
)
663 qlen
= qlen_
? qlen_
: query
.get_length();
667 Enquire::Internal::get_query()
673 Enquire::Internal::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
674 Xapian::doccount check_at_least
, const RSet
*rset
,
675 const MatchDecider
*mdecider
) const
677 LOGCALL(MATCH
, MSet
, "Enquire::Internal::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
679 if (percent_cutoff
&& (sort_by
== VAL
|| sort_by
== VAL_REL
)) {
680 throw Xapian::UnimplementedError("Use of a percentage cutoff while sorting primary by value isn't currently supported");
684 weight
= new BM25Weight
;
687 Xapian::doccount first_orig
= first
;
689 Xapian::doccount docs
= db
.get_doccount();
690 first
= min(first
, docs
);
691 maxitems
= min(maxitems
, docs
);
692 check_at_least
= min(check_at_least
, docs
);
693 check_at_least
= max(check_at_least
, maxitems
);
696 AutoPtr
<Xapian::Weight::Internal
> stats(new Xapian::Weight::Internal
);
697 ::MultiMatch
match(db
, query
, qlen
, rset
,
698 collapse_max
, collapse_key
,
699 percent_cutoff
, weight_cutoff
,
700 order
, sort_key
, sort_by
, sort_value_forward
,
701 time_limit
, errorhandler
, *(stats
.get()), weight
, spies
,
704 // Run query and put results into supplied Xapian::MSet object.
706 match
.get_mset(first
, maxitems
, check_at_least
, retval
,
707 *(stats
.get()), mdecider
, sorter
);
708 if (first_orig
!= first
&& retval
.internal
.get()) {
709 retval
.internal
->firstitem
= first_orig
;
712 Assert(weight
->name() != "bool" || retval
.get_max_possible() == 0);
714 // The Xapian::MSet needs to have a pointer to ourselves, so that it can
715 // retrieve the documents. This is set here explicitly to avoid having
716 // to pass it into the matcher, which gets messy particularly in the
718 retval
.internal
->enquire
= this;
720 if (!retval
.internal
->stats
) {
721 retval
.internal
->stats
= stats
.release();
728 Enquire::Internal::get_eset(Xapian::termcount maxitems
,
729 const RSet
& rset
, int flags
,
730 const ExpandDecider
* edecider
, double min_wt
) const
732 LOGCALL(MATCH
, ESet
, "Enquire::Internal::get_eset", maxitems
| rset
| flags
| edecider
| min_wt
);
734 if (maxitems
== 0 || rset
.empty()) {
735 // Either we were asked for no results, or wouldn't produce any
736 // because no documents were marked as relevant.
740 LOGVALUE(MATCH
, rset
.size());
742 /* The AutoPtrs will clean up any dynamically allocated
743 * expand deciders automatically.
745 AutoPtr
<ExpandDecider
> decider_noquery
;
746 AutoPtr
<ExpandDecider
> decider_andnoquery
;
748 if (!query
.empty() && !(flags
& Enquire::INCLUDE_QUERY_TERMS
)) {
749 decider_noquery
.reset(
750 new ExpandDeciderFilterTerms(query
.get_terms_begin(),
751 query
.get_terms_end()));
753 decider_andnoquery
.reset(
754 new ExpandDeciderAnd(decider_noquery
.get(), edecider
));
755 edecider
= decider_andnoquery
.get();
757 edecider
= decider_noquery
.get();
761 bool use_exact_termfreq(flags
& Enquire::USE_EXACT_TERMFREQ
);
764 if (eweightname
== "bo1") {
765 Bo1EWeight
bo1eweight(db
, rset
.size(), use_exact_termfreq
);
766 eset
.internal
->expand(maxitems
, db
, rset
, edecider
, bo1eweight
, min_wt
);
768 TradEWeight
tradeweight(db
, rset
.size(), use_exact_termfreq
, expand_k
);
769 eset
.internal
->expand(maxitems
, db
, rset
, edecider
, tradeweight
, min_wt
);
775 class ByQueryIndexCmp
{
777 typedef map
<string
, unsigned int> tmap_t
;
780 ByQueryIndexCmp(const tmap_t
&tmap_
) : tmap(tmap_
) {}
781 bool operator()(const string
&left
,
782 const string
&right
) const {
783 tmap_t::const_iterator l
, r
;
785 r
= tmap
.find(right
);
786 Assert((l
!= tmap
.end()) && (r
!= tmap
.end()));
788 return l
->second
< r
->second
;
793 Enquire::Internal::get_matching_terms(Xapian::docid did
) const
796 return TermIterator();
798 // The ordered list of terms in the query.
799 TermIterator qt
= query
.get_terms_begin();
801 // copy the list of query terms into a map for faster access.
802 // FIXME: a hash would be faster than a map, if this becomes
804 map
<string
, unsigned int> tmap
;
805 unsigned int index
= 1;
806 for ( ; qt
!= query
.get_terms_end(); ++qt
) {
807 if (tmap
.find(*qt
) == tmap
.end())
811 vector
<string
> matching_terms
;
813 TermIterator docterms
= db
.termlist_begin(did
);
814 TermIterator docterms_end
= db
.termlist_end(did
);
815 while (docterms
!= docterms_end
) {
816 string term
= *docterms
;
817 map
<string
, unsigned int>::iterator t
= tmap
.find(term
);
818 if (t
!= tmap
.end()) matching_terms
.push_back(term
);
822 // sort the resulting list by query position.
823 sort(matching_terms
.begin(), matching_terms
.end(), ByQueryIndexCmp(tmap
));
825 return TermIterator(new VectorTermList(matching_terms
.begin(),
826 matching_terms
.end()));
830 Enquire::Internal::get_matching_terms(const MSetIterator
&it
) const
832 // FIXME: take advantage of MSetIterator to ensure that database
833 // doesn't get modified underneath us.
834 return get_matching_terms(*it
);
838 Enquire::Internal::get_termfreq(const string
&tname
) const
840 return db
.get_termfreq(tname
);
844 Enquire::Internal::get_description() const
846 string description
= db
.get_description();
848 description
+= query
.get_description();
852 // Private methods for Xapian::Enquire::Internal
855 Enquire::Internal::request_doc(const Xapian::Internal::MSetItem
&item
) const
858 unsigned int multiplier
= db
.internal
.size();
860 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
861 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
863 db
.internal
[dbnumber
]->request_document(realdid
);
864 } catch (Error
& e
) {
865 if (errorhandler
) (*errorhandler
)(e
);
871 Enquire::Internal::read_doc(const Xapian::Internal::MSetItem
&item
) const
874 unsigned int multiplier
= db
.internal
.size();
876 Xapian::docid realdid
= (item
.did
- 1) / multiplier
+ 1;
877 Xapian::doccount dbnumber
= (item
.did
- 1) % multiplier
;
879 Xapian::Document::Internal
*doc
;
880 doc
= db
.internal
[dbnumber
]->collect_document(realdid
);
881 return Document(doc
);
882 } catch (Error
& e
) {
883 if (errorhandler
) (*errorhandler
)(e
);
888 // Methods of Xapian::Enquire
890 Enquire::Enquire(const Enquire
& other
) : internal(other
.internal
)
892 LOGCALL_CTOR(API
, "Enquire", other
);
896 Enquire::operator=(const Enquire
& other
)
898 LOGCALL_VOID(API
, "Xapian::Enquire::operator=", other
);
899 internal
= other
.internal
;
902 Enquire::Enquire(const Database
&databases
)
903 : internal(new Internal(databases
, NULL
))
905 LOGCALL_CTOR(API
, "Enquire", databases
);
908 Enquire::Enquire(const Database
&databases
, ErrorHandler
* errorhandler
)
909 : internal(new Internal(databases
, errorhandler
))
911 LOGCALL_CTOR(API
, "Enquire", databases
| errorhandler
);
916 LOGCALL_DTOR(API
, "Enquire");
920 Enquire::set_query(const Query
& query
, termcount len
)
922 LOGCALL_VOID(API
, "Xapian::Enquire::set_query", query
| len
);
923 internal
->set_query(query
, len
);
927 Enquire::get_query() const
929 LOGCALL(API
, const Xapian::Query
&, "Xapian::Enquire::get_query", NO_ARGS
);
931 RETURN(internal
->get_query());
932 } catch (Error
& e
) {
933 if (internal
->errorhandler
) (*internal
->errorhandler
)(e
);
939 Enquire::add_matchspy(MatchSpy
* spy
) {
940 LOGCALL_VOID(API
, "Xapian::Enquire::add_matchspy", spy
);
941 internal
->spies
.push_back(spy
);
945 Enquire::clear_matchspies() {
946 LOGCALL_VOID(API
, "Xapian::Enquire::clear_matchspies", NO_ARGS
);
947 internal
->spies
.clear();
951 Enquire::set_weighting_scheme(const Weight
&weight_
)
953 LOGCALL_VOID(API
, "Xapian::Enquire::set_weighting_scheme", weight_
);
954 // Clone first in case doing so throws an exception.
955 Weight
* wt
= weight_
.clone();
956 swap(wt
, internal
->weight
);
961 Enquire::set_expansion_scheme(const std::string
&eweightname_
, double expand_k_
) const
963 LOGCALL_VOID(API
, "Xapian::Enquire::set_expansion_scheme", eweightname_
| expand_k_
);
965 if (eweightname_
!= "bo1" && eweightname_
!= "trad") {
966 throw InvalidArgumentError("Invalid name for query expansion scheme.");
969 internal
->eweightname
= eweightname_
;
970 internal
->expand_k
= expand_k_
;
974 Enquire::set_collapse_key(Xapian::valueno collapse_key
, Xapian::doccount collapse_max
)
976 if (collapse_key
== Xapian::BAD_VALUENO
) collapse_max
= 0;
977 internal
->collapse_key
= collapse_key
;
978 internal
->collapse_max
= collapse_max
;
982 Enquire::set_docid_order(Enquire::docid_order order
)
984 internal
->order
= order
;
988 Enquire::set_cutoff(int percent_cutoff
, double weight_cutoff
)
990 internal
->percent_cutoff
= percent_cutoff
;
991 internal
->weight_cutoff
= weight_cutoff
;
995 Enquire::set_sort_by_relevance()
997 internal
->sort_by
= Internal::REL
;
1001 Enquire::set_sort_by_value(valueno sort_key
, bool ascending
)
1003 internal
->sorter
= NULL
;
1004 internal
->sort_key
= sort_key
;
1005 internal
->sort_by
= Internal::VAL
;
1006 internal
->sort_value_forward
= ascending
;
1010 Enquire::set_sort_by_value_then_relevance(valueno sort_key
, bool ascending
)
1012 internal
->sorter
= NULL
;
1013 internal
->sort_key
= sort_key
;
1014 internal
->sort_by
= Internal::VAL_REL
;
1015 internal
->sort_value_forward
= ascending
;
1019 Enquire::set_sort_by_relevance_then_value(valueno sort_key
, bool ascending
)
1021 internal
->sorter
= NULL
;
1022 internal
->sort_key
= sort_key
;
1023 internal
->sort_by
= Internal::REL_VAL
;
1024 internal
->sort_value_forward
= ascending
;
1028 Enquire::set_sort_by_key(KeyMaker
* sorter
, bool ascending
)
1031 throw InvalidArgumentError("sorter can't be NULL");
1032 internal
->sorter
= sorter
;
1033 internal
->sort_by
= Internal::VAL
;
1034 internal
->sort_value_forward
= ascending
;
1038 Enquire::set_sort_by_key_then_relevance(KeyMaker
* sorter
, bool ascending
)
1041 throw InvalidArgumentError("sorter can't be NULL");
1042 internal
->sorter
= sorter
;
1043 internal
->sort_by
= Internal::VAL_REL
;
1044 internal
->sort_value_forward
= ascending
;
1048 Enquire::set_sort_by_relevance_then_key(KeyMaker
* sorter
, bool ascending
)
1051 throw Xapian::InvalidArgumentError("sorter can't be NULL");
1052 internal
->sorter
= sorter
;
1053 internal
->sort_by
= Internal::REL_VAL
;
1054 internal
->sort_value_forward
= ascending
;
1058 Enquire::set_time_limit(double time_limit
)
1060 internal
->time_limit
= time_limit
;
1064 Enquire::get_mset(Xapian::doccount first
, Xapian::doccount maxitems
,
1065 Xapian::doccount check_at_least
, const RSet
*rset
,
1066 const MatchDecider
*mdecider
) const
1068 LOGCALL(API
, Xapian::MSet
, "Xapian::Enquire::get_mset", first
| maxitems
| check_at_least
| rset
| mdecider
);
1071 RETURN(internal
->get_mset(first
, maxitems
, check_at_least
, rset
,
1073 } catch (Error
& e
) {
1074 if (internal
->errorhandler
) (*internal
->errorhandler
)(e
);
1080 Enquire::get_eset(Xapian::termcount maxitems
, const RSet
& rset
, int flags
,
1081 const ExpandDecider
* edecider
, double min_wt
) const
1083 LOGCALL(API
, Xapian::ESet
, "Xapian::Enquire::get_eset", maxitems
| rset
| flags
| edecider
| min_wt
);
1086 RETURN(internal
->get_eset(maxitems
, rset
, flags
, edecider
, min_wt
));
1087 } catch (Error
& e
) {
1088 if (internal
->errorhandler
) (*internal
->errorhandler
)(e
);
1094 Enquire::get_matching_terms_begin(const MSetIterator
&it
) const
1096 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", it
);
1098 RETURN(internal
->get_matching_terms(it
));
1099 } catch (Error
& e
) {
1100 if (internal
->errorhandler
) (*internal
->errorhandler
)(e
);
1106 Enquire::get_matching_terms_begin(Xapian::docid did
) const
1108 LOGCALL(API
, Xapian::TermIterator
, "Xapian::Enquire::get_matching_terms_begin", did
);
1110 RETURN(internal
->get_matching_terms(did
));
1111 } catch (Error
& e
) {
1112 if (internal
->errorhandler
) (*internal
->errorhandler
)(e
);
1118 Enquire::get_description() const
1120 return "Xapian::Enquire(" + internal
->get_description() + ")";