Branch data Line data Source code
1 : : /* omenquire.cc: External interface for running queries
2 : : *
3 : : * Copyright 1999,2000,2001 BrightStation PLC
4 : : * Copyright 2001,2002 Ananova Ltd
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010 Olly Betts
6 : : * Copyright 2007,2009 Lemur Consulting Ltd
7 : : * Copyright 2011, Action Without Borders
8 : : *
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.
13 : : *
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.
18 : : *
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
23 : : */
24 : :
25 : : #include <config.h>
26 : : #include "xapian/enquire.h"
27 : :
28 : : #include "xapian/document.h"
29 : : #include "xapian/error.h"
30 : : #include "xapian/errorhandler.h"
31 : : #include "xapian/expanddecider.h"
32 : : #include "xapian/termiterator.h"
33 : : #include "xapian/weight.h"
34 : :
35 : : #include "vectortermlist.h"
36 : :
37 : : #include "database.h"
38 : : #include "debuglog.h"
39 : : #include "esetinternal.h"
40 : : #include "expandweight.h"
41 : : #include "multimatch.h"
42 : : #include "omenquireinternal.h"
43 : : #include "str.h"
44 : : #include "weightinternal.h"
45 : :
46 : : #include <algorithm>
47 : : #include "autoptr.h"
48 : : #include <cfloat>
49 : : #include <cmath>
50 : : #include <vector>
51 : :
52 : : using namespace std;
53 : :
54 : : using Xapian::Internal::ExpandWeight;
55 : :
56 : : namespace Xapian {
57 : :
58 [ # # ][ # # ]: 50 : MatchDecider::~MatchDecider() { }
[ - + ]
59 : :
60 : : // Methods for Xapian::RSet
61 : :
62 : 179764 : RSet::RSet() : internal(new RSet::Internal)
63 : : {
64 : 179764 : }
65 : :
66 : 497157 : RSet::RSet(const RSet &other) : internal(other.internal)
67 : : {
68 : 497157 : }
69 : :
70 : : void
71 : 1 : RSet::operator=(const RSet &other)
72 : : {
73 : 1 : internal = other.internal;
74 : 1 : }
75 : :
76 : 676921 : RSet::~RSet()
77 : : {
78 : 676921 : }
79 : :
80 : : Xapian::doccount
81 : 246543 : RSet::size() const
82 : : {
83 : 246543 : return internal->items.size();
84 : : }
85 : :
86 : : bool
87 : 188 : RSet::empty() const
88 : : {
89 : 188 : return internal->items.empty();
90 : : }
91 : :
92 : : void
93 : 517 : RSet::add_document(Xapian::docid did)
94 : : {
95 [ + + ]: 517 : if (did == 0) throw Xapian::InvalidArgumentError("Docid 0 not valid");
96 : 515 : internal->items.insert(did);
97 : 515 : }
98 : :
99 : : void
100 : 0 : RSet::remove_document(Xapian::docid did)
101 : : {
102 : 0 : internal->items.erase(did);
103 : 0 : }
104 : :
105 : : bool
106 : 0 : RSet::contains(Xapian::docid did) const
107 : : {
108 : 0 : return internal->items.find(did) != internal->items.end();
109 : : }
110 : :
111 : : string
112 : 2 : RSet::get_description() const
113 : : {
114 : 2 : return "RSet(" + internal->get_description() + ")";
115 : : }
116 : :
117 : : string
118 : 2 : RSet::Internal::get_description() const
119 : : {
120 : 2 : string description("RSet::Internal(");
121 : :
122 : 2 : set<Xapian::docid>::const_iterator i;
123 [ + + ]: 3 : for (i = items.begin(); i != items.end(); ++i) {
124 [ - + ]: 1 : if (i != items.begin()) description += ", ";
125 : 1 : description += str(*i);
126 : : }
127 : :
128 : 2 : description += ')';
129 : :
130 : 0 : return description;
131 : : }
132 : :
133 : : namespace Internal {
134 : :
135 : : // Methods for Xapian::MSetItem
136 : :
137 : : string
138 : 5720 : MSetItem::get_description() const
139 : : {
140 : 5720 : string description;
141 : :
142 : : description = str(did) + ", " + str(wt) + ", " +
143 : 5720 : collapse_key;
144 : :
145 : 5720 : description = "Xapian::MSetItem(" + description + ")";
146 : :
147 : 0 : return description;
148 : : }
149 : :
150 : : }
151 : :
152 : : // Methods for Xapian::MSet
153 : :
154 : 187786 : MSet::MSet() : internal(new MSet::Internal)
155 : : {
156 : 187786 : }
157 : :
158 : 179680 : MSet::MSet(MSet::Internal * internal_) : internal(internal_)
159 : : {
160 : 179680 : }
161 : :
162 : 46258186 : MSet::~MSet()
163 : : {
164 : 46258186 : }
165 : :
166 : 45890720 : MSet::MSet(const MSet & other) : internal(other.internal)
167 : : {
168 : 45890720 : }
169 : :
170 : : void
171 : 189642 : MSet::operator=(const MSet &other)
172 : : {
173 : 189642 : internal = other.internal;
174 : 189642 : }
175 : :
176 : : void
177 : 26 : MSet::fetch(const MSetIterator & beginiter, const MSetIterator & enditer) const
178 : : {
179 : : LOGCALL_VOID(API, "Xapian::MSet::fetch", beginiter | enditer);
180 : : Assert(internal.get() != 0);
181 [ + - ]: 26 : if (beginiter != enditer)
182 : 26 : internal->fetch_items(beginiter.index, enditer.index - 1);
183 : 26 : }
184 : :
185 : : void
186 : 13 : MSet::fetch(const MSetIterator & beginiter) const
187 : : {
188 : : LOGCALL_VOID(API, "Xapian::MSet::fetch", beginiter);
189 : : Assert(internal.get() != 0);
190 : 13 : internal->fetch_items(beginiter.index, beginiter.index);
191 : 13 : }
192 : :
193 : : void
194 : 13 : MSet::fetch() const
195 : : {
196 : : LOGCALL_VOID(API, "Xapian::MSet::fetch", NO_ARGS);
197 : : Assert(internal.get() != 0);
198 [ + - ]: 13 : if (!internal->items.empty())
199 : 13 : internal->fetch_items(0, internal->items.size() - 1);
200 : 13 : }
201 : :
202 : : percent
203 : 78 : MSet::convert_to_percent(Xapian::weight wt) const
204 : : {
205 : : LOGCALL(API, Xapian::percent, "Xapian::MSet::convert_to_percent", wt);
206 : : Assert(internal.get() != 0);
207 : 78 : RETURN(internal->convert_to_percent_internal(wt));
208 : : }
209 : :
210 : : percent
211 : 296 : MSet::convert_to_percent(const MSetIterator & it) const
212 : : {
213 : : LOGCALL(API, Xapian::percent, "Xapian::MSet::convert_to_percent", it);
214 : : Assert(internal.get() != 0);
215 : 296 : RETURN(internal->convert_to_percent_internal(it.get_weight()));
216 : : }
217 : :
218 : : Xapian::doccount
219 : 222 : MSet::get_termfreq(const string &tname) const
220 : : {
221 : : LOGCALL(API, Xapian::doccount, "Xapian::MSet::get_termfreq", tname);
222 : 222 : map<string, Internal::TermFreqAndWeight>::const_iterator i;
223 : : Assert(internal.get() != 0);
224 : 222 : i = internal->termfreqandwts.find(tname);
225 [ + + ]: 222 : if (i != internal->termfreqandwts.end()) {
226 : 195 : RETURN(i->second.termfreq);
227 : : }
228 [ + + ]: 27 : if (internal->enquire.get() == 0) {
229 : 1 : throw InvalidOperationError("Can't get termfreq from an MSet which is not derived from a query.");
230 : : }
231 : 221 : RETURN(internal->enquire->get_termfreq(tname));
232 : : }
233 : :
234 : : Xapian::weight
235 : 156 : MSet::get_termweight(const string &tname) const
236 : : {
237 : : LOGCALL(API, Xapian::weight, "Xapian::MSet::get_termweight", tname);
238 : 156 : map<string, Internal::TermFreqAndWeight>::const_iterator i;
239 : : Assert(internal.get() != 0);
240 : 156 : i = internal->termfreqandwts.find(tname);
241 [ + + ]: 156 : if (i == internal->termfreqandwts.end()) {
242 : : throw InvalidArgumentError("Term weight of `" + tname +
243 : 26 : "' not available.");
244 : : }
245 : 130 : RETURN(i->second.termweight);
246 : : }
247 : :
248 : : Xapian::doccount
249 : 4494 : MSet::get_firstitem() const
250 : : {
251 : : Assert(internal.get() != 0);
252 : 4494 : return internal->firstitem;
253 : : }
254 : :
255 : : Xapian::doccount
256 : 6163 : MSet::get_matches_lower_bound() const
257 : : {
258 : : Assert(internal.get() != 0);
259 : 6163 : return internal->matches_lower_bound;
260 : : }
261 : :
262 : : Xapian::doccount
263 : 5590 : MSet::get_matches_estimated() const
264 : : {
265 : : Assert(internal.get() != 0);
266 : 5590 : return internal->matches_estimated;
267 : : }
268 : :
269 : : Xapian::doccount
270 : 5890 : MSet::get_matches_upper_bound() const
271 : : {
272 : : Assert(internal.get() != 0);
273 : 5890 : return internal->matches_upper_bound;
274 : : }
275 : :
276 : : Xapian::doccount
277 : 5580 : MSet::get_uncollapsed_matches_lower_bound() const
278 : : {
279 : : Assert(internal.get() != 0);
280 : 5580 : return internal->uncollapsed_lower_bound;
281 : : }
282 : :
283 : : Xapian::doccount
284 : 5263 : MSet::get_uncollapsed_matches_estimated() const
285 : : {
286 : : Assert(internal.get() != 0);
287 : 5263 : return internal->uncollapsed_estimated;
288 : : }
289 : :
290 : : Xapian::doccount
291 : 5257 : MSet::get_uncollapsed_matches_upper_bound() const
292 : : {
293 : : Assert(internal.get() != 0);
294 : 5257 : return internal->uncollapsed_upper_bound;
295 : : }
296 : :
297 : : Xapian::weight
298 : 4741 : MSet::get_max_possible() const
299 : : {
300 : : Assert(internal.get() != 0);
301 : 4741 : return internal->max_possible;
302 : : }
303 : :
304 : : Xapian::weight
305 : 4509 : MSet::get_max_attained() const
306 : : {
307 : : Assert(internal.get() != 0);
308 : 4509 : return internal->max_attained;
309 : : }
310 : :
311 : : Xapian::doccount
312 : 12149013 : MSet::size() const
313 : : {
314 : : Assert(internal.get() != 0);
315 : 12149013 : return internal->items.size();
316 : : }
317 : :
318 : : bool
319 : 121 : MSet::empty() const
320 : : {
321 : : Assert(internal.get() != 0);
322 : 121 : return internal->items.empty();
323 : : }
324 : :
325 : : void
326 : 0 : MSet::swap(MSet & other)
327 : : {
328 : 0 : std::swap(internal, other.internal);
329 : 0 : }
330 : :
331 : : MSetIterator
332 : 11282 : MSet::begin() const
333 : : {
334 : 11282 : return MSetIterator(0, *this);
335 : : }
336 : :
337 : : MSetIterator
338 : 70836 : MSet::end() const
339 : : {
340 : : Assert(internal.get() != 0);
341 : 70836 : return MSetIterator(internal->items.size(), *this);
342 : : }
343 : :
344 : : MSetIterator
345 : 45808419 : MSet::operator[](Xapian::doccount i) const
346 : : {
347 : : // Don't test 0 <= i - that gives a compiler warning if i is unsigned
348 : : Assert(0 < (i + 1) && i < size());
349 : 45808419 : return MSetIterator(i, *this);
350 : : }
351 : :
352 : : MSetIterator
353 : 26 : MSet::back() const
354 : : {
355 : : Assert(!empty());
356 : : Assert(internal.get() != 0);
357 : 26 : return MSetIterator(internal->items.size() - 1, *this);
358 : : }
359 : :
360 : : string
361 : 196 : MSet::get_description() const
362 : : {
363 : : Assert(internal.get() != 0);
364 : 196 : return "Xapian::MSet(" + internal->get_description() + ")";
365 : : }
366 : :
367 : : percent
368 : 1316 : MSet::Internal::convert_to_percent_internal(Xapian::weight wt) const
369 : : {
370 : : LOGCALL(MATCH, Xapian::percent, "Xapian::MSet::Internal::convert_to_percent_internal", wt);
371 [ - + ]: 1316 : if (percent_factor == 0) RETURN(100);
372 : :
373 : : // Excess precision on x86 can result in a difference here.
374 : 1316 : double v = wt * percent_factor + 100.0 * DBL_EPSILON;
375 : 1316 : Xapian::percent pcent = static_cast<Xapian::percent>(v);
376 : : LOGLINE(MATCH, "wt = " << wt << ", max_possible = " << max_possible <<
377 : : " => pcent = " << pcent);
378 [ - + ]: 1316 : if (pcent > 100) pcent = 100;
379 [ - + ]: 1316 : if (pcent < 0) pcent = 0;
380 [ - + ][ # # ]: 1316 : if (pcent == 0 && wt > 0) pcent = 1;
381 : :
382 : 1316 : RETURN(pcent);
383 : : }
384 : :
385 : : Document
386 : 40913 : MSet::Internal::get_doc_by_index(Xapian::doccount index) const
387 : : {
388 : : LOGCALL(MATCH, Document, "Xapian::MSet::Internal::get_doc_by_index", index);
389 : 40913 : index += firstitem;
390 : 40913 : map<Xapian::doccount, Document>::const_iterator doc;
391 : 40913 : doc = indexeddocs.find(index);
392 [ + + ]: 40913 : if (doc != indexeddocs.end()) {
393 : 520 : RETURN(doc->second);
394 : : }
395 [ + - ][ - + ]: 40393 : if (index < firstitem || index >= firstitem + items.size()) {
[ - + ]
396 : 0 : throw RangeError("The mset returned from the match does not contain the document at index " + str(index));
397 : : }
398 : 40393 : fetch_items(index, index); // FIXME: this checks indexeddocs AGAIN!
399 : : /* Actually read the fetched documents */
400 : 40393 : read_docs();
401 : : Assert(indexeddocs.find(index) != indexeddocs.end());
402 : : Assert(indexeddocs.find(index)->first == index); // Paranoid assert
403 : 40913 : RETURN(indexeddocs.find(index)->second);
404 : : }
405 : :
406 : : void
407 : 40445 : MSet::Internal::fetch_items(Xapian::doccount first, Xapian::doccount last) const
408 : : {
409 : : LOGCALL_VOID(MATCH, "Xapian::MSet::Internal::fetch_items", first | last);
410 [ - + ]: 40445 : if (enquire.get() == 0) {
411 : 0 : throw InvalidOperationError("Can't fetch documents from an MSet which is not derived from a query.");
412 : : }
413 [ + + ]: 81072 : for (Xapian::doccount i = first; i <= last; ++i) {
414 : 40627 : map<Xapian::doccount, Document>::const_iterator doc;
415 : 40627 : doc = indexeddocs.find(i);
416 [ + - ]: 40627 : if (doc == indexeddocs.end()) {
417 : : /* We don't have the document cached */
418 : 40627 : set<Xapian::doccount>::const_iterator s;
419 : 40627 : s = requested_docs.find(i);
420 [ + + ]: 40627 : if (s == requested_docs.end()) {
421 : : /* We haven't even requested it yet - do so now. */
422 : 40458 : enquire->request_doc(items[i - firstitem]);
423 : 40458 : requested_docs.insert(i);
424 : : }
425 : : }
426 : : }
427 : 40445 : }
428 : :
429 : : string
430 : 196 : MSet::Internal::get_description() const
431 : : {
432 : 196 : string description = "Xapian::MSet::Internal(";
433 : :
434 : : description += "firstitem=" + str(firstitem) + ", " +
435 : : "matches_lower_bound=" + str(matches_lower_bound) + ", " +
436 : : "matches_estimated=" + str(matches_estimated) + ", " +
437 : : "matches_upper_bound=" + str(matches_upper_bound) + ", " +
438 : : "max_possible=" + str(max_possible) + ", " +
439 : 196 : "max_attained=" + str(max_attained);
440 : :
441 [ + + ]: 5916 : for (vector<Xapian::Internal::MSetItem>::const_iterator i = items.begin();
442 : : i != items.end(); ++i) {
443 [ + - ]: 5720 : if (!description.empty()) description += ", ";
444 : 5720 : description += i->get_description();
445 : : }
446 : :
447 : 196 : description += ")";
448 : :
449 : 0 : return description;
450 : : }
451 : :
452 : : void
453 : 40393 : MSet::Internal::read_docs() const
454 : : {
455 : 40393 : set<Xapian::doccount>::const_iterator i;
456 [ + + ]: 80851 : for (i = requested_docs.begin(); i != requested_docs.end(); ++i) {
457 : 40458 : indexeddocs[*i] = enquire->read_doc(items[*i - firstitem]);
458 : : LOGLINE(MATCH, "stored doc at index " << *i << " is " << indexeddocs[*i]);
459 : : }
460 : : /* Clear list of requested but not fetched documents. */
461 : 40393 : requested_docs.clear();
462 : 40393 : }
463 : :
464 : : // Methods for Xapian::ESet
465 : :
466 : 511 : ESet::ESet() : internal(new Internal) { }
467 : :
468 : 5044 : ESet::~ESet()
469 : : {
470 : 5044 : }
471 : :
472 : 4533 : ESet::ESet(const ESet & other) : internal(other.internal)
473 : : {
474 : 4533 : }
475 : :
476 : : void
477 : 371 : ESet::operator=(const ESet &other)
478 : : {
479 : 371 : internal = other.internal;
480 : 371 : }
481 : :
482 : : Xapian::termcount
483 : 0 : ESet::get_ebound() const
484 : : {
485 : 0 : return internal->ebound;
486 : : }
487 : :
488 : : Xapian::termcount
489 : 72 : ESet::size() const
490 : : {
491 : 72 : return internal->items.size();
492 : : }
493 : :
494 : : bool
495 : 0 : ESet::empty() const
496 : : {
497 : 0 : return internal->items.empty();
498 : : }
499 : :
500 : : void
501 : 0 : ESet::swap(ESet & other)
502 : : {
503 : 0 : std::swap(internal, other.internal);
504 : 0 : }
505 : :
506 : : ESetIterator
507 : 183 : ESet::begin() const
508 : : {
509 : 183 : return ESetIterator(0, *this);
510 : : }
511 : :
512 : : ESetIterator
513 : 4007 : ESet::end() const
514 : : {
515 : : Assert(internal.get() != 0);
516 : 4007 : return ESetIterator(internal->items.size(), *this);
517 : : }
518 : :
519 : : ESetIterator
520 : 143 : ESet::operator[](Xapian::termcount i) const
521 : : {
522 : : // Don't test 0 <= i - that gives a compiler warning if i is unsigned
523 : : Assert(0 < (i + 1) && i < size());
524 : 143 : return ESetIterator(i, *this);
525 : : }
526 : :
527 : : ESetIterator
528 : 13 : ESet::back() const
529 : : {
530 : : Assert(!empty());
531 : : Assert(internal.get() != 0);
532 : 13 : return ESetIterator(internal->items.size() - 1, *this);
533 : : }
534 : :
535 : : string
536 : 1 : ESet::get_description() const
537 : : {
538 : : Assert(internal.get() != 0);
539 : 1 : return "Xapian::ESet(" + internal->get_description() + ")";
540 : : }
541 : :
542 : : // Xapian::ESetIterator
543 : :
544 : : const string &
545 : 3814 : ESetIterator::operator *() const
546 : : {
547 : 3814 : return eset.internal->items[index].term;
548 : : }
549 : :
550 : : Xapian::weight
551 : 1568 : ESetIterator::get_weight() const
552 : : {
553 : 1568 : return eset.internal->items[index].wt;
554 : : }
555 : :
556 : : string
557 : 1 : ESetIterator::get_description() const
558 : : {
559 : 1 : return "Xapian::ESetIterator(" + str(index) + ")";
560 : : }
561 : :
562 : : // MSetIterator
563 : :
564 : : Xapian::docid
565 : 22962973 : MSetIterator::operator *() const
566 : : {
567 : 22962973 : return mset.internal->items[index].did;
568 : : }
569 : :
570 : : Document
571 : 40913 : MSetIterator::get_document() const
572 : : {
573 : 40913 : return mset.internal->get_doc_by_index(index);
574 : : }
575 : :
576 : : Xapian::weight
577 : 22934850 : MSetIterator::get_weight() const
578 : : {
579 : 22934850 : return mset.internal->items[index].wt;
580 : : }
581 : :
582 : : std::string
583 : 82008 : MSetIterator::get_collapse_key() const
584 : : {
585 : 82008 : return mset.internal->items[index].collapse_key;
586 : : }
587 : :
588 : : Xapian::doccount
589 : 38664 : MSetIterator::get_collapse_count() const
590 : : {
591 : 38664 : return mset.internal->items[index].collapse_count;
592 : : }
593 : :
594 : : Xapian::percent
595 : 942 : MSetIterator::get_percent() const
596 : : {
597 : : LOGCALL(API, Xapian::percent, "MSetIterator::get_percent", NO_ARGS);
598 : 942 : RETURN(mset.internal->convert_to_percent_internal(get_weight()));
599 : : }
600 : :
601 : : string
602 : 1 : MSetIterator::get_description() const
603 : : {
604 : 1 : return "Xapian::MSetIterator(" + str(index) + ")";
605 : : }
606 : :
607 : : // Methods for Xapian::Enquire::Internal
608 : :
609 : 5700 : Enquire::Internal::Internal(const Database &db_, ErrorHandler * errorhandler_)
610 : : : db(db_), query(), collapse_key(Xapian::BAD_VALUENO), collapse_max(0),
611 : : order(Enquire::ASCENDING), percent_cutoff(0), weight_cutoff(0),
612 : : sort_key(Xapian::BAD_VALUENO), sort_by(REL), sort_value_forward(true),
613 : 5700 : sorter(0), errorhandler(errorhandler_), weight(0)
614 : : {
615 [ + + # # ]: 5700 : if (db.internal.empty()) {
616 : 1 : throw InvalidArgumentError("Can't make an Enquire object from an uninitialised Database object.");
617 : : }
618 : 5702 : }
619 : :
620 : 5699 : Enquire::Internal::~Internal()
621 : : {
622 [ + + ][ # # ]: 5699 : delete weight;
623 : 5699 : weight = 0;
624 : 5699 : }
625 : :
626 : : void
627 : 10515 : Enquire::Internal::set_query(const Query &query_, termcount qlen_)
628 : : {
629 : 10515 : query = query_;
630 [ + + ]: 10515 : qlen = qlen_ ? qlen_ : query.get_length();
631 : 10515 : }
632 : :
633 : : const Query &
634 : 0 : Enquire::Internal::get_query()
635 : : {
636 : 0 : return query;
637 : : }
638 : :
639 : : MSet
640 : 175217 : Enquire::Internal::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
641 : : Xapian::doccount check_at_least, const RSet *rset,
642 : : const MatchDecider *mdecider,
643 : : const MatchDecider *matchspy_legacy) const
644 : : {
645 : : LOGCALL(MATCH, MSet, "Enquire::Internal::get_mset", first | maxitems | check_at_least | rset | mdecider | matchspy_legacy);
646 : :
647 [ + + ][ + + ]: 175217 : if (percent_cutoff && (sort_by == VAL || sort_by == VAL_REL)) {
[ + + ]
648 : 52 : throw Xapian::UnimplementedError("Use of a percentage cutoff while sorting primary by value isn't currently supported");
649 : : }
650 : :
651 [ + + ]: 175165 : if (weight == 0) {
652 : 5443 : weight = new BM25Weight;
653 : : }
654 : :
655 : 175165 : Xapian::doccount first_orig = first;
656 : : {
657 : 175165 : Xapian::doccount docs = db.get_doccount();
658 : 175165 : first = min(first, docs);
659 : 175165 : maxitems = min(maxitems, docs);
660 : 175165 : check_at_least = min(check_at_least, docs);
661 : 175165 : check_at_least = max(check_at_least, maxitems);
662 : : }
663 : :
664 : 175165 : Xapian::Weight::Internal stats;
665 : : ::MultiMatch match(db, query.internal.get(), qlen, rset,
666 : : collapse_max, collapse_key,
667 : : percent_cutoff, weight_cutoff,
668 : : order, sort_key, sort_by, sort_value_forward,
669 : : errorhandler, stats, weight, spies,
670 : : (sorter != NULL),
671 [ + + + + ]: 175165 : (mdecider != NULL || matchspy_legacy != NULL));
672 : : // Run query and put results into supplied Xapian::MSet object.
673 : 175120 : MSet retval;
674 : : match.get_mset(first, maxitems, check_at_least, retval,
675 : 175120 : stats, mdecider, matchspy_legacy, sorter);
676 [ + + + - ]: 175120 : if (first_orig != first && retval.internal.get()) {
[ + + ]
677 : 16 : retval.internal->firstitem = first_orig;
678 : : }
679 : :
680 : : Assert(weight->name() != "bool" || retval.get_max_possible() == 0);
681 : :
682 : : // The Xapian::MSet needs to have a pointer to ourselves, so that it can
683 : : // retrieve the documents. This is set here explicitly to avoid having
684 : : // to pass it into the matcher, which gets messy particularly in the
685 : : // networked case.
686 : 175120 : retval.internal->enquire = this;
687 : :
688 : 175165 : return retval;
689 : : }
690 : :
691 : : ESet
692 : 199 : Enquire::Internal::get_eset(Xapian::termcount maxitems,
693 : : const RSet & rset, int flags, double k,
694 : : const ExpandDecider * edecider, Xapian::weight min_wt) const
695 : : {
696 : : LOGCALL(MATCH, ESet, "Enquire::Internal::get_eset", maxitems | rset | flags | k | edecider | min_wt);
697 : :
698 [ + + ][ - + ]: 199 : if (maxitems == 0 || rset.empty()) {
[ + + ]
699 : : // Either we were asked for no results, or wouldn't produce any
700 : : // because no documents were marked as relevant.
701 : 13 : RETURN(ESet());
702 : : }
703 : :
704 : : LOGVALUE(MATCH, rset.size());
705 : :
706 : : /* The AutoPtrs will clean up any dynamically allocated
707 : : * expand deciders automatically.
708 : : */
709 : 186 : AutoPtr<ExpandDecider> decider_noquery;
710 : 186 : AutoPtr<ExpandDecider> decider_andnoquery;
711 : :
712 [ + + + + ]: 186 : if (!query.empty() && !(flags & Enquire::INCLUDE_QUERY_TERMS)) {
[ + + ]
713 : : AutoPtr<ExpandDecider> temp1(
714 : : new ExpandDeciderFilterTerms(query.get_terms_begin(),
715 : 117 : query.get_terms_end()));
716 : 117 : decider_noquery = temp1;
717 : :
718 [ + + ]: 117 : if (edecider) {
719 : : AutoPtr<ExpandDecider> temp2(
720 : 13 : new ExpandDeciderAnd(decider_noquery.get(), edecider));
721 : 13 : decider_andnoquery = temp2;
722 : 13 : edecider = decider_andnoquery.get();
723 : : } else {
724 : 104 : edecider = decider_noquery.get();
725 : 117 : }
726 : : }
727 : :
728 : 186 : bool use_exact_termfreq(flags & Enquire::USE_EXACT_TERMFREQ);
729 : 186 : ExpandWeight eweight(db, rset.size(), use_exact_termfreq, k);
730 : :
731 : 186 : Xapian::ESet eset;
732 : 186 : eset.internal->expand(maxitems, db, rset, edecider, eweight, min_wt);
733 : 199 : RETURN(eset);
734 : : }
735 : :
736 : : class ByQueryIndexCmp {
737 : : private:
738 : : typedef map<string, unsigned int> tmap_t;
739 : : const tmap_t &tmap;
740 : : public:
741 : 68 : ByQueryIndexCmp(const tmap_t &tmap_) : tmap(tmap_) {}
742 : 281 : bool operator()(const string &left,
743 : : const string &right) const {
744 : 281 : tmap_t::const_iterator l, r;
745 : 281 : l = tmap.find(left);
746 : 281 : r = tmap.find(right);
747 : : Assert((l != tmap.end()) && (r != tmap.end()));
748 : :
749 : 281 : return l->second < r->second;
750 : : }
751 : : };
752 : :
753 : : TermIterator
754 : 81 : Enquire::Internal::get_matching_terms(Xapian::docid did) const
755 : : {
756 [ + + ]: 81 : if (query.empty())
757 : 13 : throw Xapian::InvalidArgumentError("get_matching_terms with empty query");
758 : : //return TermIterator(NULL);
759 : :
760 : : // The ordered list of terms in the query.
761 : 68 : TermIterator qt = query.get_terms_begin();
762 : 68 : TermIterator qt_end = query.get_terms_end();
763 : :
764 : : // copy the list of query terms into a map for faster access.
765 : : // FIXME: a hash would be faster than a map, if this becomes
766 : : // a problem.
767 : 68 : map<string, unsigned int> tmap;
768 : 68 : unsigned int index = 1;
769 [ + + ]: 340 : for ( ; qt != qt_end; ++qt) {
770 [ + + ]: 272 : if (tmap.find(*qt) == tmap.end())
771 : 259 : tmap[*qt] = index++;
772 : : }
773 : :
774 : 68 : vector<string> matching_terms;
775 : :
776 : 68 : TermIterator docterms = db.termlist_begin(did);
777 : 68 : TermIterator docterms_end = db.termlist_end(did);
778 [ + + ]: 2033 : while (docterms != docterms_end) {
779 : 1965 : string term = *docterms;
780 : 1965 : map<string, unsigned int>::iterator t = tmap.find(term);
781 [ + + ]: 1965 : if (t != tmap.end()) matching_terms.push_back(term);
782 : 1965 : docterms++;
783 : : }
784 : :
785 : : // sort the resulting list by query position.
786 : 68 : sort(matching_terms.begin(), matching_terms.end(), ByQueryIndexCmp(tmap));
787 : :
788 : : return TermIterator(new VectorTermList(matching_terms.begin(),
789 : 68 : matching_terms.end()));
790 : : }
791 : :
792 : : TermIterator
793 : 68 : Enquire::Internal::get_matching_terms(const MSetIterator &it) const
794 : : {
795 : : // FIXME: take advantage of MSetIterator to ensure that database
796 : : // doesn't get modified underneath us.
797 : 68 : return get_matching_terms(*it);
798 : : }
799 : :
800 : : Xapian::doccount
801 : 26 : Enquire::Internal::get_termfreq(const string &tname) const
802 : : {
803 : 26 : return db.get_termfreq(tname);
804 : : }
805 : :
806 : : string
807 : 1 : Enquire::Internal::get_description() const
808 : : {
809 : 1 : string description = db.get_description();
810 : 1 : description += ", ";
811 : 1 : description += query.get_description();
812 : 0 : return description;
813 : : }
814 : :
815 : : // Private methods for Xapian::Enquire::Internal
816 : :
817 : : void
818 : 40458 : Enquire::Internal::request_doc(const Xapian::Internal::MSetItem &item) const
819 : : {
820 : : try {
821 : 40458 : unsigned int multiplier = db.internal.size();
822 : :
823 : 40458 : Xapian::docid realdid = (item.did - 1) / multiplier + 1;
824 : 40458 : Xapian::doccount dbnumber = (item.did - 1) % multiplier;
825 : :
826 : 40458 : db.internal[dbnumber]->request_document(realdid);
827 : 0 : } catch (Error & e) {
828 [ # # ]: 0 : if (errorhandler) (*errorhandler)(e);
829 : 0 : throw;
830 : : }
831 : 40458 : }
832 : :
833 : : Document
834 : 40458 : Enquire::Internal::read_doc(const Xapian::Internal::MSetItem &item) const
835 : : {
836 : : try {
837 : 40458 : unsigned int multiplier = db.internal.size();
838 : :
839 : 40458 : Xapian::docid realdid = (item.did - 1) / multiplier + 1;
840 : 40458 : Xapian::doccount dbnumber = (item.did - 1) % multiplier;
841 : :
842 : : Xapian::Document::Internal *doc;
843 : 40458 : doc = db.internal[dbnumber]->collect_document(realdid);
844 : 40458 : return Document(doc);
845 : 0 : } catch (Error & e) {
846 [ # # ]: 0 : if (errorhandler) (*errorhandler)(e);
847 : 0 : throw;
848 : : }
849 : : }
850 : :
851 : : void
852 : 0 : Enquire::Internal::register_match_decider(const string &,
853 : : const MatchDecider *)
854 : : {
855 : 0 : }
856 : :
857 : : // Methods of Xapian::Enquire
858 : :
859 : 1 : Enquire::Enquire(const Enquire & other) : internal(other.internal)
860 : : {
861 : : LOGCALL_VOID(API, "Xapian::Enquire::Enquire", other);
862 : 1 : }
863 : :
864 : : void
865 : 1 : Enquire::operator=(const Enquire & other)
866 : : {
867 : : LOGCALL_VOID(API, "Xapian::Enquire::operator=", other);
868 : 1 : internal = other.internal;
869 : 1 : }
870 : :
871 : 5700 : Enquire::Enquire(const Database &databases, ErrorHandler * errorhandler)
872 : 5701 : : internal(new Internal(databases, errorhandler))
873 : : {
874 : : LOGCALL_VOID(API, "Xapian::Enquire::Enquire", databases);
875 : 5699 : }
876 : :
877 : 5700 : Enquire::~Enquire()
878 : : {
879 : : LOGCALL_VOID(API, "Xapian::Enquire::~Enquire", NO_ARGS);
880 : 5700 : }
881 : :
882 : : void
883 : 10515 : Enquire::set_query(const Query & query, termcount len)
884 : : {
885 : : LOGCALL_VOID(API, "Xapian::Enquire::set_query", query | len);
886 : 10515 : internal->set_query(query, len);
887 : 10515 : }
888 : :
889 : : const Query &
890 : 0 : Enquire::get_query() const
891 : : {
892 : : LOGCALL(API, const Xapian::Query &, "Xapian::Enquire::get_query", NO_ARGS);
893 : : try {
894 : 0 : RETURN(internal->get_query());
895 : 0 : } catch (Error & e) {
896 [ # # ]: 0 : if (internal->errorhandler) (*internal->errorhandler)(e);
897 : 0 : throw;
898 : : }
899 : : }
900 : :
901 : : void
902 : 123 : Enquire::add_matchspy(MatchSpy * spy) {
903 : : LOGCALL_VOID(API, "Xapian::Enquire::add_matchspy", spy);
904 : 123 : internal->spies.push_back(spy);
905 : 123 : }
906 : :
907 : : void
908 : 0 : Enquire::clear_matchspies() {
909 : : LOGCALL(API, const Xapian::Query &, "Xapian::Enquire::clear_matchspies", NO_ARGS);
910 : 0 : internal->spies.clear();
911 : 0 : }
912 : :
913 : : void
914 : 226 : Enquire::set_weighting_scheme(const Weight &weight_)
915 : : {
916 : : LOGCALL_VOID(API, "Xapian::Enquire::set_weighting_scheme", weight_);
917 : : // Clone first in case doing so throws an exception.
918 : 226 : Weight * wt = weight_.clone();
919 : 226 : swap(wt, internal->weight);
920 [ + + ]: 226 : delete wt;
921 : 226 : }
922 : :
923 : : void
924 : 1204 : Enquire::set_collapse_key(Xapian::valueno collapse_key, Xapian::doccount collapse_max)
925 : : {
926 [ + + ]: 1204 : if (collapse_key == Xapian::BAD_VALUENO) collapse_max = 0;
927 : 1204 : internal->collapse_key = collapse_key;
928 : 1204 : internal->collapse_max = collapse_max;
929 : 1204 : }
930 : :
931 : : void
932 : 251 : Enquire::set_docid_order(Enquire::docid_order order)
933 : : {
934 : 251 : internal->order = order;
935 : 251 : }
936 : :
937 : : void
938 : 114 : Enquire::set_cutoff(Xapian::percent percent_cutoff, Xapian::weight weight_cutoff)
939 : : {
940 : 114 : internal->percent_cutoff = percent_cutoff;
941 : 114 : internal->weight_cutoff = weight_cutoff;
942 : 114 : }
943 : :
944 : : void
945 : 53 : Enquire::set_sort_by_relevance()
946 : : {
947 : 53 : internal->sort_by = Internal::REL;
948 : 53 : }
949 : :
950 : : void
951 : 381 : Enquire::set_sort_by_value(valueno sort_key, bool ascending)
952 : : {
953 : 381 : internal->sorter = NULL;
954 : 381 : internal->sort_key = sort_key;
955 : 381 : internal->sort_by = Internal::VAL;
956 : 381 : internal->sort_value_forward = ascending;
957 : 381 : }
958 : :
959 : : void
960 : 180 : Enquire::set_sort_by_value_then_relevance(valueno sort_key, bool ascending)
961 : : {
962 : 180 : internal->sorter = NULL;
963 : 180 : internal->sort_key = sort_key;
964 : 180 : internal->sort_by = Internal::VAL_REL;
965 : 180 : internal->sort_value_forward = ascending;
966 : 180 : }
967 : :
968 : : void
969 : 144 : Enquire::set_sort_by_relevance_then_value(valueno sort_key, bool ascending)
970 : : {
971 : 144 : internal->sorter = NULL;
972 : 144 : internal->sort_key = sort_key;
973 : 144 : internal->sort_by = Internal::REL_VAL;
974 : 144 : internal->sort_value_forward = ascending;
975 : 144 : }
976 : :
977 : : void
978 : 180 : Enquire::set_sort_by_key(KeyMaker * sorter, bool ascending)
979 : : {
980 [ - + ]: 180 : if (sorter == NULL)
981 : 0 : throw InvalidArgumentError("sorter can't be NULL");
982 : 180 : internal->sorter = sorter;
983 : 180 : internal->sort_by = Internal::VAL;
984 : 180 : internal->sort_value_forward = ascending;
985 : 180 : }
986 : :
987 : : void
988 : 0 : Enquire::set_sort_by_key_then_relevance(KeyMaker * sorter, bool ascending)
989 : : {
990 [ # # ]: 0 : if (sorter == NULL)
991 : 0 : throw InvalidArgumentError("sorter can't be NULL");
992 : 0 : internal->sorter = sorter;
993 : 0 : internal->sort_by = Internal::VAL_REL;
994 : 0 : internal->sort_value_forward = ascending;
995 : 0 : }
996 : :
997 : : void
998 : 0 : Enquire::set_sort_by_relevance_then_key(KeyMaker * sorter, bool ascending)
999 : : {
1000 [ # # ]: 0 : if (sorter == NULL)
1001 : 0 : throw Xapian::InvalidArgumentError("sorter can't be NULL");
1002 : 0 : internal->sorter = sorter;
1003 : 0 : internal->sort_by = Internal::REL_VAL;
1004 : 0 : internal->sort_value_forward = ascending;
1005 : 0 : }
1006 : :
1007 : : MSet
1008 : 12 : Enquire::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
1009 : : Xapian::doccount check_at_least, const RSet *rset,
1010 : : const MatchDecider *mdecider,
1011 : : const MatchDecider *matchspy) const
1012 : : {
1013 : : LOGCALL(API, Xapian::MSet, "Xapian::Enquire::get_mset", first | maxitems | check_at_least | rset | mdecider | matchspy);
1014 : :
1015 : : try {
1016 : 12 : RETURN(internal->get_mset(first, maxitems, check_at_least, rset,
1017 : : mdecider, matchspy));
1018 : 24 : } catch (Error & e) {
1019 [ - + ]: 12 : if (internal->errorhandler) (*internal->errorhandler)(e);
1020 : 12 : throw;
1021 : : }
1022 : : }
1023 : :
1024 : : MSet
1025 : 175205 : Enquire::get_mset(Xapian::doccount first, Xapian::doccount maxitems,
1026 : : Xapian::doccount check_at_least, const RSet *rset,
1027 : : const MatchDecider *mdecider) const
1028 : : {
1029 : : LOGCALL(API, Xapian::MSet, "Xapian::Enquire::get_mset", first | maxitems | check_at_least | rset | mdecider);
1030 : :
1031 : : try {
1032 : 175205 : RETURN(internal->get_mset(first, maxitems, check_at_least, rset,
1033 : : mdecider, NULL));
1034 : 170 : } catch (Error & e) {
1035 [ - + ]: 85 : if (internal->errorhandler) (*internal->errorhandler)(e);
1036 : 85 : throw;
1037 : : }
1038 : : }
1039 : :
1040 : : ESet
1041 : 173 : Enquire::get_eset(Xapian::termcount maxitems, const RSet & rset, int flags,
1042 : : double k, const ExpandDecider * edecider) const
1043 : : {
1044 : : LOGCALL(API, Xapian::ESet, "Xapian::Enquire::get_eset", maxitems | rset | flags | k | edecider);
1045 : :
1046 : : try {
1047 : 173 : RETURN(internal->get_eset(maxitems, rset, flags, k, edecider, 0));
1048 : 0 : } catch (Error & e) {
1049 [ # # ]: 0 : if (internal->errorhandler) (*internal->errorhandler)(e);
1050 : 0 : throw;
1051 : : }
1052 : : }
1053 : :
1054 : : ESet
1055 : 26 : Enquire::get_eset(Xapian::termcount maxitems, const RSet & rset, int flags,
1056 : : double k, const ExpandDecider * edecider, Xapian::weight min_wt) const
1057 : : {
1058 : : LOGCALL(API, Xapian::ESet, "Xapian::Enquire::get_eset", maxitems | rset | flags | k | edecider | min_wt);
1059 : :
1060 : : try {
1061 : 26 : RETURN(internal->get_eset(maxitems, rset, flags, k, edecider, min_wt));
1062 : 0 : } catch (Error & e) {
1063 [ # # ]: 0 : if (internal->errorhandler) (*internal->errorhandler)(e);
1064 : 0 : throw;
1065 : : }
1066 : : }
1067 : :
1068 : : TermIterator
1069 : 68 : Enquire::get_matching_terms_begin(const MSetIterator &it) const
1070 : : {
1071 : : LOGCALL(API, Xapian::TermIterator, "Xapian::Enquire::get_matching_terms", it);
1072 : : try {
1073 : 68 : RETURN(internal->get_matching_terms(it));
1074 : 0 : } catch (Error & e) {
1075 [ # # ]: 0 : if (internal->errorhandler) (*internal->errorhandler)(e);
1076 : 0 : throw;
1077 : : }
1078 : : }
1079 : :
1080 : : TermIterator
1081 : 13 : Enquire::get_matching_terms_begin(Xapian::docid did) const
1082 : : {
1083 : : LOGCALL(API, Xapian::TermIterator, "Xapian::Enquire::get_matching_terms", did);
1084 : : try {
1085 : 13 : RETURN(internal->get_matching_terms(did));
1086 : 26 : } catch (Error & e) {
1087 [ - + ]: 13 : if (internal->errorhandler) (*internal->errorhandler)(e);
1088 : 13 : throw;
1089 : : }
1090 : : }
1091 : :
1092 : : string
1093 : 1 : Enquire::get_description() const
1094 : : {
1095 : 1 : return "Xapian::Enquire(" + internal->get_description() + ")";
1096 : : }
1097 : :
1098 : : }
|