Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/db.cpp
4 : : \copyright 2022-2025 J. Bakosi,
5 : : All rights reserved. See the LICENSE file for details.
6 : : \brief Piac database functionality
7 : : */
8 : : // *****************************************************************************
9 : :
10 : : #include <string>
11 : :
12 : : #include "string_util.hpp"
13 : : #include "logging_util.hpp"
14 : : #include "crypto_util.hpp"
15 : : #include "db.hpp"
16 : : #include "document.hpp"
17 : :
18 : : Xapian::doccount
19 : 23 : piac::get_doccount( const std::string db_name )
20 : : // *****************************************************************************
21 : : // Get number of documents in Xapian database
22 : : //! \param[in] db_name Name of Xapian db to operate on
23 : : //! \return Number of documents in database
24 : : // *****************************************************************************
25 : : {
26 : : try {
27 [ + + ]: 39 : Xapian::Database db( db_name );
28 [ + - ]: 16 : return db.get_doccount();
29 [ - + ]: 14 : } catch ( const Xapian::Error &e ) {
30 [ + - ][ + - ]: 7 : MWARNING( e.get_description() );
[ + - ][ + - ]
[ + - ]
31 : : }
32 : 7 : return {};
33 : : }
34 : :
35 : : std::string
36 : 7 : piac::add_document( const std::string& author,
37 : : Xapian::TermGenerator& indexer,
38 : : Xapian::WritableDatabase& db,
39 : : Document& ndoc )
40 : : // ****************************************************************************
41 : : // Add document to Xapian database
42 : : //! \param[in] author Author of the database document
43 : : //! \param[in,out] indexer Xapian indexer to use for database indexing
44 : : //! \param[in,out] db Xapian database object to add document to
45 : : //! \param[in,out] ndoc Json document to add
46 : : //! \return Hash of the document added
47 : : // ****************************************************************************
48 : : {
49 [ - + ]: 7 : assert( not author.empty() );
50 [ + - ]: 14 : Xapian::Document doc;
51 [ + - ]: 7 : indexer.set_document( doc );
52 : : // Index each field with a suitable prefix
53 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.title(), 1, "S" );
54 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.description(), 1, "XD" );
55 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.category(), 1, "XC" );
56 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.condition(), 1, "XO" );
57 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.shipping(), 1, "XS" );
58 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.format(), 1, "XF" );
59 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.location(), 1, "XL" );
60 [ + - ][ + - ]: 7 : indexer.index_text( ndoc.keywords(), 1, "K" );
61 : : // Index fields without prefixes for general search
62 [ + - ]: 7 : indexer.index_text( ndoc.title() );
63 [ + - ]: 7 : indexer.increase_termpos();
64 [ + - ]: 7 : indexer.index_text( ndoc.description() );
65 [ + - ]: 7 : indexer.increase_termpos();
66 [ + - ]: 7 : indexer.index_text( ndoc.category() );
67 [ + - ]: 7 : indexer.increase_termpos();
68 [ + - ]: 7 : indexer.index_text( ndoc.condition() );
69 [ + - ]: 7 : indexer.increase_termpos();
70 [ + - ]: 7 : indexer.index_text( ndoc.shipping() );
71 [ + - ]: 7 : indexer.increase_termpos();
72 [ + - ]: 7 : indexer.index_text( ndoc.format() );
73 [ + - ]: 7 : indexer.increase_termpos();
74 [ + - ]: 7 : indexer.index_text( ndoc.location() );
75 [ + - ]: 7 : indexer.increase_termpos();
76 [ + - ]: 7 : indexer.index_text( ndoc.keywords() );
77 : : // Add value fields
78 [ + - ][ + - ]: 7 : doc.add_value( 1, std::to_string( ndoc.price() ) );
79 : : // Generate a hash of the doc fields and store it in the document
80 [ + - ]: 7 : ndoc.author( author );
81 [ + - ]: 14 : auto entry = ndoc.serialize();
82 [ + - ][ + - ]: 7 : ndoc.sha( sha256( entry ) );
83 [ + - ]: 7 : auto sha = ndoc.sha();
84 : : // Ensure each object ends up in the database only once no matter how
85 : : // many times we run the indexer
86 [ + - ][ + - ]: 7 : doc.add_boolean_term( std::to_string( ndoc.id() ) );
87 [ + - ]: 7 : doc.set_data( entry );
88 [ + - ][ + - ]: 7 : doc.add_term( 'Q' + sha );
89 : : // Add Xapian doc to db
90 [ + - ][ + - ]: 7 : db.replace_document( 'Q' + sha, doc );
91 : 14 : return sha;
92 : : }
93 : :
94 : : std::string
95 : 6 : piac::index_db( const std::string& author,
96 : : const std::string& db_name,
97 : : const std::string& input_filename,
98 : : const std::unordered_set< std::string >& my_hashes )
99 : : // ****************************************************************************
100 : : // Index Xapian database
101 : : //! \param[in] author Author of the database document
102 : : //! \param[in] db_name Name of the Xapian database object
103 : : //! \param[in] input_filename File to read JSON data from
104 : : //! \param[in] my_hashes Hashes to check for duplicates when adding documents
105 : : //! \return Info string showing how many documents have been added
106 : : // ****************************************************************************
107 : : {
108 [ - + ]: 6 : assert( not author.empty() );
109 : :
110 [ + - ]: 12 : std::ifstream f( input_filename );
111 [ + - ][ - + ]: 6 : if (not f.good()) {
112 [ - - ]: 0 : return "Cannot open database input file: " + input_filename;
113 : : }
114 : :
115 [ + - ][ + - ]: 6 : MDEBUG( "Indexing " << input_filename );
[ + - ][ + - ]
[ + - ]
116 [ + - ]: 12 : Xapian::WritableDatabase db( db_name, Xapian::DB_CREATE_OR_OPEN );
117 [ + - ]: 12 : Xapian::TermGenerator indexer;
118 [ + - ][ + - ]: 12 : Xapian::Stem stemmer( "english" );
119 [ + - ]: 6 : indexer.set_stemmer( stemmer );
120 [ + - ]: 6 : indexer.set_stemming_strategy( indexer.STEM_SOME_FULL_POS );
121 : 6 : std::size_t numins = 0;
122 : : try {
123 : : // Read json db from file
124 : 12 : Documents ndoc;
125 [ + - ]: 6 : ndoc.deserializeFromFile( input_filename );
126 : : // Insert documents we do not yet have from json into xapian db
127 [ + + ]: 16 : for (auto& d : ndoc.documents()) {
128 [ + - ]: 10 : d.author( author );
129 [ + - ]: 20 : auto entry = d.serialize();
130 [ + - ][ + - ]: 10 : if (my_hashes.find( sha256( entry ) ) == end(my_hashes)) {
[ + + ]
131 [ + - ]: 5 : add_document( author, indexer, db, d );
132 : 5 : ++numins;
133 : : }
134 : : }
135 [ + - ][ + - ]: 6 : MDEBUG( "Indexed " << numins << " entries" );
[ + - ][ + - ]
[ + - ][ + - ]
136 : : // Explicitly commit so that we get to see any errors. WritableDatabase's
137 : : // destructor will commit implicitly (unless we're in a transaction) but
138 : : // will swallow any exceptions produced.
139 [ + - ]: 6 : db.commit();
140 : :
141 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
142 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
143 : : }
144 [ + - ][ + - ]: 12 : return "Added " + std::to_string( numins ) + " entries";
[ + - ]
145 : : }
146 : :
147 : : [[nodiscard]] std::string
148 : 1 : piac::db_query( const std::string& db_name, std::string&& cmd )
149 : : // *****************************************************************************
150 : : // Query Xapian database
151 : : //! \param[in] db_name Name of the Xapian database object
152 : : //! \param[in,out] cmd Query command
153 : : //! \return Result of the database query
154 : : // *****************************************************************************
155 : : {
156 : : try {
157 : :
158 [ + - ][ + - ]: 1 : MDEBUG( "db query: '" << cmd << "'" );
[ + - ][ + - ]
[ + - ][ + - ]
159 : : // Open the database for searching
160 [ + - ]: 2 : Xapian::Database db( db_name );
161 : : // Start an enquire session
162 [ + - ]: 2 : Xapian::Enquire enquire( db );
163 : : // Parse the query string to produce a Xapian::Query object
164 [ + - ]: 2 : Xapian::QueryParser qp;
165 [ + - ][ + - ]: 3 : Xapian::Stem stemmer("english");
166 [ + - ]: 1 : qp.set_stemmer( stemmer );
167 [ + - ]: 1 : qp.set_database( db );
168 [ + - ]: 1 : qp.set_stemming_strategy( Xapian::QueryParser::STEM_SOME );
169 [ + - ]: 2 : Xapian::Query query = qp.parse_query( cmd );
170 [ + - ][ + - ]: 1 : MDEBUG( "parsed query: '" << query.get_description() << "'" );
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
171 : : // Find the top 10 results for the query
172 [ + - ]: 1 : enquire.set_query( query );
173 [ + - ][ + - ]: 1 : MDEBUG( "set query: '" << query.get_description() << "'" );
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
174 [ + - ]: 2 : Xapian::MSet matches = enquire.get_mset( 0, 10 );
175 [ + - ][ + - ]: 1 : MDEBUG( "got matches" );
[ + - ][ + - ]
176 : : // Construct the results
177 [ + - ]: 1 : auto nr = matches.get_matches_estimated();
178 [ + - ][ + - ]: 1 : MDEBUG( "got estimated matches: " << nr );
[ + - ][ + - ]
[ + - ]
179 [ + - ]: 2 : std::stringstream result;
180 [ + - ][ + - ]: 1 : result << nr << " results found.";
181 [ - + ]: 1 : if (nr) {
182 [ - - ][ - - ]: 0 : result << "\nmatches 1-" << matches.size() << ":\n\n";
[ - - ][ - - ]
183 [ - - ][ - - ]: 0 : for (Xapian::MSetIterator i = matches.begin(); i != matches.end(); ++i) {
[ - - ]
184 [ - - ][ - - ]: 0 : MDEBUG( "getting match: " << i.get_rank() );
[ - - ][ - - ]
[ - - ][ - - ]
185 [ - - ][ - - ]: 0 : result << i.get_rank() + 1 << ": " << i.get_weight() << " docid=" << *i
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
186 [ - - ][ - - ]: 0 : << " [" << i.get_document().get_data() << "]\n";
[ - - ][ - - ]
[ - - ]
187 : : }
188 : : }
189 : : //MDEBUG( "results: " + result.str() );
190 [ + - ]: 1 : return result.str();
191 : :
192 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
193 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
194 : : }
195 : 0 : return {};
196 : : }
197 : :
198 : : [[nodiscard]] std::vector< std::string >
199 : 3 : piac::db_get_docs( const std::string& db_name,
200 : : const std::vector< std::string >& hashes )
201 : : // *****************************************************************************
202 : : // Get documents from Xapian database
203 : : //! \param[in] db_name Name of the Xapian database object
204 : : //! \param[in] hashes Hashes of database documents to get retrieve
205 : : //! \return Result of the database query
206 : : // *****************************************************************************
207 : : {
208 : 6 : std::vector< std::string > docs;
209 : : try {
210 : :
211 [ + - ]: 3 : Xapian::Database db( db_name );
212 [ + - ]: 3 : Xapian::doccount dbsize = db.get_doccount();
213 [ - + ]: 3 : if (dbsize == 0) return {};
214 : :
215 [ + + ]: 6 : for (const auto& h : hashes) {
216 [ - + ]: 3 : assert( h.size() == 32 );
217 [ + - ][ + - ]: 6 : auto p = db.postlist_begin( 'Q' + h );
218 [ + - ][ + - ]: 3 : if (p != db.postlist_end( 'Q' + h ))
219 [ + - ][ + - ]: 3 : docs.push_back( db.get_document( *p ).get_data() );
[ + - ][ + - ]
220 : : else
221 [ - - ][ - - ]: 0 : MWARNING( "Document not found: " << hex(h) );
[ - - ][ - - ]
[ - - ][ - - ]
222 : : }
223 : :
224 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
225 [ - - ][ - - ]: 0 : if (e.get_description().find("No such file") == std::string::npos)
226 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
227 : : }
228 : :
229 : 3 : return docs;
230 : : }
231 : :
232 : : std::size_t
233 : 2 : piac::db_put_docs( const std::string& db_name,
234 : : const std::vector< std::string >& docs )
235 : : // *****************************************************************************
236 : : // Put documents to Xapian database
237 : : //! \param[in] db_name Name of the Xapian database object
238 : : //! \param[in] docs Documents to insert to Xapian database
239 : : //! \return Number of documents inserted
240 : : // *****************************************************************************
241 : : {
242 : : try {
243 [ + - ][ + - ]: 2 : MDEBUG( "Inserting & indexing " << docs.size() << " new entries" );
[ + - ][ + - ]
[ + - ][ + - ]
244 [ + - ]: 4 : Xapian::WritableDatabase db( db_name, Xapian::DB_CREATE_OR_OPEN );
245 [ + - ]: 4 : Xapian::TermGenerator indexer;
246 [ + - ][ + - ]: 6 : Xapian::Stem stemmer( "english" );
247 [ + - ]: 2 : indexer.set_stemmer( stemmer );
248 [ + - ]: 2 : indexer.set_stemming_strategy( indexer.STEM_SOME_FULL_POS );
249 : :
250 : : // Insert all documents into xapian db
251 [ + + ]: 4 : for (const auto& d : docs) {
252 : 4 : Document ndoc;
253 [ + - ]: 2 : ndoc.deserialize( d );
254 : : // refuse doc without author
255 [ + - ]: 4 : auto author = ndoc.author();
256 [ + - ][ + - ]: 2 : if (not author.empty()) add_document( author, indexer, db, ndoc );
257 : : }
258 : :
259 [ + - ][ + - ]: 2 : MDEBUG( "Finished indexing " << docs.size() <<
[ + - ][ + - ]
[ + - ][ + - ]
260 : : " new entries, commit to db" );
261 : : // Explicitly commit so that we get to see any errors. WritableDatabase's
262 : : // destructor will commit implicitly (unless we're in a transaction) but
263 : : // will swallow any exceptions produced.
264 [ + - ]: 2 : db.commit();
265 : 2 : return docs.size();
266 : :
267 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
268 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
269 : : }
270 : :
271 : 0 : return 0;
272 : : }
273 : :
274 : : std::string
275 : 3 : piac::db_rm_docs( const std::string& author,
276 : : const std::string& db_name,
277 : : const std::unordered_set< std::string >& hashes_to_delete,
278 : : const std::unordered_set< std::string >& my_hashes )
279 : : // *****************************************************************************
280 : : // Remove documents from Xapian database
281 : : //! \param[in] author Author of the database document
282 : : //! \param[in] db_name Name of the Xapian database object
283 : : //! \param[in] hashes_to_delete Hashes of documents to delete
284 : : //! \param[in] my_hashes Hashes to check for duplicates when removing documents
285 : : //! \return Info on number of documents removed
286 : : // *****************************************************************************
287 : : {
288 : 3 : std::size_t numrm = 0;
289 : : try {
290 : :
291 [ + - ]: 3 : Xapian::WritableDatabase db( db_name );
292 [ + - ]: 3 : Xapian::doccount dbsize = db.get_doccount();
293 [ - + ][ - - ]: 3 : if (dbsize == 0) return "no docs";
294 : :
295 [ + + ]: 16 : for (const auto& h : my_hashes) {
296 [ - + ]: 13 : assert( h.size() == 32 );
297 [ + - ][ + - ]: 13 : auto p = db.postlist_begin( 'Q' + h );
298 [ + - ][ + - ]: 39 : if ( p != db.postlist_end( 'Q' + h ) &&
[ + + ][ + - ]
[ + + ][ - - ]
[ - - ]
299 [ + - ][ + - ]: 26 : hashes_to_delete.find(hex(h)) != end(hashes_to_delete) )
[ + - ][ + - ]
[ - - ]
300 : : {
301 [ + - ][ + - ]: 3 : auto entry = db.get_document( *p ).get_data();
[ + - ]
302 : 3 : Document ndoc;
303 [ + - ]: 3 : ndoc.deserialize( entry );
304 [ + - ]: 3 : if (author == ndoc.author()) {
305 [ + - ][ + - ]: 3 : MDEBUG( "db rm" + sha256(h) );
[ + - ][ + - ]
[ + - ][ + - ]
306 [ + - ][ + - ]: 3 : db.delete_document( 'Q' + h );
307 : 3 : ++numrm;
308 : : } else {
309 [ - - ][ - - ]: 0 : MDEBUG( "db rm auth: " + hex(author) + " != " + hex(ndoc.author()) );
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
310 [ - - ]: 0 : return "db rm: author != user";
311 : : }
312 : : }
313 : : }
314 : :
315 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
316 [ - - ][ - - ]: 0 : if (e.get_description().find("No such file") == std::string::npos)
317 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
318 : : }
319 : :
320 [ + - ][ + - ]: 6 : return "Removed " + std::to_string( numrm ) + " entries";
321 : : }
322 : :
323 : : [[nodiscard]] std::vector< std::string >
324 : 31 : piac::db_list_hash( const std::string& db_name, bool inhex )
325 : : // *****************************************************************************
326 : : // List hashes from Xapian database
327 : : //! \param[in] db_name Name of the Xapian database object
328 : : //! \param[in] inhex True to list hashes hex-encoded
329 : : //! \return List of hashes
330 : : // *****************************************************************************
331 : : {
332 : 62 : std::vector< std::string > hashes;
333 : : try {
334 : :
335 [ + + ]: 31 : Xapian::Database db( db_name );
336 [ + - ]: 24 : Xapian::doccount dbsize = db.get_doccount();
337 [ - + ]: 24 : if (dbsize == 0) return {};
338 : :
339 [ + - ][ + + ]: 132 : for (auto it = db.postlist_begin({}); it != db.postlist_end({}); ++it) {
[ + - ]
340 [ + - ][ + - ]: 168 : auto entry = db.get_document( *it ).get_data();
[ + - ]
341 [ + - ]: 84 : auto digest = sha256( entry );
342 [ + + ][ + - ]: 84 : hashes.emplace_back( inhex ? hex(digest) : digest );
[ + - ][ + - ]
343 : : }
344 : :
345 [ - + ]: 14 : } catch ( const Xapian::Error &e ) {
346 [ + - ][ - + ]: 7 : if (e.get_description().find("No such file") == std::string::npos)
347 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
348 : : }
349 : :
350 : 31 : return hashes;
351 : : }
352 : :
353 : : [[nodiscard]] std::vector< std::string >
354 : 1 : piac::db_list_doc( const std::string& db_name )
355 : : // *****************************************************************************
356 : : // List documents from Xapian database
357 : : //! \param[in] db_name Name of the Xapian database object
358 : : //! \return List of documents
359 : : // *****************************************************************************
360 : : {
361 : 2 : std::vector< std::string > docs;
362 : : try {
363 : :
364 [ + - ]: 1 : Xapian::Database db( db_name );
365 [ + - ]: 1 : Xapian::doccount dbsize = db.get_doccount();
366 [ - + ]: 1 : if (dbsize == 0) return {};
367 : :
368 [ + - ][ + + ]: 7 : for (auto it = db.postlist_begin({}); it != db.postlist_end({}); ++it) {
[ + - ]
369 [ + - ][ + - ]: 10 : auto entry = db.get_document( *it ).get_data();
[ + - ]
370 [ + - ]: 10 : auto digest = sha256( entry );
371 : 5 : Document d;
372 [ + - ]: 5 : d.deserialize( entry );
373 [ + - ][ + - ]: 5 : d.author( hex( d.author() ) );
374 [ + - ][ + - ]: 5 : docs.emplace_back( hex( digest ) + ": " + d.serialize() );
[ + - ][ + - ]
[ + - ]
375 : : }
376 : :
377 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
378 [ - - ][ - - ]: 0 : if (e.get_description().find("No such file") == std::string::npos)
379 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
380 : : }
381 : :
382 : 1 : return docs;
383 : : }
384 : :
385 : : [[nodiscard]] std::size_t
386 : 1 : piac::db_list_numuser( const std::string& db_name )
387 : : // *****************************************************************************
388 : : // List number of unique users in Xapian database
389 : : //! \param[in] db_name Name of the Xapian database object
390 : : //! \return Number of unique users created documents in database
391 : : // *****************************************************************************
392 : : {
393 : : try {
394 : :
395 [ + - ]: 2 : Xapian::Database db( db_name );
396 [ + - ]: 1 : Xapian::doccount dbsize = db.get_doccount();
397 [ - + ]: 1 : if (dbsize == 0) return {};
398 : :
399 : 2 : std::unordered_set< std::string > user;
400 [ + - ][ + + ]: 7 : for (auto it = db.postlist_begin({}); it != db.postlist_end({}); ++it) {
[ + - ]
401 [ + - ][ + - ]: 10 : auto entry = db.get_document( *it ).get_data();
[ + - ]
402 : 10 : Document d;
403 [ + - ]: 5 : d.deserialize( entry );
404 [ + - ]: 5 : user.insert( d.author() );
405 : : }
406 : 1 : return user.size();
407 : :
408 : :
409 [ - - ]: 0 : } catch ( const Xapian::Error &e ) {
410 [ - - ][ - - ]: 0 : if (e.get_description().find("No such file") == std::string::npos)
411 [ - - ][ - - ]: 0 : MERROR( e.get_description() );
[ - - ][ - - ]
[ - - ]
412 : : }
413 : :
414 : 0 : return {};
415 : : }
416 : :
417 : : std::string
418 : 6 : piac::db_add( const std::string& author,
419 : : const std::string& db_name,
420 : : std::string&& cmd,
421 : : const std::unordered_set< std::string >& my_hashes )
422 : : // *****************************************************************************
423 : : // Add documents to Xapian database
424 : : //! \param[in] author Author of the database document
425 : : //! \param[in] db_name Name of the Xapian database object
426 : : //! \param[in,out] cmd Add command
427 : : //! \param[in] my_hashes Hashes to check for duplicates when adding documents
428 : : //! \return Info string after add database operation
429 : : // *****************************************************************************
430 : : {
431 : 6 : trim( cmd );
432 [ + - ][ + - ]: 6 : MDEBUG( "db add " + cmd );
[ + - ][ + - ]
433 [ - + ]: 6 : assert( not author.empty() );
434 [ + - ][ + - ]: 6 : if (cmd[0]=='j' && cmd[1]=='s' && cmd[2]=='o' && cmd[3]=='n') {
[ + - ][ + - ]
[ + - ]
435 : 6 : cmd.erase( 0, 5 );
436 [ + - ][ + - ]: 6 : MDEBUG( "Add json file: '" << cmd << "' to db" );
[ + - ][ + - ]
[ + - ]
437 : 6 : return index_db( author, db_name, cmd, my_hashes );
438 : : }
439 [ - - ]: 0 : return "unknown cmd";
440 : : }
441 : :
442 : : std::string
443 : 3 : piac::db_rm( const std::string& author,
444 : : const std::string& db_name,
445 : : std::string&& cmd,
446 : : const std::unordered_set< std::string >& my_hashes )
447 : : // *****************************************************************************
448 : : // Remove documents from Xapian database
449 : : //! \param[in] author Author of the database document
450 : : //! \param[in] db_name Name of the Xapian database object
451 : : //! \param[in,out] cmd Remove command
452 : : //! \param[in] my_hashes Hashes to check for duplicates when removing documents
453 : : //! \return Info string after remove database operation
454 : : // *****************************************************************************
455 : : {
456 : 3 : trim( cmd );
457 [ + - ][ + - ]: 3 : MDEBUG( "db rm " + cmd );
[ + - ][ + - ]
458 [ - + ]: 3 : assert( not author.empty() );
459 [ + - ]: 3 : if (not cmd.empty()) {
460 [ + - ]: 6 : auto h = tokenize( cmd );
461 [ + - ]: 6 : std::unordered_set< std::string > hashes_to_delete( begin(h), end(h) );
462 [ + - ]: 3 : return db_rm_docs( author, db_name, hashes_to_delete, my_hashes );
463 : : }
464 [ - - ]: 0 : return "unknown cmd";
465 : : }
466 : :
467 : : std::string
468 : 12 : piac::db_list( const std::string& db_name, std::string&& cmd )
469 : : // *****************************************************************************
470 : : // List Xapian database
471 : : //! \param[in] db_name Name of the Xapian database object
472 : : //! \param[in,out] cmd List command
473 : : //! \return List of items queried from database
474 : : // *****************************************************************************
475 : : {
476 : 12 : trim( cmd );
477 [ + - ][ + - ]: 12 : MDEBUG( "db list " + cmd );
[ + - ][ + - ]
478 : :
479 [ + + ]: 12 : if (cmd.empty()) {
480 : :
481 [ + - ]: 2 : auto docs = db_list_doc( db_name );
482 : 1 : std::string result( "Number of documents: " +
483 [ + - ][ + - ]: 2 : std::to_string( docs.size() ) + '\n' );
[ + - ]
484 [ + + ][ + - ]: 6 : for (auto&& d : docs) result += std::move(d) + '\n';
[ + - ]
485 : 1 : result.pop_back();
486 : 1 : return result;
487 : :
488 [ + - ][ + - ]: 13 : } else if (cmd[0]=='n' && cmd[1]=='u' && cmd[2]=='m' && cmd[3]=='d' &&
[ + + ]
489 [ + + ][ + - ]: 13 : cmd[4]=='o' && cmd[5]=='c')
[ + - ][ + + ]
490 : : {
491 : :
492 [ + - ][ + - ]: 2 : return "Number of documents: " + std::to_string( get_doccount( db_name ) );
[ + - ]
493 : :
494 [ + - ][ + - ]: 11 : } else if (cmd[0]=='n' && cmd[1]=='u' && cmd[2]=='m' && cmd[3]=='u' &&
[ + - ]
495 [ + + ][ + - ]: 11 : cmd[4]=='s' && cmd[5]=='r')
[ + - ][ + + ]
496 : : {
497 : :
498 [ + - ]: 2 : return "Number of users: " + std::to_string( db_list_numuser( db_name ) );
499 : :
500 [ + - ][ + - ]: 9 : } else if (cmd[0]=='h' && cmd[1]=='a' && cmd[2]=='s' && cmd[3]=='h') {
[ + - ][ + - ]
[ + - ]
501 : :
502 [ + - ]: 9 : cmd.erase( 0, 5 );
503 [ + - ]: 18 : auto hashes = db_list_hash( db_name, /* inhex = */ true );
504 : 9 : std::string result( "Number of documents: " +
505 [ + - ][ + - ]: 18 : std::to_string( hashes.size() ) + '\n' );
[ + - ]
506 [ + + ][ + - ]: 47 : for (auto&& h : hashes) result += std::move(h) + '\n';
[ + - ]
507 : 9 : result.pop_back();
508 : 9 : return result;
509 : :
510 : : }
511 : :
512 [ - - ]: 0 : return "unknown cmd";
513 : : }
|