Branch data Line data Source code
1 : : /* inmemory_database.cc
2 : : *
3 : : * Copyright 1999,2000,2001 BrightStation PLC
4 : : * Copyright 2002 Ananova Ltd
5 : : * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010 Olly Betts
6 : : * Copyright 2006,2009 Lemur Consulting Ltd
7 : : *
8 : : * This program is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU General Public License as
10 : : * published by the Free Software Foundation; either version 2 of the
11 : : * License, or (at your option) any later version.
12 : : *
13 : : * This program is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 : : * USA
22 : : */
23 : :
24 : : #include <config.h>
25 : :
26 : : #include "inmemory_database.h"
27 : :
28 : : #include "debuglog.h"
29 : :
30 : : #include "expandweight.h"
31 : : #include "inmemory_document.h"
32 : : #include "inmemory_alltermslist.h"
33 : : #include "str.h"
34 : : #include "valuestats.h"
35 : :
36 : : #include <string>
37 : : #include <vector>
38 : : #include <map>
39 : :
40 : : #include <xapian/error.h>
41 : : #include <xapian/valueiterator.h>
42 : :
43 : : using std::make_pair;
44 : :
45 : : inline void
46 : 1063941 : InMemoryTerm::add_posting(const InMemoryPosting & post)
47 : : {
48 : : // Add document to right place in list
49 : 1063941 : vector<InMemoryPosting>::iterator p;
50 : : p = lower_bound(docs.begin(), docs.end(),
51 : 1063941 : post, InMemoryPostingLessThan());
52 [ + + + + ]: 1063941 : if (p == docs.end() || InMemoryPostingLessThan()(post, *p)) {
[ + + ]
53 : 795713 : docs.insert(p, post);
54 [ + + ]: 268228 : } else if (!p->valid) {
55 : 148 : *p = post;
56 : : } else {
57 : 268080 : (*p).merge(post);
58 : : }
59 : 1063941 : }
60 : :
61 : : inline void
62 : 1063941 : InMemoryDoc::add_posting(const InMemoryTermEntry & post)
63 : : {
64 : : // Add document to right place in list
65 : 1063941 : vector<InMemoryTermEntry>::iterator p;
66 : : p = lower_bound(terms.begin(), terms.end(),
67 : 1063941 : post, InMemoryTermEntryLessThan());
68 [ + + - + ]: 1063941 : if (p == terms.end() || InMemoryTermEntryLessThan()(post, *p)) {
[ + + ]
69 : 795861 : terms.insert(p, post);
70 : : } else {
71 : 268080 : (*p).merge(post);
72 : : }
73 : 1063941 : }
74 : :
75 : : //////////////
76 : : // Postlist //
77 : : //////////////
78 : :
79 : 47143 : InMemoryPostList::InMemoryPostList(Xapian::Internal::RefCntPtr<const InMemoryDatabase> db_,
80 : : const InMemoryTerm & imterm,
81 : : const std::string & term_)
82 : : : LeafPostList(term_),
83 : : pos(imterm.docs.begin()),
84 : : end(imterm.docs.end()),
85 : : termfreq(imterm.term_freq),
86 : : started(false),
87 : 47143 : db(db_)
88 : : {
89 [ + + ][ + + ]: 47144 : while (pos != end && !pos->valid) ++pos;
[ + + ][ # # ]
[ # # ][ # # ]
90 : 47143 : }
91 : :
92 : : Xapian::doccount
93 : 188460 : InMemoryPostList::get_termfreq() const
94 : : {
95 : 188460 : return termfreq;
96 : : }
97 : :
98 : : Xapian::docid
99 : 18012647 : InMemoryPostList::get_docid() const
100 : : {
101 [ + + ]: 18012647 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
102 : : Assert(started);
103 : : Assert(!at_end());
104 : 18012646 : return (*pos).did;
105 : : }
106 : :
107 : : PostList *
108 : 9470254 : InMemoryPostList::next(Xapian::weight /*w_min*/)
109 : : {
110 [ + + ]: 9470254 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
111 [ + + ]: 9470253 : if (started) {
112 : : Assert(!at_end());
113 : 9423739 : ++pos;
114 [ + + ][ + + ]: 9423745 : while (pos != end && !pos->valid) ++pos;
[ + + ]
115 : : } else {
116 : 46514 : started = true;
117 : : }
118 : 9470253 : return NULL;
119 : : }
120 : :
121 : : PostList *
122 : 631 : InMemoryPostList::skip_to(Xapian::docid did, Xapian::weight w_min)
123 : : {
124 [ - + ]: 631 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
125 : : // FIXME - see if we can make more efficient, perhaps using better
126 : : // data structure. Note, though, that a binary search of
127 : : // the remaining list may NOT be a good idea (search time is then
128 : : // O(log {length of list}), as opposed to O(distance we want to skip)
129 : : // Since we will frequently only be skipping a short distance, this
130 : : // could well be worse.
131 : 631 : started = true;
132 : : Assert(!at_end());
133 [ + + ][ + + ]: 1952 : while (!at_end() && (*pos).did < did) {
[ + + ]
134 : 1321 : (void) next(w_min);
135 : : }
136 : 631 : return NULL;
137 : : }
138 : :
139 : : bool
140 : 9494544 : InMemoryPostList::at_end() const
141 : : {
142 [ - + ]: 9494544 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
143 : 9494544 : return (pos == end);
144 : : }
145 : :
146 : : string
147 : 3 : InMemoryPostList::get_description() const
148 : : {
149 : 3 : return "InMemoryPostList " + str(termfreq);
150 : : }
151 : :
152 : : Xapian::termcount
153 : 9420556 : InMemoryPostList::get_doclength() const
154 : : {
155 [ + + ]: 9420556 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
156 : 9420555 : return db->get_doclength(get_docid());
157 : : }
158 : :
159 : : PositionList *
160 : 570 : InMemoryPostList::read_position_list()
161 : : {
162 [ - + ]: 570 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
163 : 570 : mypositions.set_data(pos->positions);
164 : 570 : return &mypositions;
165 : : }
166 : :
167 : : PositionList *
168 : 55 : InMemoryPostList::open_position_list() const
169 : : {
170 [ - + ]: 55 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
171 : 55 : return new InMemoryPositionList(pos->positions);
172 : : }
173 : :
174 : : Xapian::termcount
175 : 9422216 : InMemoryPostList::get_wdf() const
176 : : {
177 [ - + ]: 9422216 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
178 : 9422216 : return (*pos).wdf;
179 : : }
180 : :
181 : : //////////////
182 : : // Termlist //
183 : : //////////////
184 : :
185 : 398 : InMemoryTermList::InMemoryTermList(Xapian::Internal::RefCntPtr<const InMemoryDatabase> db_,
186 : : Xapian::docid did_,
187 : : const InMemoryDoc & doc,
188 : : Xapian::termcount len)
189 : : : pos(doc.terms.begin()), end(doc.terms.end()), terms(doc.terms.size()),
190 : 398 : started(false), db(db_), did(did_), document_length(len)
191 : : {
192 : : LOGLINE(DB, "InMemoryTermList::InMemoryTermList(): " <<
193 : : terms << " terms starting from " << pos->tname);
194 : 398 : }
195 : :
196 : : Xapian::termcount
197 : 1188 : InMemoryTermList::get_wdf() const
198 : : {
199 [ - + ]: 1188 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
200 : : Assert(started);
201 : : Assert(!at_end());
202 : 1188 : return (*pos).wdf;
203 : : }
204 : :
205 : : Xapian::doccount
206 : 912 : InMemoryTermList::get_termfreq() const
207 : : {
208 [ - + ]: 912 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
209 : : Assert(started);
210 : : Assert(!at_end());
211 : :
212 : 912 : return db->get_termfreq((*pos).tname);
213 : : }
214 : :
215 : : Xapian::termcount
216 : 26 : InMemoryTermList::get_approx_size() const
217 : : {
218 [ - + ]: 26 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
219 : 26 : return terms;
220 : : }
221 : :
222 : : void
223 : 794 : InMemoryTermList::accumulate_stats(Xapian::Internal::ExpandStats & stats) const
224 : : {
225 [ - + ]: 794 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
226 : : Assert(started);
227 : : Assert(!at_end());
228 : : stats.accumulate(InMemoryTermList::get_wdf(), document_length,
229 : : InMemoryTermList::get_termfreq(),
230 : 794 : db->get_doccount());
231 : 794 : }
232 : :
233 : : string
234 : 2231 : InMemoryTermList::get_termname() const
235 : : {
236 [ - + ]: 2231 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
237 : : Assert(started);
238 : : Assert(!at_end());
239 : 2231 : return (*pos).tname;
240 : : }
241 : :
242 : : TermList *
243 : 2027 : InMemoryTermList::next()
244 : : {
245 [ - + ]: 2027 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
246 [ + + ]: 2027 : if (started) {
247 : : Assert(!at_end());
248 : 1635 : pos++;
249 : : } else {
250 : 392 : started = true;
251 : : }
252 : 2027 : return NULL;
253 : : }
254 : :
255 : : TermList *
256 : 13 : InMemoryTermList::skip_to(const string & term)
257 : : {
258 [ - + ]: 13 : if (rare(db->is_closed()))
259 : 0 : InMemoryDatabase::throw_database_closed();
260 : :
261 [ + + ][ + + ]: 130 : while (pos != end && pos->tname < term) {
[ + + ]
262 : 117 : ++pos;
263 : : }
264 : :
265 : 13 : started = true;
266 : 13 : return NULL;
267 : : }
268 : :
269 : : bool
270 : 2053 : InMemoryTermList::at_end() const
271 : : {
272 [ - + ]: 2053 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
273 : : Assert(started);
274 : 2053 : return (pos == end);
275 : : }
276 : :
277 : : Xapian::termcount
278 : 57 : InMemoryTermList::positionlist_count() const
279 : : {
280 [ - + ]: 57 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
281 : 57 : return db->positionlist_count(did, (*pos).tname);
282 : : }
283 : :
284 : : Xapian::PositionIterator
285 : 279 : InMemoryTermList::positionlist_begin() const
286 : : {
287 [ - + ]: 279 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
288 : 279 : return Xapian::PositionIterator(db->open_position_list(did, (*pos).tname));
289 : : }
290 : :
291 : : /////////////////////////////
292 : : // InMemoryAllDocsPostList //
293 : : /////////////////////////////
294 : :
295 : 42 : InMemoryAllDocsPostList::InMemoryAllDocsPostList(Xapian::Internal::RefCntPtr<const InMemoryDatabase> db_)
296 : 42 : : LeafPostList(std::string()), did(0), db(db_)
297 : : {
298 : 42 : }
299 : :
300 : : Xapian::doccount
301 : 14 : InMemoryAllDocsPostList::get_termfreq() const
302 : : {
303 [ - + ]: 14 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
304 : 14 : return db->totdocs;
305 : : }
306 : :
307 : : Xapian::docid
308 : 6278 : InMemoryAllDocsPostList::get_docid() const
309 : : {
310 [ - + ]: 6278 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
311 : : Assert(did > 0);
312 : : Assert(did <= db->termlists.size());
313 : : Assert(db->termlists[did - 1].is_valid);
314 : 6278 : return did;
315 : : }
316 : :
317 : : Xapian::termcount
318 : 7 : InMemoryAllDocsPostList::get_doclength() const
319 : : {
320 [ - + ]: 7 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
321 : 7 : return db->get_doclength(did);
322 : : }
323 : :
324 : : Xapian::termcount
325 : 26 : InMemoryAllDocsPostList::get_wdf() const
326 : : {
327 : 26 : return 1;
328 : : }
329 : :
330 : : PositionList *
331 : 0 : InMemoryAllDocsPostList::read_position_list()
332 : : {
333 : 0 : throw Xapian::UnimplementedError("Can't open position list for all docs iterator");
334 : : }
335 : :
336 : : PositionList *
337 : 0 : InMemoryAllDocsPostList::open_position_list() const
338 : : {
339 : 0 : throw Xapian::UnimplementedError("Can't open position list for all docs iterator");
340 : : }
341 : :
342 : : PostList *
343 : 3779 : InMemoryAllDocsPostList::next(Xapian::weight /*w_min*/)
344 : : {
345 [ - + ]: 3779 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
346 : : Assert(!at_end());
347 [ + + ][ + + ]: 35583 : do {
[ + + ]
348 : 35583 : ++did;
349 : : } while (did <= db->termlists.size() && !db->termlists[did - 1].is_valid);
350 : 3779 : return NULL;
351 : : }
352 : :
353 : : PostList *
354 : 5 : InMemoryAllDocsPostList::skip_to(Xapian::docid did_, Xapian::weight /*w_min*/)
355 : : {
356 [ - + ]: 5 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
357 : : Assert(!at_end());
358 [ + - ]: 5 : if (did <= did_) {
359 : 5 : did = did_;
360 [ + - ][ + + ]: 6 : while (did <= db->termlists.size() && !db->termlists[did - 1].is_valid) {
[ + + ]
361 : 1 : ++did;
362 : : }
363 : : }
364 : 5 : return NULL;
365 : : }
366 : :
367 : : bool
368 : 3785 : InMemoryAllDocsPostList::at_end() const
369 : : {
370 [ - + ]: 3785 : if (db->is_closed()) InMemoryDatabase::throw_database_closed();
371 : 3785 : return (did > db->termlists.size());
372 : : }
373 : :
374 : : string
375 : 0 : InMemoryAllDocsPostList::get_description() const
376 : : {
377 : 0 : return "InMemoryAllDocsPostList " + str(did);
378 : : }
379 : :
380 : : ///////////////////////////
381 : : // Actual database class //
382 : : ///////////////////////////
383 : :
384 : 266 : InMemoryDatabase::InMemoryDatabase()
385 : 266 : : totdocs(0), totlen(0), positions_present(false), closed(false)
386 : : {
387 : : // Updates are applied immediately so we can't support transactions.
388 : 266 : transaction_state = TRANSACTION_UNIMPLEMENTED;
389 : :
390 : : // We keep an empty entry in postlists for convenience of implementing
391 : : // allterms iteration and returning a PostList for an absent term.
392 : 266 : postlists.insert(make_pair(string(), InMemoryTerm()));
393 : 266 : }
394 : :
395 : 266 : InMemoryDatabase::~InMemoryDatabase()
396 : : {
397 : 266 : dtor_called();
398 [ + - ][ # # ]: 266 : }
[ # # ]
399 : :
400 : : void
401 : 20 : InMemoryDatabase::reopen()
402 : : {
403 [ + + ]: 20 : if (closed) InMemoryDatabase::throw_database_closed();
404 : 18 : }
405 : :
406 : : void
407 : 3 : InMemoryDatabase::close()
408 : : {
409 : : // Free all the resources, and mark the db as closed.
410 : 3 : postlists.clear();
411 : 3 : termlists.clear();
412 : 3 : doclists.clear();
413 : 3 : valuelists.clear();
414 : 3 : valuestats.clear();
415 : 3 : doclengths.clear();
416 : 3 : metadata.clear();
417 : 3 : closed = true;
418 : 3 : }
419 : :
420 : : LeafPostList *
421 : 47187 : InMemoryDatabase::open_post_list(const string & tname) const
422 : : {
423 [ + + ]: 47187 : if (closed) InMemoryDatabase::throw_database_closed();
424 [ + + ]: 47185 : if (tname.empty()) {
425 : 42 : Xapian::Internal::RefCntPtr<const InMemoryDatabase> ptrtothis(this);
426 : 42 : return new InMemoryAllDocsPostList(ptrtothis);
427 : : }
428 : 47143 : map<string, InMemoryTerm>::const_iterator i = postlists.find(tname);
429 [ + + + + ]: 47143 : if (i == postlists.end() || i->second.term_freq == 0) {
[ + + ]
430 : 171 : i = postlists.begin();
431 : : // Check that our dummy entry for string() is present.
432 : : Assert(i->first.empty());
433 : : }
434 : 47143 : Xapian::Internal::RefCntPtr<const InMemoryDatabase> ptrtothis(this);
435 : 47185 : return new InMemoryPostList(ptrtothis, i->second, tname);
436 : : }
437 : :
438 : : bool
439 : 9787910 : InMemoryDatabase::doc_exists(Xapian::docid did) const
440 : : {
441 [ - + ]: 9787910 : if (closed) InMemoryDatabase::throw_database_closed();
442 [ + - ][ + + ]: 9787910 : return (did > 0 && did <= termlists.size() && termlists[did - 1].is_valid);
[ + + ]
443 : : }
444 : :
445 : : Xapian::doccount
446 : 48167 : InMemoryDatabase::get_termfreq(const string & tname) const
447 : : {
448 [ - + ]: 48167 : if (closed) InMemoryDatabase::throw_database_closed();
449 : 48167 : map<string, InMemoryTerm>::const_iterator i = postlists.find(tname);
450 [ + + ]: 48167 : if (i == postlists.end()) return 0;
451 : 48167 : return i->second.term_freq;
452 : : }
453 : :
454 : : Xapian::termcount
455 : 46920 : InMemoryDatabase::get_collection_freq(const string &tname) const
456 : : {
457 [ - + ]: 46920 : if (closed) InMemoryDatabase::throw_database_closed();
458 : 46920 : map<string, InMemoryTerm>::const_iterator i = postlists.find(tname);
459 [ + + ]: 46920 : if (i == postlists.end()) return 0;
460 : 46920 : return i->second.collection_freq;
461 : : }
462 : :
463 : : Xapian::doccount
464 : 58 : InMemoryDatabase::get_value_freq(Xapian::valueno slot) const
465 : : {
466 [ - + ]: 58 : if (closed) InMemoryDatabase::throw_database_closed();
467 : 58 : map<Xapian::valueno, ValueStats>::const_iterator i = valuestats.find(slot);
468 [ + + ]: 58 : if (i == valuestats.end()) return 0;
469 : 58 : return i->second.freq;
470 : : }
471 : :
472 : : std::string
473 : 3916 : InMemoryDatabase::get_value_lower_bound(Xapian::valueno slot) const
474 : : {
475 [ - + ]: 3916 : if (closed) InMemoryDatabase::throw_database_closed();
476 : 3916 : map<Xapian::valueno, ValueStats>::const_iterator i = valuestats.find(slot);
477 [ + + ]: 3916 : if (i == valuestats.end()) return string();
478 : 3916 : return i->second.lower_bound;
479 : : }
480 : :
481 : : std::string
482 : 3921 : InMemoryDatabase::get_value_upper_bound(Xapian::valueno slot) const
483 : : {
484 [ - + ]: 3921 : if (closed) InMemoryDatabase::throw_database_closed();
485 : 3921 : map<Xapian::valueno, ValueStats>::const_iterator i = valuestats.find(slot);
486 [ + + ]: 3921 : if (i == valuestats.end()) return string();
487 : 3921 : return i->second.upper_bound;
488 : : }
489 : :
490 : : Xapian::doccount
491 : 87903 : InMemoryDatabase::get_doccount() const
492 : : {
493 [ + + ]: 87903 : if (closed) InMemoryDatabase::throw_database_closed();
494 : 87902 : return totdocs;
495 : : }
496 : :
497 : : Xapian::docid
498 : 4608 : InMemoryDatabase::get_lastdocid() const
499 : : {
500 [ - + ]: 4608 : if (closed) InMemoryDatabase::throw_database_closed();
501 : 4608 : return termlists.size();
502 : : }
503 : :
504 : : totlen_t
505 : 27694 : InMemoryDatabase::get_total_length() const
506 : : {
507 : 27694 : return totlen;
508 : : }
509 : :
510 : : Xapian::doclength
511 : 45 : InMemoryDatabase::get_avlength() const
512 : : {
513 [ - + ]: 45 : if (closed) InMemoryDatabase::throw_database_closed();
514 [ + + ]: 45 : if (totdocs == 0) return 0;
515 : 45 : return Xapian::doclength(totlen) / totdocs;
516 : : }
517 : :
518 : : Xapian::termcount
519 : 9423598 : InMemoryDatabase::get_doclength(Xapian::docid did) const
520 : : {
521 [ - + ]: 9423598 : if (closed) InMemoryDatabase::throw_database_closed();
522 [ + + ]: 9423598 : if (!doc_exists(did)) {
523 : : throw Xapian::DocNotFoundError(string("Docid ") + str(did) +
524 : 3007 : string(" not found"));
525 : : }
526 : 9420591 : return doclengths[did - 1];
527 : : }
528 : :
529 : : TermList *
530 : 3408 : InMemoryDatabase::open_term_list(Xapian::docid did) const
531 : : {
532 [ + + ]: 3408 : if (closed) InMemoryDatabase::throw_database_closed();
533 : : Assert(did != 0);
534 [ + + ]: 3407 : if (!doc_exists(did)) {
535 : : // FIXME: the docid in this message will be local, not global
536 : : throw Xapian::DocNotFoundError(string("Docid ") + str(did) +
537 : 3009 : string(" not found"));
538 : : }
539 : : return new InMemoryTermList(Xapian::Internal::RefCntPtr<const InMemoryDatabase>(this), did,
540 : 398 : termlists[did - 1], doclengths[did - 1]);
541 : : }
542 : :
543 : : Xapian::Document::Internal *
544 : 354450 : InMemoryDatabase::open_document(Xapian::docid did, bool lazy) const
545 : : {
546 [ + + ]: 354450 : if (closed) InMemoryDatabase::throw_database_closed();
547 : : Assert(did != 0);
548 [ + + ]: 354448 : if (!doc_exists(did)) {
549 [ + + ]: 3018 : if (lazy) return NULL;
550 : : // FIXME: the docid in this message will be local, not global
551 : : throw Xapian::DocNotFoundError(string("Docid ") + str(did) +
552 : 3014 : string(" not found"));
553 : : }
554 : 351434 : return new InMemoryDocument(this, did);
555 : : }
556 : :
557 : : std::string
558 : 11 : InMemoryDatabase::get_metadata(const std::string & key) const
559 : : {
560 [ - + ]: 11 : if (closed) InMemoryDatabase::throw_database_closed();
561 : 11 : map<string, string>::const_iterator i = metadata.find(key);
562 [ + + ]: 11 : if (i == metadata.end())
563 : 2 : return string();
564 : 11 : return i->second;
565 : : }
566 : :
567 : : TermList *
568 : 1 : InMemoryDatabase::open_metadata_keylist(const string &) const
569 : : {
570 [ + - ]: 1 : if (metadata.empty()) return NULL;
571 : : // FIXME: nobody implemented this yet...
572 : 0 : throw Xapian::UnimplementedError("InMemory backend doesn't currently implement Database::metadata_keys_begin()");
573 : : }
574 : :
575 : : void
576 : 8 : InMemoryDatabase::set_metadata(const std::string & key,
577 : : const std::string & value)
578 : : {
579 [ - + ]: 8 : if (closed) InMemoryDatabase::throw_database_closed();
580 [ + + ]: 8 : if (!value.empty()) {
581 : 7 : metadata[key] = value;
582 : : } else {
583 : 1 : metadata.erase(key);
584 : : }
585 : 8 : }
586 : :
587 : : Xapian::termcount
588 : 57 : InMemoryDatabase::positionlist_count(Xapian::docid did,
589 : : const string & tname) const
590 : : {
591 [ - + ]: 57 : if (closed) InMemoryDatabase::throw_database_closed();
592 [ - + ]: 57 : if (!doc_exists(did)) {
593 : 0 : return 0;
594 : : }
595 : 57 : const InMemoryDoc &doc = termlists[did-1];
596 : :
597 : 57 : vector<InMemoryTermEntry>::const_iterator i;
598 [ + - ]: 114 : for (i = doc.terms.begin(); i != doc.terms.end(); ++i) {
599 [ + + ]: 114 : if (i->tname == tname) {
600 : 57 : return i->positions.size();
601 : : }
602 : : }
603 : 57 : return 0;
604 : : }
605 : :
606 : : PositionList *
607 : 297 : InMemoryDatabase::open_position_list(Xapian::docid did,
608 : : const string & tname) const
609 : : {
610 [ - + ]: 297 : if (closed) InMemoryDatabase::throw_database_closed();
611 [ + + ]: 297 : if (usual(doc_exists(did))) {
612 : 295 : const InMemoryDoc &doc = termlists[did-1];
613 : :
614 : 295 : vector<InMemoryTermEntry>::const_iterator i;
615 [ + + ]: 578 : for (i = doc.terms.begin(); i != doc.terms.end(); ++i) {
616 [ + + ]: 575 : if (i->tname == tname) {
617 : 292 : return new InMemoryPositionList(i->positions);
618 : : }
619 : : }
620 : : }
621 : 297 : return new InMemoryPositionList(false);
622 : : }
623 : :
624 : : void
625 : 21461 : InMemoryDatabase::add_values(Xapian::docid did,
626 : : const map<Xapian::valueno, string> &values_)
627 : : {
628 [ - + ]: 21461 : if (closed) InMemoryDatabase::throw_database_closed();
629 [ + + ]: 21461 : if (did > valuelists.size()) {
630 : 18385 : valuelists.resize(did);
631 : : }
632 : 21461 : valuelists[did-1] = values_;
633 : :
634 : : // Update the statistics.
635 : 21461 : map<Xapian::valueno, string>::const_iterator j;
636 [ + + ]: 229320 : for (j = values_.begin(); j != values_.end(); ++j) {
637 : 207859 : std::pair<map<Xapian::valueno, ValueStats>::iterator, bool> i;
638 [ - + ]: 207859 : i = valuestats.insert(make_pair(j->first, ValueStats()));
639 : :
640 : : // Now, modify the stored statistics.
641 [ + + ]: 207859 : if ((i.first->second.freq)++ == 0) {
642 : : // If the value count was previously zero, set the upper and lower
643 : : // bounds to the newly added value.
644 : 57658 : i.first->second.lower_bound = j->second;
645 : 57658 : i.first->second.upper_bound = j->second;
646 : : } else {
647 : : // Otherwise, simply make sure they reflect the new value.
648 [ + + ]: 150201 : if (j->second < i.first->second.lower_bound) {
649 : 2809 : i.first->second.lower_bound = j->second;
650 : : }
651 [ + + ]: 150201 : if (j->second > i.first->second.upper_bound) {
652 : 2972 : i.first->second.upper_bound = j->second;
653 : : }
654 : : }
655 : : }
656 : 21461 : }
657 : :
658 : : // We implicitly commit each modification right away, so nothing to do here.
659 : : void
660 : 64 : InMemoryDatabase::commit()
661 : : {
662 : 64 : }
663 : :
664 : : // We implicitly commit each modification right away, so nothing to do here.
665 : : void
666 : 0 : InMemoryDatabase::cancel()
667 : : {
668 : 0 : }
669 : :
670 : : void
671 : 3027 : InMemoryDatabase::delete_document(Xapian::docid did)
672 : : {
673 [ - + ]: 3027 : if (closed) InMemoryDatabase::throw_database_closed();
674 [ + + ]: 3027 : if (!doc_exists(did)) {
675 : : throw Xapian::DocNotFoundError(string("Docid ") + str(did) +
676 : 1 : string(" not found"));
677 : : }
678 : 3026 : termlists[did-1].is_valid = false;
679 : 3026 : doclists[did-1] = "";
680 : 3026 : map<Xapian::valueno, string>::const_iterator j;
681 [ + + ]: 3128 : for (j = valuelists[did-1].begin(); j != valuelists[did-1].end(); ++j) {
682 : 102 : map<Xapian::valueno, ValueStats>::iterator i;
683 : 102 : i = valuestats.find(j->first);
684 [ + + ]: 102 : if (--(i->second.freq) == 0) {
685 : 16 : i->second.lower_bound.resize(0);
686 : 16 : i->second.upper_bound.resize(0);
687 : : }
688 : : }
689 : 3026 : valuelists[did-1].clear();
690 : :
691 : 3026 : totlen -= doclengths[did-1];
692 : 3026 : doclengths[did-1] = 0;
693 : 3026 : totdocs--;
694 : : // A crude check, but it's hard to be more precise with the current
695 : : // InMemory structure without being very inefficient.
696 [ + + ]: 3026 : if (totdocs == 0) positions_present = false;
697 : :
698 : 3026 : vector<InMemoryTermEntry>::const_iterator i;
699 [ + + ]: 9102 : for (i = termlists[did - 1].terms.begin();
700 : : i != termlists[did - 1].terms.end();
701 : : ++i) {
702 : 6076 : map<string, InMemoryTerm>::iterator t = postlists.find(i->tname);
703 : : Assert(t != postlists.end());
704 : 6076 : t->second.collection_freq -= i->wdf;
705 : 6076 : --t->second.term_freq;
706 : 6076 : vector<InMemoryPosting>::iterator posting = t->second.docs.begin();
707 [ + + ]: 18018540 : while (posting != t->second.docs.end()) {
708 : : // Just zero out erased doc ids - otherwise we need to erase
709 : : // in a vector (inefficient) and we break any posting lists
710 : : // iterating over this posting list.
711 [ + + ]: 18012464 : if (posting->did == did) posting->valid = false;
712 : 18012464 : ++posting;
713 : : }
714 : : }
715 : 3026 : termlists[did-1].terms.clear();
716 : 3026 : }
717 : :
718 : : void
719 : 3076 : InMemoryDatabase::replace_document(Xapian::docid did,
720 : : const Xapian::Document & document)
721 : : {
722 : : LOGCALL_VOID(DB, "InMemoryDatabase::replace_document", did | document);
723 : :
724 [ - + ]: 3076 : if (closed) InMemoryDatabase::throw_database_closed();
725 : :
726 [ + + ]: 3076 : if (doc_exists(did)) {
727 : 3059 : map<Xapian::valueno, string>::const_iterator j;
728 [ + + ]: 5663 : for (j = valuelists[did-1].begin(); j != valuelists[did-1].end(); ++j) {
729 : 2604 : map<Xapian::valueno, ValueStats>::iterator i;
730 : 2604 : i = valuestats.find(j->first);
731 [ + + ]: 2604 : if (--(i->second.freq) == 0) {
732 : 2 : i->second.lower_bound.resize(0);
733 : 2 : i->second.upper_bound.resize(0);
734 : : }
735 : : }
736 : :
737 : 3059 : totlen -= doclengths[did - 1];
738 : 3059 : totdocs--;
739 [ + + ]: 17 : } else if (did > termlists.size()) {
740 : 12 : termlists.resize(did);
741 : 12 : termlists[did - 1].is_valid = true;
742 : 12 : doclengths.resize(did);
743 : 12 : doclists.resize(did);
744 : 12 : valuelists.resize(did);
745 : : } else {
746 : 5 : termlists[did - 1].is_valid = true;
747 : : }
748 : :
749 : 3076 : vector<InMemoryTermEntry>::const_iterator i;
750 [ + + ]: 3239 : for (i = termlists[did - 1].terms.begin();
751 : : i != termlists[did - 1].terms.end();
752 : : ++i) {
753 : 163 : map<string, InMemoryTerm>::iterator t = postlists.find(i->tname);
754 : : Assert(t != postlists.end());
755 : 163 : t->second.collection_freq -= i->wdf;
756 : 163 : --t->second.term_freq;
757 : 163 : vector<InMemoryPosting>::iterator posting = t->second.docs.begin();
758 [ + + ]: 716 : while (posting != t->second.docs.end()) {
759 : : // Just invalidate erased doc ids - otherwise we need to erase
760 : : // in a vector (inefficient) and we break any posting lists
761 : : // iterating over this posting list.
762 [ + + ]: 553 : if (posting->did == did) posting->valid = false;
763 : 553 : ++posting;
764 : : }
765 : : }
766 : :
767 : 3076 : doclengths[did - 1] = 0;
768 : 3076 : doclists[did - 1] = document.get_data();
769 : :
770 : 3076 : finish_add_doc(did, document);
771 : 3076 : }
772 : :
773 : : Xapian::docid
774 : 18385 : InMemoryDatabase::add_document(const Xapian::Document & document)
775 : : {
776 : : LOGCALL(DB, Xapian::docid, "InMemoryDatabase::add_document", document);
777 [ - + ]: 18385 : if (closed) InMemoryDatabase::throw_database_closed();
778 : :
779 : 18385 : Xapian::docid did = make_doc(document.get_data());
780 : :
781 : 18385 : finish_add_doc(did, document);
782 : :
783 : 18385 : RETURN(did);
784 : : }
785 : :
786 : : void
787 : 21461 : InMemoryDatabase::finish_add_doc(Xapian::docid did, const Xapian::Document &document)
788 : : {
789 : : {
790 : 21461 : map<Xapian::valueno, string> values;
791 : 21461 : Xapian::ValueIterator k = document.values_begin();
792 [ + + ]: 229320 : for ( ; k != document.values_end(); ++k) {
793 : 207859 : values.insert(make_pair(k.get_valueno(), *k));
794 : : LOGLINE(DB, "InMemoryDatabase::finish_add_doc(): adding value " <<
795 : : k.get_valueno() << " -> " << *k);
796 : : }
797 : 21461 : add_values(did, values);
798 : : }
799 : :
800 : 21461 : InMemoryDoc doc(true);
801 : 21461 : Xapian::TermIterator i = document.termlist_begin();
802 : 21461 : Xapian::TermIterator i_end = document.termlist_end();
803 [ + + ]: 817322 : for ( ; i != i_end; ++i) {
804 : 795861 : make_term(*i);
805 : :
806 : : LOGLINE(DB, "InMemoryDatabase::finish_add_doc(): adding term " << *i);
807 : 795861 : Xapian::PositionIterator j = i.positionlist_begin();
808 : 795861 : Xapian::PositionIterator j_end = i.positionlist_end();
809 : :
810 [ + + ]: 795861 : if (j == j_end) {
811 : : /* Make sure the posting exists, even without a position. */
812 : 834 : make_posting(&doc, *i, did, 0, i.get_wdf(), false);
813 : : } else {
814 : 795027 : positions_present = true;
815 [ + + ]: 1858134 : for ( ; j != j_end; ++j) {
816 : 1063107 : make_posting(&doc, *i, did, *j, i.get_wdf());
817 : : }
818 : : }
819 : :
820 : : Assert(did > 0 && did <= doclengths.size());
821 : 795861 : doclengths[did - 1] += i.get_wdf();
822 : 795861 : totlen += i.get_wdf();
823 : 795861 : postlists[*i].collection_freq += i.get_wdf();
824 : 795861 : ++postlists[*i].term_freq;
825 : : }
826 : 21461 : swap(termlists[did - 1], doc);
827 : :
828 : 21461 : totdocs++;
829 : 21461 : }
830 : :
831 : : void
832 : 795861 : InMemoryDatabase::make_term(const string & tname)
833 : : {
834 : 795861 : postlists[tname]; // Initialise, if not already there.
835 : 795861 : }
836 : :
837 : : Xapian::docid
838 : 18385 : InMemoryDatabase::make_doc(const string & docdata)
839 : : {
840 : 18385 : termlists.push_back(InMemoryDoc(true));
841 : 18385 : doclengths.push_back(0);
842 : 18385 : doclists.push_back(docdata);
843 : :
844 : : AssertEqParanoid(termlists.size(), doclengths.size());
845 : :
846 : 18385 : return termlists.size();
847 : : }
848 : :
849 : 1063941 : void InMemoryDatabase::make_posting(InMemoryDoc * doc,
850 : : const string & tname,
851 : : Xapian::docid did,
852 : : Xapian::termpos position,
853 : : Xapian::termcount wdf,
854 : : bool use_position)
855 : : {
856 : : Assert(doc);
857 : : Assert(postlists.find(tname) != postlists.end());
858 : : Assert(did > 0 && did <= termlists.size());
859 : : Assert(did > 0 && did <= doclengths.size());
860 : : Assert(doc_exists(did));
861 : :
862 : : // Make the posting
863 : 1063941 : InMemoryPosting posting;
864 : 1063941 : posting.did = did;
865 [ + + ]: 1063941 : if (use_position) {
866 : 1063107 : posting.positions.push_back(position);
867 : : }
868 : 1063941 : posting.wdf = wdf;
869 : 1063941 : posting.valid = true;
870 : :
871 : : // Now record the posting
872 : 1063941 : postlists[tname].add_posting(posting);
873 : :
874 : : // Make the termentry
875 : 1063941 : InMemoryTermEntry termentry;
876 : 1063941 : termentry.tname = tname;
877 [ + + ]: 1063941 : if (use_position) {
878 : 1063107 : termentry.positions.push_back(position);
879 : : }
880 : 1063941 : termentry.wdf = wdf;
881 : :
882 : : // Now record the termentry
883 : 1063941 : doc->add_posting(termentry);
884 : 1063941 : }
885 : :
886 : : bool
887 : 67 : InMemoryDatabase::term_exists(const string & tname) const
888 : : {
889 [ - + ]: 67 : if (closed) InMemoryDatabase::throw_database_closed();
890 : : Assert(!tname.empty());
891 : 67 : map<string, InMemoryTerm>::const_iterator i = postlists.find(tname);
892 [ + + ]: 67 : if (i == postlists.end()) return false;
893 : 67 : return (i->second.term_freq != 0);
894 : : }
895 : :
896 : : bool
897 : 73 : InMemoryDatabase::has_positions() const
898 : : {
899 [ + + ]: 73 : if (closed) InMemoryDatabase::throw_database_closed();
900 : 72 : return positions_present;
901 : : }
902 : :
903 : : TermList *
904 : 128 : InMemoryDatabase::open_allterms(const string & prefix) const
905 : : {
906 [ - + ]: 128 : if (closed) InMemoryDatabase::throw_database_closed();
907 : : return new InMemoryAllTermsList(&postlists,
908 : : Xapian::Internal::RefCntPtr<const InMemoryDatabase>(this),
909 : 128 : prefix);
910 : : }
911 : :
912 : : void
913 : 13 : InMemoryDatabase::throw_database_closed()
914 : : {
915 : 13 : throw Xapian::DatabaseError("Database has been closed");
916 : : }
|