1 /* inmemory_database.cc
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2014,2017 Olly Betts
6 * Copyright 2006,2009 Lemur Consulting Ltd
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.
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.
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
26 #include "inmemory_database.h"
30 #include "expand/expandweight.h"
31 #include "inmemory_document.h"
32 #include "inmemory_alltermslist.h"
34 #include "backends/valuestats.h"
41 #include <xapian/error.h>
42 #include <xapian/valueiterator.h>
45 using Xapian::Internal::intrusive_ptr
;
48 InMemoryTerm::add_posting(InMemoryPosting
&& post
)
50 // Add document to right place in list
51 vector
<InMemoryPosting
>::iterator p
;
52 p
= lower_bound(docs
.begin(), docs
.end(),
53 post
, InMemoryPostingLessThan());
54 if (p
== docs
.end() || InMemoryPostingLessThan()(post
, *p
)) {
55 docs
.insert(p
, std::move(post
));
56 } else if (!p
->valid
) {
64 InMemoryDoc::add_posting(InMemoryTermEntry
&& post
)
66 // Add document to right place in list
67 vector
<InMemoryTermEntry
>::iterator p
;
68 p
= lower_bound(terms
.begin(), terms
.end(),
69 post
, InMemoryTermEntryLessThan());
70 if (p
== terms
.end() || InMemoryTermEntryLessThan()(post
, *p
)) {
71 terms
.insert(p
, std::move(post
));
81 InMemoryPostList::InMemoryPostList(intrusive_ptr
<const InMemoryDatabase
> db_
,
82 const InMemoryTerm
& imterm
,
83 const std::string
& term_
)
84 : LeafPostList(term_
),
85 pos(imterm
.docs
.begin()),
86 end(imterm
.docs
.end()),
87 termfreq(imterm
.term_freq
),
91 while (pos
!= end
&& !pos
->valid
) ++pos
;
95 InMemoryPostList::get_termfreq() const
101 InMemoryPostList::get_docid() const
103 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
110 InMemoryPostList::next(double /*w_min*/)
112 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
116 while (pos
!= end
&& !pos
->valid
) ++pos
;
124 InMemoryPostList::skip_to(Xapian::docid did
, double w_min
)
126 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
127 // FIXME - see if we can make more efficient, perhaps using better
128 // data structure. Note, though, that a binary search of
129 // the remaining list may NOT be a good idea (search time is then
130 // O(log {length of list}), as opposed to O(distance we want to skip)
131 // Since we will frequently only be skipping a short distance, this
132 // could well be worse.
134 // If we've not started, it's OK to call skip_to().
135 Assert(!at_end() || !started
);
137 while (!at_end() && (*pos
).did
< did
) {
144 InMemoryPostList::at_end() const
146 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
151 InMemoryPostList::get_description() const
153 return "InMemoryPostList " + str(termfreq
);
157 InMemoryPostList::read_position_list()
159 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
160 mypositions
.assign(pos
->positions
.copy());
165 InMemoryPostList::open_position_list() const
167 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
168 return new InMemoryPositionList(pos
->positions
.copy());
172 InMemoryPostList::get_wdf() const
174 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
182 InMemoryTermList::InMemoryTermList(intrusive_ptr
<const InMemoryDatabase
> db_
,
184 const InMemoryDoc
& doc
,
185 Xapian::termcount len
)
186 : pos(doc
.terms
.begin()), end(doc
.terms
.end()), terms(doc
.terms
.size()),
187 started(false), db(db_
), did(did_
), document_length(len
)
189 LOGLINE(DB
, "InMemoryTermList::InMemoryTermList(): " <<
190 terms
<< " terms starting from " << pos
->tname
);
194 InMemoryTermList::get_wdf() const
196 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
203 InMemoryTermList::get_termfreq() const
205 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
210 db
->get_freqs((*pos
).tname
, &tf
, NULL
);
215 InMemoryTermList::get_approx_size() const
217 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
222 InMemoryTermList::accumulate_stats(Xapian::Internal::ExpandStats
& stats
) const
224 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
227 stats
.accumulate(InMemoryTermList::get_wdf(), document_length
,
228 InMemoryTermList::get_termfreq(),
233 InMemoryTermList::get_termname() const
235 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
242 InMemoryTermList::next()
244 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
255 InMemoryTermList::skip_to(const string
& term
)
257 if (rare(db
->is_closed()))
258 InMemoryDatabase::throw_database_closed();
260 while (pos
!= end
&& pos
->tname
< term
) {
269 InMemoryTermList::at_end() const
271 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
277 InMemoryTermList::positionlist_count() const
279 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
280 return db
->positionlist_count(did
, (*pos
).tname
);
284 InMemoryTermList::positionlist_begin() const
286 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
287 return db
->open_position_list(did
, (*pos
).tname
);
290 /////////////////////////////
291 // InMemoryAllDocsPostList //
292 /////////////////////////////
294 InMemoryAllDocsPostList::InMemoryAllDocsPostList(intrusive_ptr
<const InMemoryDatabase
> db_
)
295 : LeafPostList(std::string()), did(0), db(db_
)
300 InMemoryAllDocsPostList::get_termfreq() const
302 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
307 InMemoryAllDocsPostList::get_docid() const
309 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
311 Assert(did
<= db
->termlists
.size());
312 Assert(db
->termlists
[did
- 1].is_valid
);
317 InMemoryAllDocsPostList::get_wdf() const
323 InMemoryAllDocsPostList::read_position_list()
325 throw Xapian::UnimplementedError("Can't open position list for all docs iterator");
329 InMemoryAllDocsPostList::open_position_list() const
331 throw Xapian::UnimplementedError("Can't open position list for all docs iterator");
335 InMemoryAllDocsPostList::next(double /*w_min*/)
337 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
341 } while (did
<= db
->termlists
.size() && !db
->termlists
[did
- 1].is_valid
);
346 InMemoryAllDocsPostList::skip_to(Xapian::docid did_
, double /*w_min*/)
348 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
352 while (did
<= db
->termlists
.size() && !db
->termlists
[did
- 1].is_valid
) {
360 InMemoryAllDocsPostList::at_end() const
362 if (db
->is_closed()) InMemoryDatabase::throw_database_closed();
363 return (did
> db
->termlists
.size());
367 InMemoryAllDocsPostList::get_description() const
369 return "InMemoryAllDocsPostList " + str(did
);
372 ///////////////////////////
373 // Actual database class //
374 ///////////////////////////
376 // Updates are applied immediately so we can't support transactions.
377 InMemoryDatabase::InMemoryDatabase()
378 : Xapian::Database::Internal(TRANSACTION_UNIMPLEMENTED
),
379 totdocs(0), totlen(0), positions_present(false), closed(false)
381 // We keep an empty entry in postlists for convenience of implementing
382 // allterms iteration and returning a PostList for an absent term.
383 postlists
.insert(make_pair(string(), InMemoryTerm()));
386 InMemoryDatabase::~InMemoryDatabase()
392 InMemoryDatabase::reopen()
394 if (closed
) InMemoryDatabase::throw_database_closed();
399 InMemoryDatabase::close()
401 // Free all the resources, and mark the db as closed.
413 InMemoryDatabase::open_post_list(const string
& term
) const
415 return InMemoryDatabase::open_leaf_post_list(term
, false);
419 InMemoryDatabase::open_leaf_post_list(const string
& term
, bool need_pos
) const
422 if (closed
) InMemoryDatabase::throw_database_closed();
425 intrusive_ptr
<const InMemoryDatabase
> ptrtothis(this);
426 return new InMemoryAllDocsPostList(ptrtothis
);
428 map
<string
, InMemoryTerm
>::const_iterator i
= postlists
.find(term
);
429 if (i
== postlists
.end() || i
->second
.term_freq
== 0) {
430 i
= postlists
.begin();
431 // Check that our dummy entry for string() is present.
432 Assert(i
->first
.empty());
434 intrusive_ptr
<const InMemoryDatabase
> ptrtothis(this);
435 return new InMemoryPostList(ptrtothis
, i
->second
, term
);
439 InMemoryDatabase::doc_exists(Xapian::docid did
) const
441 if (closed
) InMemoryDatabase::throw_database_closed();
442 return (did
> 0 && did
<= termlists
.size() && termlists
[did
- 1].is_valid
);
446 InMemoryDatabase::get_freqs(const string
& term
,
447 Xapian::doccount
* termfreq_ptr
,
448 Xapian::termcount
* collfreq_ptr
) const
450 if (closed
) InMemoryDatabase::throw_database_closed();
451 map
<string
, InMemoryTerm
>::const_iterator i
= postlists
.find(term
);
452 if (i
!= postlists
.end()) {
454 *termfreq_ptr
= i
->second
.term_freq
;
456 *collfreq_ptr
= i
->second
.collection_freq
;
466 InMemoryDatabase::get_value_freq(Xapian::valueno slot
) const
468 if (closed
) InMemoryDatabase::throw_database_closed();
469 map
<Xapian::valueno
, ValueStats
>::const_iterator i
= valuestats
.find(slot
);
470 if (i
== valuestats
.end()) return 0;
471 return i
->second
.freq
;
475 InMemoryDatabase::get_value_lower_bound(Xapian::valueno slot
) const
477 if (closed
) InMemoryDatabase::throw_database_closed();
478 map
<Xapian::valueno
, ValueStats
>::const_iterator i
= valuestats
.find(slot
);
479 if (i
== valuestats
.end()) return string();
480 return i
->second
.lower_bound
;
484 InMemoryDatabase::get_value_upper_bound(Xapian::valueno slot
) const
486 if (closed
) InMemoryDatabase::throw_database_closed();
487 map
<Xapian::valueno
, ValueStats
>::const_iterator i
= valuestats
.find(slot
);
488 if (i
== valuestats
.end()) return string();
489 return i
->second
.upper_bound
;
493 InMemoryDatabase::get_doccount() const
495 if (closed
) InMemoryDatabase::throw_database_closed();
500 InMemoryDatabase::get_lastdocid() const
502 if (closed
) InMemoryDatabase::throw_database_closed();
503 return termlists
.size();
507 InMemoryDatabase::get_total_length() const
513 InMemoryDatabase::get_doclength(Xapian::docid did
) const
515 if (closed
) InMemoryDatabase::throw_database_closed();
516 if (!doc_exists(did
)) {
517 throw Xapian::DocNotFoundError(string("Docid ") + str(did
) +
518 string(" not found"));
520 return doclengths
[did
- 1];
524 InMemoryDatabase::get_unique_terms(Xapian::docid did
) const
526 if (closed
) InMemoryDatabase::throw_database_closed();
527 if (did
== 0 || did
> termlists
.size() || !termlists
[did
- 1].is_valid
)
528 throw Xapian::DocNotFoundError(string("Docid ") + str(did
) +
529 string(" not found"));
530 // get_unique_terms() really ought to only count terms with wdf > 0, but
531 // that's expensive to calculate on demand, so for now let's just ensure
532 // unique_terms <= doclen.
533 Xapian::termcount terms
= termlists
[did
- 1].terms
.size();
534 return std::min(terms
, Xapian::termcount(doclengths
[did
- 1]));
538 InMemoryDatabase::open_term_list(Xapian::docid did
) const
540 if (closed
) InMemoryDatabase::throw_database_closed();
542 if (!doc_exists(did
)) {
543 // FIXME: the docid in this message will be local, not global
544 throw Xapian::DocNotFoundError(string("Docid ") + str(did
) +
545 string(" not found"));
547 return new InMemoryTermList(intrusive_ptr
<const InMemoryDatabase
>(this), did
,
548 termlists
[did
- 1], doclengths
[did
- 1]);
552 InMemoryDatabase::open_term_list_direct(Xapian::docid did
) const
554 return InMemoryDatabase::open_term_list(did
);
557 Xapian::Document::Internal
*
558 InMemoryDatabase::open_document(Xapian::docid did
, bool lazy
) const
560 if (closed
) InMemoryDatabase::throw_database_closed();
562 if (!lazy
&& !doc_exists(did
)) {
563 // FIXME: the docid in this message will be local, not global
564 throw Xapian::DocNotFoundError(string("Docid ") + str(did
) +
565 string(" not found"));
567 return new InMemoryDocument(this, did
);
571 InMemoryDatabase::get_metadata(const std::string
& key
) const
573 if (closed
) InMemoryDatabase::throw_database_closed();
574 map
<string
, string
>::const_iterator i
= metadata
.find(key
);
575 if (i
== metadata
.end())
581 InMemoryDatabase::open_metadata_keylist(const string
&) const
583 if (closed
) InMemoryDatabase::throw_database_closed();
584 if (metadata
.empty()) return NULL
;
585 // FIXME: nobody implemented this yet...
586 throw Xapian::UnimplementedError("InMemory backend doesn't currently implement Database::metadata_keys_begin()");
590 InMemoryDatabase::set_metadata(const std::string
& key
,
591 const std::string
& value
)
593 if (closed
) InMemoryDatabase::throw_database_closed();
594 if (!value
.empty()) {
595 metadata
[key
] = value
;
602 InMemoryDatabase::positionlist_count(Xapian::docid did
,
603 const string
& tname
) const
605 if (closed
) InMemoryDatabase::throw_database_closed();
606 if (!doc_exists(did
)) {
609 const InMemoryDoc
&doc
= termlists
[did
- 1];
611 InMemoryTermEntry temp
;
613 auto t
= lower_bound(doc
.terms
.begin(), doc
.terms
.end(),
614 temp
, InMemoryTermEntryLessThan());
615 if (t
!= doc
.terms
.end() && t
->tname
== tname
) {
616 return t
->positions
.size();
622 InMemoryDatabase::open_position_list(Xapian::docid did
,
623 const string
& tname
) const
625 if (closed
) InMemoryDatabase::throw_database_closed();
626 if (usual(doc_exists(did
))) {
627 const InMemoryDoc
&doc
= termlists
[did
- 1];
629 InMemoryTermEntry temp
;
631 auto t
= lower_bound(doc
.terms
.begin(), doc
.terms
.end(),
632 temp
, InMemoryTermEntryLessThan());
633 if (t
!= doc
.terms
.end() && t
->tname
== tname
) {
634 return new InMemoryPositionList(t
->positions
);
637 return new InMemoryPositionList();
641 InMemoryDatabase::add_values(Xapian::docid did
,
642 const map
<Xapian::valueno
, string
> &values_
)
644 if (closed
) InMemoryDatabase::throw_database_closed();
645 if (did
> valuelists
.size()) {
646 valuelists
.resize(did
);
648 valuelists
[did
- 1] = values_
;
650 // Update the statistics.
651 map
<Xapian::valueno
, string
>::const_iterator j
;
652 for (j
= values_
.begin(); j
!= values_
.end(); ++j
) {
653 std::pair
<map
<Xapian::valueno
, ValueStats
>::iterator
, bool> i
;
654 i
= valuestats
.insert(make_pair(j
->first
, ValueStats()));
656 // Now, modify the stored statistics.
657 if ((i
.first
->second
.freq
)++ == 0) {
658 // If the value count was previously zero, set the upper and lower
659 // bounds to the newly added value.
660 i
.first
->second
.lower_bound
= j
->second
;
661 i
.first
->second
.upper_bound
= j
->second
;
663 // Otherwise, simply make sure they reflect the new value.
664 if (j
->second
< i
.first
->second
.lower_bound
) {
665 i
.first
->second
.lower_bound
= j
->second
;
667 if (j
->second
> i
.first
->second
.upper_bound
) {
668 i
.first
->second
.upper_bound
= j
->second
;
674 // We implicitly commit each modification right away, so nothing to do here.
676 InMemoryDatabase::commit()
680 // We implicitly commit each modification right away, so nothing to do here.
682 InMemoryDatabase::cancel()
687 InMemoryDatabase::delete_document(Xapian::docid did
)
689 if (closed
) InMemoryDatabase::throw_database_closed();
690 if (!doc_exists(did
)) {
691 throw Xapian::DocNotFoundError(string("Docid ") + str(did
) +
692 string(" not found"));
694 termlists
[did
- 1].is_valid
= false;
695 doclists
[did
- 1] = string();
696 map
<Xapian::valueno
, string
>::const_iterator j
;
697 for (j
= valuelists
[did
- 1].begin(); j
!= valuelists
[did
- 1].end(); ++j
) {
698 map
<Xapian::valueno
, ValueStats
>::iterator i
;
699 i
= valuestats
.find(j
->first
);
700 if (--(i
->second
.freq
) == 0) {
701 i
->second
.lower_bound
.resize(0);
702 i
->second
.upper_bound
.resize(0);
705 valuelists
[did
- 1].clear();
707 totlen
-= doclengths
[did
- 1];
708 doclengths
[did
- 1] = 0;
710 // A crude check, but it's hard to be more precise with the current
711 // InMemory structure without being very inefficient.
712 if (totdocs
== 0) positions_present
= false;
714 vector
<InMemoryTermEntry
>::const_iterator i
;
715 for (i
= termlists
[did
- 1].terms
.begin();
716 i
!= termlists
[did
- 1].terms
.end();
718 map
<string
, InMemoryTerm
>::iterator t
= postlists
.find(i
->tname
);
719 Assert(t
!= postlists
.end());
720 t
->second
.collection_freq
-= i
->wdf
;
721 --t
->second
.term_freq
;
723 // Just invalidate erased doc ids - otherwise we need to erase
724 // in a vector (inefficient) and we break any posting lists
725 // iterating over this posting list.
726 InMemoryPosting temp
;
728 auto p
= lower_bound(t
->second
.docs
.begin(), t
->second
.docs
.end(),
729 temp
, InMemoryPostingLessThan());
730 if (p
!= t
->second
.docs
.end() && p
->did
== did
) {
734 termlists
[did
- 1].terms
.clear();
738 InMemoryDatabase::replace_document(Xapian::docid did
,
739 const Xapian::Document
& document
)
741 LOGCALL_VOID(DB
, "InMemoryDatabase::replace_document", did
| document
);
743 if (closed
) InMemoryDatabase::throw_database_closed();
745 if (doc_exists(did
)) {
746 map
<Xapian::valueno
, string
>::const_iterator j
;
747 for (j
= valuelists
[did
- 1].begin(); j
!= valuelists
[did
- 1].end(); ++j
) {
748 map
<Xapian::valueno
, ValueStats
>::iterator i
;
749 i
= valuestats
.find(j
->first
);
750 if (--(i
->second
.freq
) == 0) {
751 i
->second
.lower_bound
.resize(0);
752 i
->second
.upper_bound
.resize(0);
756 totlen
-= doclengths
[did
- 1];
758 } else if (did
> termlists
.size()) {
759 termlists
.resize(did
);
760 termlists
[did
- 1].is_valid
= true;
761 doclengths
.resize(did
);
762 doclists
.resize(did
);
763 valuelists
.resize(did
);
765 termlists
[did
- 1].is_valid
= true;
768 vector
<InMemoryTermEntry
>::const_iterator i
;
769 for (i
= termlists
[did
- 1].terms
.begin();
770 i
!= termlists
[did
- 1].terms
.end();
772 map
<string
, InMemoryTerm
>::iterator t
= postlists
.find(i
->tname
);
773 Assert(t
!= postlists
.end());
774 t
->second
.collection_freq
-= i
->wdf
;
775 --t
->second
.term_freq
;
777 // Just invalidate erased doc ids - otherwise we need to erase
778 // in a vector (inefficient) and we break any posting lists
779 // iterating over this posting list.
780 InMemoryPosting temp
;
782 auto p
= lower_bound(t
->second
.docs
.begin(), t
->second
.docs
.end(),
783 temp
, InMemoryPostingLessThan());
784 if (p
!= t
->second
.docs
.end() && p
->did
== did
) {
789 doclengths
[did
- 1] = 0;
790 doclists
[did
- 1] = document
.get_data();
792 finish_add_doc(did
, document
);
796 InMemoryDatabase::add_document(const Xapian::Document
& document
)
798 LOGCALL(DB
, Xapian::docid
, "InMemoryDatabase::add_document", document
);
799 if (closed
) InMemoryDatabase::throw_database_closed();
801 Xapian::docid did
= make_doc(document
.get_data());
803 finish_add_doc(did
, document
);
809 InMemoryDatabase::finish_add_doc(Xapian::docid did
, const Xapian::Document
&document
)
812 map
<Xapian::valueno
, string
> values
;
813 Xapian::ValueIterator k
= document
.values_begin();
814 for ( ; k
!= document
.values_end(); ++k
) {
815 values
.insert(make_pair(k
.get_valueno(), *k
));
816 LOGLINE(DB
, "InMemoryDatabase::finish_add_doc(): adding value " <<
817 k
.get_valueno() << " -> " << *k
);
819 add_values(did
, values
);
822 InMemoryDoc
doc(true);
823 Xapian::TermIterator i
= document
.termlist_begin();
824 for ( ; i
!= document
.termlist_end(); ++i
) {
827 LOGLINE(DB
, "InMemoryDatabase::finish_add_doc(): adding term " << *i
);
828 Xapian::PositionIterator j
= i
.positionlist_begin();
829 if (j
== i
.positionlist_end()) {
830 /* Make sure the posting exists, even without a position. */
831 make_posting(&doc
, *i
, did
, 0, i
.get_wdf(), false);
833 positions_present
= true;
834 for ( ; j
!= i
.positionlist_end(); ++j
) {
835 make_posting(&doc
, *i
, did
, *j
, i
.get_wdf());
839 Assert(did
> 0 && did
<= doclengths
.size());
840 doclengths
[did
- 1] += i
.get_wdf();
841 totlen
+= i
.get_wdf();
842 postlists
[*i
].collection_freq
+= i
.get_wdf();
843 ++postlists
[*i
].term_freq
;
845 swap(termlists
[did
- 1], doc
);
851 InMemoryDatabase::make_term(const string
& tname
)
853 postlists
[tname
]; // Initialise, if not already there.
857 InMemoryDatabase::make_doc(const string
& docdata
)
859 termlists
.push_back(InMemoryDoc(true));
860 doclengths
.push_back(0);
861 doclists
.push_back(docdata
);
863 AssertEqParanoid(termlists
.size(), doclengths
.size());
865 return termlists
.size();
868 void InMemoryDatabase::make_posting(InMemoryDoc
* doc
,
869 const string
& tname
,
871 Xapian::termpos position
,
872 Xapian::termcount wdf
,
876 Assert(postlists
.find(tname
) != postlists
.end());
877 Assert(did
> 0 && did
<= termlists
.size());
878 Assert(did
> 0 && did
<= doclengths
.size());
879 Assert(doc_exists(did
));
882 InMemoryPosting posting
;
885 posting
.positions
.push_back(position
);
888 posting
.valid
= true;
890 // Now record the posting
891 postlists
[tname
].add_posting(std::move(posting
));
893 // Make the termentry
894 InMemoryTermEntry termentry
;
895 termentry
.tname
= tname
;
897 termentry
.positions
.push_back(position
);
901 // Now record the termentry
902 doc
->add_posting(std::move(termentry
));
906 InMemoryDatabase::term_exists(const string
& tname
) const
908 if (closed
) InMemoryDatabase::throw_database_closed();
912 map
<string
, InMemoryTerm
>::const_iterator i
= postlists
.find(tname
);
913 if (i
== postlists
.end()) return false;
914 return (i
->second
.term_freq
!= 0);
918 InMemoryDatabase::has_positions() const
920 if (closed
) InMemoryDatabase::throw_database_closed();
921 return positions_present
;
925 InMemoryDatabase::open_allterms(const string
& prefix
) const
927 if (closed
) InMemoryDatabase::throw_database_closed();
928 return new InMemoryAllTermsList(&postlists
,
929 intrusive_ptr
<const InMemoryDatabase
>(this),
934 InMemoryDatabase::throw_database_closed()
936 throw Xapian::DatabaseError("Database has been closed");
940 InMemoryDatabase::get_description() const
945 #ifdef DISABLE_GPL_LIBXAPIAN
946 # error GPL source we cannot relicense included in libxapian