/** @file * @brief An indexed database of documents */ /* Copyright 2003-2026 Olly Betts * Copyright 2007,2008,2009 Lemur Consulting Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see * . */ #ifndef XAPIAN_INCLUDED_DATABASE_H #define XAPIAN_INCLUDED_DATABASE_H #if !defined XAPIAN_IN_XAPIAN_H && !defined XAPIAN_LIB_BUILD # error Never use directly; include instead. #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Xapian { class Compactor; class Document; class WritableDatabase; /** An indexed database of documents. * * A Database object contains zero or more shards, and operations are * performed across these shards. * * To perform a search on a Database, you need to use an Enquire object. * * @since 2.0.0 This class is a reference counted handle like many other * Xapian API classes. In earlier versions, it worked like a typedef * to std::vector. The key difference is that * previously copying or assigning a Xapian::Database made a deep copy, * whereas now it makes a shallow copy. * * Most methods can throw: * * @exception Xapian::DatabaseCorruptError if database corruption is detected * @exception Xapian::DatabaseError in various situation (for example, if * there's an I/O error). * @exception Xapian::DatabaseModifiedError if the revision being read has * been discarded * @exception Xapian::DatabaseClosedError may be thrown by some methods after * after @a close() has been called * @exception Xapian::NetworkError when remote databases are in use */ class XAPIAN_VISIBILITY_DEFAULT Database { /// @internal Implementation behind check() static methods. static size_t check_(const std::string_view* path_ptr, int fd, int opts, std::ostream* out); /// @internal Implementation behind public compact() methods. void compact_(const std::string_view* output_ptr, int fd, unsigned flags, int block_size, Xapian::Compactor* compactor) const; protected: /// @private @internal Implementation behind public add_database() methods. void add_database_(const Database& other, bool read_only); public: /// Class representing the Database internals. class Internal; /// @private @internal Reference counted internals. Xapian::Internal::intrusive_ptr_nonnull internal; /** Add shards from another Database. * * Any shards in @a other are appended to the list of shards in this * object. The shards are reference counted and also remain in @a other. * * @param other Another Database to add shards from * * @exception Xapian::InvalidArgumentError if @a other is the same object * as this. */ void add_database(const Database& other) { add_database_(other, true); } /** Return number of shards in this Database object. * * If you want the number of documents, see @ get_doccount(). * * @since Xapian 1.4.12 */ size_t size() const; /** Construct a Database containing no shards. * * You can then add shards by calling add_database(). A Database * containing no shards can also be useful in situations where you need an * empty database. */ Database(); /** Open a Database. * * @param path Filing system path to open database from * @param flags Bitwise-or of Xapian::DB_* constants * * The @a path can be a file (for a stub database or a single-file glass * database) or a directory (for a standard glass database). If * @a flags includes @a DB_BACKEND_INMEMORY then @a path is ignored. * * @exception Xapian::DatabaseOpeningError if the specified database * cannot be opened * @exception Xapian::DatabaseVersionError if the specified database has * a format too old or too new to be supported. */ explicit Database(std::string_view path, int flags = 0); /** Open a single-file Database. * * This method opens a single-file Database given a file descriptor open * on it. Xapian looks starting at the current file offset, allowing a * single file database to be easily embedded within another file. * * @param fd File descriptor for the file. Xapian takes ownership * of this and will close it when the database is closed. * @param flags Bitwise-or of Xapian::DB_* constants. * * @exception Xapian::DatabaseOpeningError if the specified database * cannot be opened * @exception Xapian::DatabaseVersionError if the specified database has * a format too old or too new to be supported. */ explicit Database(int fd, int flags = 0); /// @private @internal Wrap an existing Internal. XAPIAN_VISIBILITY_INTERNAL explicit Database(Internal* internal) XAPIAN_NONNULL(); /// Destructor. virtual ~Database(); /** Copy constructor. * * The internals are reference counted, so copying is cheap. */ Database(const Database& o); /** Assignment operator. * * The internals are reference counted, so assignment is cheap. */ Database& operator=(const Database& o); /// Move constructor. Database(Database&& o); /// Move assignment operator. Database& operator=(Database&& o); /** Reopen the database at the latest available revision. * * Xapian databases (at least with most backends) support versioning * such that a Database object uses a snapshot of the database. * However, write operations may cause this snapshot to be discarded, * which can cause Xapian::DatabaseModifiedError to be thrown. You * can recover from this situation by calling reopen() and restarting * the search operation. * * All shards are updated to the latest available revision. This should * be a cheap operation if they're already at the latest revision, so * if you're using the same Database object for many searches it's * reasonable to call reopen() before each search. * * @return true if one or more shards have moved to a newer revision * (if false is returned then it's definitely the case that no * shards were reopened, which applications may find useful when * caching results, etc). In Xapian < 1.3.0, this method did not * return a value. * * @exception Xapian::DatabaseError is thrown if close() has been called * on any of the shards. */ bool reopen(); /** Close the database. * * This closes the database and closes all its file handles. * * For a WritableDatabase, if a transaction is active it will be aborted, * while if no transaction is active commit() will be implicitly called. * Also the write lock is released. * * Calling close() on an object cannot be undone - in particular, a * subsequent call to reopen() on the same object will not reopen it, but * will instead throw a Xapian::DatabaseClosedError exception. * * Calling close() again on an object which has already been closed has * no effect (and doesn't raise an exception). * * After close() has been called, calls to other methods of the database, * and to methods of other objects associated with the database, will * either: * * - behave exactly as they would have done if the database had not been * closed (this can only happen if all the required data is cached) * * - raise a Xapian::DatabaseClosedError exception. * * The reason for this behaviour is that otherwise we'd have to check that * the database is still open on every method call on every object * associated with a Database, when in many cases they are working on data * which has already been loaded and so they are able to just behave * correctly. * * @since This method was added in Xapian 1.1.0. */ void close(); /// Return a string describing this object. virtual std::string get_description() const; /** Start iterating the postings of a term. * * @param term The term to iterate the postings of. An empty string * acts as a special pseudo-term which indexes all the * documents in the database with a wdf of 1. */ PostingIterator postlist_begin(std::string_view term) const; /** End iterator corresponding to postlist_begin(). */ PostingIterator postlist_end(std::string_view) const noexcept { return PostingIterator(); } /** Start iterating the terms in a document. * * @param did The document id to iterate terms from * * The terms are returned in ascending string order (by byte value). */ TermIterator termlist_begin(Xapian::docid did) const; /** End iterator corresponding to termlist_begin(). */ TermIterator termlist_end(Xapian::docid) const noexcept { return TermIterator(); } /** Does this database have any positional information? */ bool has_positions() const; /** Start iterating positions for a term in a document. * * @param did The document id of the document * @param term The term * * @since 1.1.0 If the specified document doesn't exist or the * specified term doesn't exist in the specified document, * then a valid iterator is still returned, but it will be * equal to positionlist_end(). */ PositionIterator positionlist_begin(Xapian::docid did, std::string_view term) const; /** End iterator corresponding to positionlist_begin(). */ PositionIterator positionlist_end(Xapian::docid, std::string_view) const noexcept { return PositionIterator(); } /** Start iterating all terms in the database with a given prefix. * * The terms are returned in ascending string order (by byte value). * * @param prefix The prefix to restrict the returned terms to (default: * iterate all terms) */ TermIterator allterms_begin(std::string_view prefix = {}) const; /** End iterator corresponding to allterms_begin(prefix). */ TermIterator allterms_end(std::string_view = {}) const noexcept { return TermIterator(); } /// Get the number of documents in the database. Xapian::doccount get_doccount() const; /// Get the highest document id which has been used in the database. Xapian::docid get_lastdocid() const; /// Get the mean document length in the database. double get_average_length() const; /// Old name for get_average_length() for backward compatibility. double get_avlength() const { return get_average_length(); } /** Get the total length of all the documents in the database. * * @since Added in Xapian 1.4.5. */ Xapian::totallength get_total_length() const; /** Get the number of documents indexed by a specified term. * * @param term The term to get the frequency of. An empty string * acts as a special pseudo-term which indexes all the * documents in the database, so returns get_doccount(). * If the term isn't present in the database, 0 is * returned. */ Xapian::doccount get_termfreq(std::string_view term) const; /** Test is a particular term is present in any document. * * @param term The term to test for. An empty string acts as a * special pseudo-term which indexes all the documents in * the database, so returns true if the database contains * any documents. * * db.term_exists(t) gives the same answer as db.get_termfreq(t) != 0, but * is typically more efficient. */ bool term_exists(std::string_view term) const; /** Get the total number of occurrences of a specified term. * * The collection frequency of a term is defined as the total number of * times it occurs in the database, which is the sum of its wdf in all the * documents it indexes. * * @param term The term to get the collection frequency of. An empty * string acts as a special pseudo-term which indexes all * the documents in the database, so returns * get_doccount(). If the term isn't present in the * database, 0 is returned. */ Xapian::termcount get_collection_freq(std::string_view term) const; /** Return the frequency of a given value slot. * * This is the number of documents which have a (non-empty) value stored * in the slot. * * @param slot The value slot to examine. */ Xapian::doccount get_value_freq(Xapian::valueno slot) const; /** Get a lower bound on the values stored in the given value slot. * * If there are no values stored in the given value slot, this will return * an empty string. * * @param slot The value slot to examine. */ std::string get_value_lower_bound(Xapian::valueno slot) const; /** Get an upper bound on the values stored in the given value slot. * * If there are no values stored in the given value slot, this will return * an empty string. * * @param slot The value slot to examine. */ std::string get_value_upper_bound(Xapian::valueno slot) const; /** Get a lower bound on the length of a document in this DB. * * This bound does not include any zero-length documents. */ Xapian::termcount get_doclength_lower_bound() const; /// Get an upper bound on the length of a document in this DB. Xapian::termcount get_doclength_upper_bound() const; /// Get an upper bound on the wdf of term @a term. Xapian::termcount get_wdf_upper_bound(std::string_view term) const; /** Get a lower bound on the unique terms size of a document in this DB. * * @since Added in Xapian 2.0.0. */ Xapian::termcount get_unique_terms_lower_bound() const; /** Get an upper bound on the unique terms size of a document in this DB. * * @since Added in Xapian 2.0.0. */ Xapian::termcount get_unique_terms_upper_bound() const; /// Return an iterator over the value in slot @a slot for each document. ValueIterator valuestream_begin(Xapian::valueno slot) const; /// Return end iterator corresponding to valuestream_begin(). ValueIterator valuestream_end(Xapian::valueno) const noexcept { return ValueIterator(); } /** Get the length of a specified document. * * @param did The document id of the document * * Xapian defines a document's length as the sum of the wdf of all the * terms which index it. */ Xapian::termcount get_doclength(Xapian::docid did) const; /** Get the number of unique terms in a specified document. * * @param did The document id of the document * * This is the number of different terms which index the given document. */ Xapian::termcount get_unique_terms(Xapian::docid did) const; /** Get the maximum wdf value in a specified document. * * @param did The document id of the document * * @since Added in Xapian 2.0.0. */ Xapian::termcount get_wdfdocmax(Xapian::docid did) const; /** Send a keep-alive message. * * For remote databases, this method sends a message to the server to * reset the timeout timer. As well as preventing timeouts at the Xapian * remote protocol level, this message will also avoid timeouts at lower * levels. * * For local databases, this method does nothing. */ void keep_alive(); /** Get a document from the database. * * The returned object acts as a handle which lazily fetches information * about the specified document from the database. * * @param did The document ID of the document to be get * @param flags Zero or more flags bitwise-or-ed together (currently * only Xapian::DOC_ASSUME_VALID is supported). * (default: 0) * * @since The @a flags parameter was added in Xapian 2.0.0. * * @exception Xapian::InvalidArgumentError is thrown if @a did is 0. * * @exception Xapian::DocNotFoundError is thrown if the specified docid * is not present in this database. */ Xapian::Document get_document(Xapian::docid did, unsigned flags = 0) const; /** Suggest a spelling correction. * * @param word The potentially misspelled word. * @param max_edit_distance Only consider words which are at most * @a max_edit_distance edits from @a word. * An edit is a character insertion, deletion, * or the transposition of two adjacent * characters (default is 2). */ std::string get_spelling_suggestion(std::string_view word, unsigned max_edit_distance = 2) const; /** An iterator which returns all the spelling correction targets. * * This returns all the words which are considered as targets for the * spelling correction algorithm. The frequency of each word is available * as the term frequency of each entry in the returned iterator. */ Xapian::TermIterator spellings_begin() const; /// End iterator corresponding to spellings_begin(). Xapian::TermIterator spellings_end() const noexcept { return Xapian::TermIterator(); } /** An iterator which returns all the synonyms for a given term. * * @param term The term to return synonyms for. */ Xapian::TermIterator synonyms_begin(std::string_view term) const; /// End iterator corresponding to synonyms_begin(term). Xapian::TermIterator synonyms_end(std::string_view) const noexcept { return Xapian::TermIterator(); } /** An iterator which returns all terms which have synonyms. * * @param prefix If non-empty, only terms with this prefix are returned. */ Xapian::TermIterator synonym_keys_begin(std::string_view prefix = {}) const; /// End iterator corresponding to synonym_keys_begin(prefix). Xapian::TermIterator synonym_keys_end(std::string_view = {}) const noexcept { return Xapian::TermIterator(); } /** Get the user-specified metadata associated with a given key. * * User-specified metadata allows you to store arbitrary information in * the form of (key, value) pairs. See @a * WritableDatabase::set_metadata() for more information. * * When invoked on a Xapian::Database object representing multiple * databases, currently only the metadata for the first is considered but * this behaviour may change in the future. * * If there is no piece of metadata associated with the specified key, an * empty string is returned (this applies even for backends which don't * support metadata). * * Empty keys are not valid, and specifying one will cause an exception. * * @param key The key of the metadata item to access. * * @return The retrieved metadata item's value. * * @exception Xapian::InvalidArgumentError will be thrown if the key * supplied is empty. */ std::string get_metadata(std::string_view key) const; /** An iterator which returns all user-specified metadata keys. * * When invoked on a Xapian::Database object representing multiple * databases, currently only the metadata for the first is considered but * this behaviour may change in the future. * * If the backend doesn't support metadata, then this method returns an * iterator which compares equal to that returned by metadata_keys_end(). * * @param prefix If non-empty, only keys with this prefix are returned. * * @exception Xapian::UnimplementedError will be thrown if the backend * implements user-specified metadata, but doesn't implement * iterating its keys (currently this happens for the InMemory * backend). */ Xapian::TermIterator metadata_keys_begin(std::string_view prefix = {}) const; /// End iterator corresponding to metadata_keys_begin(). Xapian::TermIterator metadata_keys_end(std::string_view = {}) const noexcept { return Xapian::TermIterator(); } /** Get the UUID for the database. * * The UUID will persist for the lifetime of the database. * * Replicas (eg, made with the replication protocol, or by copying all the * database files) will have the same UUID. However, copies (made with * copydatabase, or xapian-compact) will have different UUIDs. * * If the backend does not support UUIDs or this database has no * subdatabases, the UUID will be empty. * * If this database has multiple sub-databases, the UUID string will * contain the UUIDs of all the sub-databases separated by colons. */ std::string get_uuid() const; /** Test if this database is currently locked for writing. * * If the underlying object is actually a WritableDatabase, always returns * true unless close() has been called. * * Otherwise tests if there's a writer holding the lock (or if we can't * test for a lock without taking it on the current platform, throw * Xapian::UnimplementedError). If there's an error while trying to test * the lock, throws Xapian::DatabaseLockError. * * For multi-databases, this tests each sub-database and returns true if * any of them are locked. */ bool locked() const; /** Lock a read-only database for writing. * * If the database is actually already writable (i.e. a WritableDatabase * via a Database reference) then the same database is returned (with * its flags updated, so this provides an efficient way to modify flags * on an open WritableDatabase). * * Unlike unlock(), the object this is called on remains open. * * @param flags The flags to use for the writable database. Flags which * specify how to open the database are ignored (e.g. * DB_CREATE_OR_OVERWRITE doesn't result in the database * being wiped), and flags which specify the backend are * also ignored as they are only relevant when creating * a new database. * * @return A WritableDatabase object open on the same database. * * @since Added in Xapian 2.0.0. */ Xapian::WritableDatabase lock(int flags = 0); /** Release a database write lock. * * If called on a read-only database then the same database is returned. * * If called on a writable database, the object this method was called * on is closed. * * @return A Database object open on the same database. * * @since Added in Xapian 2.0.0. */ Xapian::Database unlock(); /** Get the revision of the database. * * The revision is an unsigned integer which increases with each commit. * In practice it typically increments by one for each revision but you * should not rely on that. * * @since Xapian 1.4.13 and later return 0 for a database consisting of no * shards. * * @exception Xapian::InvalidOperationError If the database consists of * more than one shard. In Xapian < 1.4.13, this is also thrown * if the database consists of no shards. * @exception Xapian::UnimplementedError Currently this is only * implemented for glass (and for chert in Xapian 1.4.x). */ Xapian::rev get_revision() const; /** Check the integrity of a database or database table. * * @param path Path to database or table * @param opts Options to use for check * @param out std::ostream to write output to (NULL for no output) */ static size_t check(std::string_view path, int opts = 0, std::ostream* out = NULL) { return check_(&path, 0, opts, out); } /** Check the integrity of a single file database. * * @param fd file descriptor for the database. The current file * offset is used, allowing checking a single file * database which is embedded within another file. Xapian * takes ownership of the file descriptor and will close * it before returning. * @param opts Options to use for check * @param out std::ostream to write output to (NULL for no output) */ static size_t check(int fd, int opts = 0, std::ostream* out = NULL) { return check_(NULL, fd, opts, out); } /** Produce a compact version of this database. * * @param output Path to write the compact version to. This can be the * same as an input if that input is a stub database (in * which case the database(s) listed in the stub will be * compacted to a new database and then the stub will be * atomically updated to point to this new database). * * @param flags Any of the following combined using bitwise-or (| in * C++): * - Xapian::DBCOMPACT_NO_RENUMBER By default the document ids will * be renumbered the output - currently by applying the same * offset to all the document ids in a particular source database. * If this flag is specified, then this renumbering doesn't * happen, but all the document ids must be unique over all source * databases. Currently the ranges of document ids in each source * must not overlap either, though this restriction may be removed * in the future. * - Xapian::DBCOMPACT_MULTIPASS * If merging more than 3 databases, merge the postlists in * multiple passes, which is generally faster but requires more * disk space for temporary files. * - Xapian::DBCOMPACT_SINGLE_FILE * Produce a single-file database (only supported for glass * currently). * - At most one of: * - Xapian::Compactor::STANDARD - Don't split items unnecessarily. * - Xapian::Compactor::FULL - Split items whenever it saves space * (the default). * - Xapian::Compactor::FULLER - Allow oversize items to save more * space (not recommended if you ever * plan to update the compacted * database). @since 1.4.31 Has the * same effect as FULL. * * - At most one of the following to specify the output format (currently * only glass to honey conversion is supported, and all shards of the * input must have the same format): * - Xapian::DB_BACKEND_HONEY * * @param block_size This specifies the block size (in bytes) for to * use for the output. For glass, the block size * must be a power of 2 between 2048 and 65536 * (inclusive), and the default (also used if an * invalid value is passed) is 8192 bytes. * * @since 1.3.4 This method was added to replace various methods of the * Compactor class. */ void compact(std::string_view output, unsigned flags = 0, int block_size = 0) { compact_(&output, 0, flags, block_size, NULL); } /** Produce a compact version of this database. * * This variant writes a single-file database to the specified file * descriptor. Only the glass backend supports such databases, so * this form is only supported for this backend. * * @param fd File descriptor to write the compact version to. The * descriptor needs to be readable and writable (open with * O_RDWR) and seekable. The current file offset is used, * allowing compacting to a single file database embedded * within another file. Xapian takes ownership of the * file descriptor and will close it before returning. * * @param flags Any of the following combined using bitwise-or (| in * C++): * - Xapian::DBCOMPACT_NO_RENUMBER By default the document ids will * be renumbered the output - currently by applying the same * offset to all the document ids in a particular source database. * If this flag is specified, then this renumbering doesn't * happen, but all the document ids must be unique over all source * databases. Currently the ranges of document ids in each source * must not overlap either, though this restriction may be removed * in the future. * - Xapian::DBCOMPACT_MULTIPASS * If merging more than 3 databases, merge the postlists in * multiple passes, which is generally faster but requires more * disk space for temporary files. * - Xapian::DBCOMPACT_SINGLE_FILE * Produce a single-file database (only supported for glass * currently). * - At most one of: * - Xapian::Compactor::STANDARD - Don't split items unnecessarily. * - Xapian::Compactor::FULL - Split items whenever it saves space * (the default). * - Xapian::Compactor::FULLER - Allow oversize items to save more * space (not recommended if you ever * plan to update the compacted * database). @since 1.4.31 Has the * same effect as FULL. * * @param block_size This specifies the block size (in bytes) for to * use for the output. For glass, the block size * must be a power of 2 between 2048 and 65536 * (inclusive), and the default (also used if an * invalid value is passed) is 8192 bytes. * * @since 1.3.4 This method was added to replace various methods of the * Compactor class. */ void compact(int fd, unsigned flags = 0, int block_size = 0) { compact_(NULL, fd, flags, block_size, NULL); } /** Produce a compact version of this database. * * The @a compactor functor allows handling progress output and * specifying how user metadata is merged. * * @param output Path to write the compact version to. * This can be the same as an input if that input is a * stub database (in which case the database(s) listed * in the stub will be compacted to a new database and * then the stub will be atomically updated to point to * this new database). * * @param flags Any of the following combined using bitwise-or (| in * C++): * - Xapian::DBCOMPACT_NO_RENUMBER By default the document ids will * be renumbered the output - currently by applying the same * offset to all the document ids in a particular source database. * If this flag is specified, then this renumbering doesn't * happen, but all the document ids must be unique over all source * databases. Currently the ranges of document ids in each source * must not overlap either, though this restriction may be removed * in the future. * - Xapian::DBCOMPACT_MULTIPASS * If merging more than 3 databases, merge the postlists in * multiple passes, which is generally faster but requires more * disk space for temporary files. * - Xapian::DBCOMPACT_SINGLE_FILE * Produce a single-file database (only supported for glass * currently). * - At most one of: * - Xapian::Compactor::STANDARD - Don't split items unnecessarily. * - Xapian::Compactor::FULL - Split items whenever it saves space * (the default). * - Xapian::Compactor::FULLER - Allow oversize items to save more * space (not recommended if you ever * plan to update the compacted * database). @since 1.4.31 Has the * same effect as FULL. * * @param block_size This specifies the block size (in bytes) for to * use for the output. For glass, the block size * must be a power of 2 between 2048 and 65536 * (inclusive), and the default (also used if an * invalid value is passed) is 8192 bytes. * * @param compactor Functor * * @since 1.3.4 This method was added to replace various methods of the * Compactor class. */ void compact(std::string_view output, unsigned flags, int block_size, Xapian::Compactor& compactor) { compact_(&output, 0, flags, block_size, &compactor); } /** Produce a compact version of this database. * * The @a compactor functor allows handling progress output and specifying * how user metadata is merged. * * This variant writes a single-file database to the specified file * descriptor. Only the glass backend supports such databases, so this * form is only supported for this backend. * * @param fd File descriptor to write the compact version to. The * descriptor needs to be readable and writable (open with * O_RDWR) and seekable. The current file offset is used, * allowing compacting to a single file database embedded * within another file. Xapian takes ownership of the * file descriptor and will close it before returning. * * @param flags Any of the following combined using bitwise-or (| in * C++): * - Xapian::DBCOMPACT_NO_RENUMBER By default the document ids will * be renumbered the output - currently by applying the same * offset to all the document ids in a particular source database. * If this flag is specified, then this renumbering doesn't * happen, but all the document ids must be unique over all source * databases. Currently the ranges of document ids in each source * must not overlap either, though this restriction may be removed * in the future. * - Xapian::DBCOMPACT_MULTIPASS * If merging more than 3 databases, merge the postlists in * multiple passes, which is generally faster but requires more * disk space for temporary files. * - Xapian::DBCOMPACT_SINGLE_FILE * Produce a single-file database (only supported for glass * currently). * - At most one of: * - Xapian::Compactor::STANDARD - Don't split items unnecessarily. * - Xapian::Compactor::FULL - Split items whenever it saves space * (the default). * - Xapian::Compactor::FULLER - Allow oversize items to save more * space (not recommended if you ever * plan to update the compacted * database). @since 1.4.31 Has the * same effect as FULL. * * @param block_size This specifies the block size (in bytes) for to * use for the output. For glass, the block size * must be a power of 2 between 2048 and 65536 * (inclusive), and the default (also used if an * invalid value is passed) is 8192 bytes. * * @param compactor Functor * * @since 1.3.4 This method was added to replace various methods of the * Compactor class. */ void compact(int fd, unsigned flags, int block_size, Xapian::Compactor& compactor) { compact_(NULL, fd, flags, block_size, &compactor); } /** Reconstruct document text. * * This uses term positional information to reconstruct the document text * which was indexed. Reading the required positional information is * potentially quite I/O intensive. * * The reconstructed text will be missing punctuation and most * capitalisation. * * @param did The document id of the document to reconstruct * @param length Number of bytes of text to aim for - note that * slightly more may be returned (default: 0 meaning * unlimited) * @param prefix Term prefix to reconstruct (default: none) * @param start_pos First position to reconstruct (default: 0) * @param end_pos Last position to reconstruct (default: 0 meaning all) * * @since Added in Xapian 2.0.0. */ std::string reconstruct_text(Xapian::docid did, size_t length = 0, std::string_view prefix = {}, Xapian::termpos start_pos = 0, Xapian::termpos end_pos = 0) const; }; /** This class provides read/write access to a database. * * A WritableDatabase object contains zero or more shards, and operations are * performed across these shards. Documents added by add_document() are * stored to the shards in a round-robin fashion. * * @since 2.0.0 This class is a reference counted handle like many other * Xapian API classes. In earlier versions, it worked like a typedef * to std::vector. The key difference is that * previously copying or assigning a Xapian::Database made a deep copy, * whereas now it makes a shallow copy. * * Most methods can throw: * * @exception Xapian::DatabaseCorruptError if database corruption is detected * @exception Xapian::DatabaseError in various situation (for example, calling * methods after @a close() has been called) * @exception Xapian::NetworkError when remote databases are in use */ class XAPIAN_VISIBILITY_DEFAULT WritableDatabase : public Database { /** @internal @private Helper method which implements cancel_transaction() * and commit_transaction(). * * @param do_commit If true, then commit, else cancel. */ void end_transaction_(bool do_commit); public: /** Create a WritableDatabase with no subdatabases. * * The created object isn't very useful in this state - it's intended * as a placeholder value. */ WritableDatabase() : Database() {} /** Add shards from another WritableDatabase. * * Any shards in @a other are added to the list of shards in this object. * The shards are reference counted and also remain in @a other. * * @param other Another WritableDatabase to add shards from * * @exception Xapian::InvalidArgumentError if @a other is the same object * as this. */ void add_database(const WritableDatabase& other) { // This method is provided mainly so that adding a Database to a // WritableDatabase is a compile-time error - prior to 2.0.0, it // would essentially act as a "black-hole" shard which discarded // any changes made to it. add_database_(other, false); } /** Create or open a Xapian database for both reading and writing. * * @param path Filing system path for the database. If creating a * new database with a backend which uses a directory of * files (such as glass does by default) then Xapian will * create a directory for @a path if necessary (but the * parent directory must already exist). * * @param flags A bitwise-or (| in C++) combination of: * * * at most one of the following constants indicating how to handle * the database already existing or not (the default action is * Xapian::DB_CREATE_OR_OPEN): * * Constant | DB exists | DB doesn't exist * ------------------------------ | --------- | ------------------ * Xapian::DB_CREATE_OR_OPEN | open | create * Xapian::DB_CREATE | fail | create * Xapian::DB_CREATE_OR_OVERWRITE | overwrite | create * Xapian::DB_OPEN | open | fail * * * at most one of the follow constants indicating which backend to * use when creating a new database, ignored when opening or overwriting * an existing database (default: currently Xapian::DB_BACKEND_GLASS): * * Constant | Meaning * ------------------------------ | ----------------------- * Xapian::DB_BACKEND_GLASS | Create a glass database * Xapian::DB_BACKEND_INMEMORY | Create inmemory DB (ignores @a path) * * * any number of the following flags: * * - Xapian::DB_NO_SYNC don't call fsync() or similar * - Xapian::DB_FULL_SYNC try harder to ensure data is safe * - Xapian::DB_DANGEROUS don't be crash-safe, no concurrent readers * - Xapian::DB_NO_TERMLIST don't use a termlist table * - Xapian::DB_RETRY_LOCK to wait to get a write lock * * @param block_size The block size in bytes to use when creating a * new database. This is ignored when opening an * existing database, and by backends which don't * have the concept of a block size. The glass * backend allows block sizes which are a power of * 2 between 2048 and 65536 (inclusive) and its * default (also used instead of an invalid value) * is 8192 bytes. * * @exception Xapian::DatabaseLockError is thrown if the database's * write lock could not be acquired. * @exception Xapian::DatabaseOpeningError if the specified database * cannot be opened * @exception Xapian::DatabaseVersionError if the specified database has * a format too old or too new to be supported. */ explicit WritableDatabase(std::string_view path, int flags = 0, int block_size = 0); /** @private @internal Create a WritableDatabase given its internals. */ XAPIAN_VISIBILITY_INTERNAL explicit WritableDatabase(Database::Internal* internal_) : Database(internal_) {} /** Copy constructor. * * The internals are reference counted, so copying is cheap. */ WritableDatabase(const WritableDatabase& o) : Database(o) {} /** Assignment operator. * * The internals are reference counted, so assignment is cheap. */ WritableDatabase& operator=(const WritableDatabase& o) { Database::operator=(o); return *this; } /// Move constructor. WritableDatabase(WritableDatabase&& o) : Database(std::move(o)) {} /// Move assignment operator. WritableDatabase& operator=(WritableDatabase&& o) { Database::operator=(std::move(o)); return *this; } /** Commit pending modifications. * * Updates to a Xapian database are more efficient when applied in bulk, * so by default Xapian stores modifications in memory until a threshold * is exceeded and then they are committed to disk. * * When the database is closed (by an explicit call to close() or its * destructor being called) then commit() is implicitly called unless * a transaction is active. * * You can force any such pending modifications to be committed by calling * this method, but bear in mind that the batching happens for a reason * and calling commit() a lot is likely to slow down indexing. * * If the commit operation succeeds then the changes are reliably written * to disk and available to readers. If the commit operation fails, then * any pending modifications are discarded. * * However, note that if called on a sharded database, atomicity isn't * guaranteed between shards - it's possible for the changes to one * shard to be committed but changes to another shard to fail. * * It's not valid to call commit() within a transaction - see * begin_transaction() for more details of how transactions work in * Xapian. * * Currently batched modifications are automatically committed every * 10000 documents added, deleted, or modified. This value is rather * conservative, and if you have a machine with plenty of memory, * you can improve indexing throughput dramatically by setting * XAPIAN_FLUSH_THRESHOLD in the environment to a larger value. * * @since This method was new in Xapian 1.1.0 - in earlier versions it * was called flush(). */ void commit(); /** Begin a transaction. * * A Xapian transaction is a set of consecutive modifications to be * committed as an atomic unit - in any committed revision of the * database either none are present or they all are. * * However, note that if called on a sharded database, atomicity isn't * guaranteed between shards. Within each shard, the transaction will * still act atomically. * * A transaction is started with begin_transaction() and can either be * completed by calling commit_transaction() or aborted by calling * cancel_transaction(). * * Closing the database (by an explicit call to close() or by its * destructor being called) when a transaction is active will implicitly * call cancel_transaction() to abort the transaction and discard the * changes in it. * * By default, commit() is implicitly called by begin_transaction() and * commit_transaction() so that the changes in the transaction are * committed or not independent of changes before or after it. * * The downside of these implicit calls to commit() is that small * transactions can harm indexing performance in the same way that * explicitly calling commit() frequently can. * * If you're applying atomic groups of changes and only wish to ensure * that each group is either applied or not applied, then you can prevent * the automatic commit() before and after the transaction by starting the * transaction with begin_transaction(false). However, if * cancel_transaction() is called (or if commit_transaction() isn't called * before the WritableDatabase object is destroyed) then any changes which * were pending before the transaction began will also be discarded. * * @param flushed Is this a flushed transaction? By default transactions * are "flushed", which means that committing a * transaction will ensure those changes are permanently * written to the database. By contrast, unflushed * transactions only ensure that changes within the * transaction are either all applied or all aren't. * * @exception Xapian::UnimplementedError is thrown if this is an InMemory * database, which don't currently support transactions. * @exception Xapian::InvalidOperationError will be thrown if a transaction * is already active. */ void begin_transaction(bool flushed = true); /** Complete the transaction currently in progress. * * If the transaction was begun as a flushed transaction then the changes * in it have been committed to the database upon successful completion * of this method. * * If an exception is thrown, then the changes in the transaction will be * discarded (if the transaction was not begun as a flushed transaction, * any changes made but not committed before begin_transaction() will also * be discarded). * * In all cases the transaction will no longer be in progress. * * Note that if called on a sharded database, atomicity isn't guaranteed * between shards. Within each shard, the transaction will still act * atomically. * * @exception Xapian::UnimplementedError is thrown if this is an InMemory * database, which don't currently support transactions. * @exception Xapian::InvalidOperationError is thrown if no transaction * was active. */ void commit_transaction() { end_transaction_(true); } /** Abort the transaction currently in progress. * * Changes made within the current transaction will be discarded (if the * transaction was not begun as a flushed transaction, any changes made * but not committed before begin_transaction() will also be discarded). * * @exception Xapian::UnimplementedError is thrown if this is an InMemory * database, which don't currently support transactions. * @exception Xapian::InvalidOperationError is thrown if no transaction * was active. */ void cancel_transaction() { end_transaction_(false); } /** Add a document to the database. * * The document is allocated document ID (get_lastdocid() + 1) - the * next highest document ID which has never previously been used by * this database (so docids from deleted documents won't be reused). * * If you want to specify the document ID to be used, you should * call replace_document() instead. * * If a transaction is active, the document addition is added to the * transaction; otherwise it is added to the current batch of changes. * Either way, it won't be visible to readers right away (unless we're * not in a transaction and the addition triggers an automatic commit). * * @param doc The Document object to be added. * * @return The document ID allocated to the document. */ Xapian::docid add_document(const Xapian::Document& doc); /** Delete a document from the database. * * This method removes the document with the specified document ID * from the database. * * If a transaction is active, the document removal is added to the * transaction; otherwise it is added to the current batch of changes. * Either way, it won't be visible to readers right away (unless we're * not in a transaction and the addition triggers an automatic commit). * * @param did The document ID of the document to be removed. */ void delete_document(Xapian::docid did); /** Delete any documents indexed by a term from the database. * * This method removes any documents indexed by the specified term * from the database. * * A major use is for convenience when UIDs from another system are * mapped to terms in Xapian, although this method has other uses * (for example, you could add a "deletion date" term to documents at * index time and use this method to delete all documents due for * deletion on a particular date). * * @param unique_term The term to remove references to. * * @since 2.0.0 The changes made by this method are made atomically. * Previously automatic commits could happen during the * batch. */ void delete_document(std::string_view unique_term); /** Replace a document in the database. * * This method replaces the document with the specified document ID. * If document ID @a did isn't currently used, the document will be * added with document ID @a did. * * The monotonic counter used for automatically allocating document * IDs is increased so that the next automatically allocated document * ID will be did + 1. Be aware that if you use this method to * specify a high document ID for a new document, and also use * WritableDatabase::add_document(), Xapian may get to a state where * this counter wraps around and will be unable to automatically * allocate document IDs! * * Note that changes to the database won't be immediately committed to * disk; see commit() for more details. * * @param did The document ID of the document to be replaced. * @param document The new document. */ void replace_document(Xapian::docid did, const Xapian::Document& document); /** Replace any documents matching a term. * * This method replaces any documents indexed by the specified term * with the specified document. If any documents are indexed by the * term, the lowest document ID will be used for the document, * otherwise a new document ID will be generated as for add_document. * * One common use is to allow UIDs from another system to easily be * mapped to terms in Xapian. Note that this method doesn't * automatically add unique_term as a term, so you'll need to call * document.add_term(unique_term) first when using replace_document() * in this way. * * Note that changes to the database won't be immediately committed to * disk; see commit() for more details. * * @param unique_term The "unique" term. * @param document The new document. * * @return The document ID used by the new document. If term existed * in the database, this will be the first document ID that * was indexed by that term; otherwise the database allocates * (get_lastdocid() + 1) as it does for add_document(). * * @since 2.0.0 The changes made by this method are made atomically. * Previously automatic commits could happen during the * batch. */ Xapian::docid replace_document(std::string_view unique_term, const Xapian::Document& document); /** Add a word to the spelling dictionary. * * If the word is already present, its frequency is increased. * * @param word The word to add. * @param freqinc How much to increase its frequency by (default 1). */ void add_spelling(std::string_view word, Xapian::termcount freqinc = 1) const; /** Remove a word from the spelling dictionary. * * The word's frequency is decreased, and if would become zero or less * then the word is removed completely. * * @param word The word to remove. * @param freqdec How much to decrease its frequency by (default 1). * * @return Any "unused" freqdec (if the word's frequency was less than * freqdec then the difference is returned, else 0 is returned). * Prior to 2.0.0 this method had void return type. */ termcount remove_spelling(std::string_view word, termcount freqdec = 1) const; /** Add a synonym for a term. * * @param term The term to add a synonym for. * @param synonym The synonym to add. If this is already a synonym for * @a term, then no action is taken. */ void add_synonym(std::string_view term, std::string_view synonym) const; /** Remove a synonym for a term. * * @param term The term to remove a synonym for. * @param synonym The synonym to remove. If this isn't currently a * synonym for @a term, then no action is taken. */ void remove_synonym(std::string_view term, std::string_view synonym) const; /** Remove all synonyms for a term. * * @param term The term to remove all synonyms for. If the term has * no synonyms, no action is taken. */ void clear_synonyms(std::string_view term) const; /** Set the user-specified metadata associated with a given key. * * This method sets the metadata value associated with a given key. If * there is already a metadata value stored in the database with the same * key, the old value is replaced. If you want to delete an existing item * of metadata, just set its value to the empty string. * * User-specified metadata allows you to store arbitrary information in * the form of (key, value) pairs. * * There's no hard limit on the number of metadata items, or the size of * the metadata values. Metadata keys have a limited length, which depend * on the backend. We recommend limiting them to 200 bytes. Empty keys * are not valid, and specifying one will cause an exception. * * Metadata modifications are committed to disk in the same way as * modifications to the documents in the database are: i.e., modifications * are atomic, and won't be committed to disk immediately (see commit() * for more details). This allows metadata to be used to link databases * with versioned external resources by storing the appropriate version * number in a metadata item. * * You can also use the metadata to store arbitrary extra information * associated with terms, documents, or postings by encoding the termname * and/or document id into the metadata key. * * @param key The key of the metadata item to set. * * @param metadata The value of the metadata item to set. * * @exception Xapian::DatabaseError will be thrown if a problem occurs * while writing to the database. * * @exception Xapian::DatabaseCorruptError will be thrown if the database * is in a corrupt state. * * @exception Xapian::InvalidArgumentError will be thrown if the key * supplied is empty. * * @exception Xapian::UnimplementedError will be thrown if the database * backend in use doesn't support user-specified metadata. */ void set_metadata(std::string_view key, std::string_view metadata); /// Return a string describing this object. std::string get_description() const; }; } #endif // XAPIAN_INCLUDED_DATABASE_H