1 /*
2 SPDX-FileCopyrightText: 2016 Pino Toscano <pino@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "xapiancommon.h"
8
9 #include <QCoreApplication>
10 #include <QCommandLineParser>
11 #include <QLoggingCategory>
12 #include <QVector>
13
14 #include <iostream>
15
16 namespace {
17
18 Q_LOGGING_CATEGORY( LOG, "org.kde.khelpcenter.xapian.search", QtWarningMsg )
19
20 }
21
queryFromWordlist(const QString & words,Xapian::Query::op op)22 Xapian::Query queryFromWordlist( const QString& words, Xapian::Query::op op )
23 {
24 QVector<std::string> wordlist;
25 const QStringList splitlist = words.split( QLatin1Char( '+' ) );
26 wordlist.reserve( splitlist.size() );
27 for ( const QString& word : splitlist ) {
28 wordlist.append( word.toStdString() );
29 }
30
31 return Xapian::Query( op, wordlist.constBegin(), wordlist.constEnd() );
32 }
33
main(int argc,char * argv[])34 int main( int argc, char *argv[] )
35 {
36 QCoreApplication app( argc, argv );
37
38 QCommandLineParser parser;
39 const QCommandLineOption indexdirOption( QStringLiteral("indexdir"), QStringLiteral("Index directory"), QStringLiteral("dir") );
40 parser.addOption( indexdirOption );
41 const QCommandLineOption identifierOption( QStringLiteral("identifier"), QStringLiteral("Index identifier"), QStringLiteral("identifier") );
42 parser.addOption( identifierOption );
43 const QCommandLineOption wordsOption( QStringLiteral("words"), QStringLiteral("Words to search"), QStringLiteral("words") );
44 parser.addOption( wordsOption );
45 const QCommandLineOption methodOption( QStringLiteral("method"), QStringLiteral("Method"), QStringLiteral("and|or") );
46 parser.addOption( methodOption );
47 const QCommandLineOption maxnumOption( QStringLiteral("maxnum"), QStringLiteral("Maximum number of results"), QStringLiteral("maxnum") );
48 parser.addOption( maxnumOption );
49 const QCommandLineOption langOption( QStringLiteral("lang"), QStringLiteral("Language"), QStringLiteral("lang") );
50 parser.addOption( langOption );
51
52 parser.process( app );
53
54 const QString indexdir = parser.value( indexdirOption );
55 const QString identifier = parser.value( identifierOption );
56 const QString words = parser.value( wordsOption );
57 const QString method = parser.value( methodOption );
58 const QString maxnumString = parser.value( maxnumOption );
59 if ( indexdir.isEmpty() || identifier.isEmpty() || words.isEmpty() || method.isEmpty() ) {
60 qCCritical(LOG) << "Missing arguments.";
61 parser.showHelp( 1 );
62 }
63
64 Xapian::Query::op op;
65 if ( method == QLatin1String( "and" ) ) {
66 op = Xapian::Query::OP_AND;
67 } else if ( method == QLatin1String( "or" ) ) {
68 op = Xapian::Query::OP_OR;
69 } else {
70 qCCritical(LOG) << "Unrecognized method:" << method;
71 parser.showHelp( 1 );
72 }
73
74 bool ok;
75 const Xapian::doccount maxnum = maxnumString.toUInt( &ok );
76 if ( !ok ) {
77 qCCritical(LOG) << "--maxnum is not a number";
78 parser.showHelp( 1 );
79 }
80
81 QString lang = parser.value( langOption );
82 if ( lang.isEmpty() || lang == QLatin1String( "C" ) ) {
83 lang = QStringLiteral("en");
84 }
85
86 Xapian::Database db;
87
88 try {
89 db = openDb( indexdir + QLatin1Char('/') + identifier );
90 } catch ( const DatabaseVersionMismatch& e ) {
91 qCWarning(LOG) << "Own version mismatch in Xapian DB: found" << e.version << "vs wanted" << e.refVersion;
92 return 1;
93 } catch ( const Xapian::Error& e ) {
94 qCCritical(LOG) << "Xapian error:" << e.get_description();
95 return 1;
96 }
97
98 try {
99 Xapian::Query query( Xapian::Query::OP_AND, Xapian::Query( "L" + lang.toStdString() ), queryFromWordlist( words, op ) );
100 qCDebug(LOG) << "query:" << query.get_description();
101
102 Xapian::Enquire enquire( db );
103 enquire.set_query( query );
104
105 const Xapian::MSet mset = enquire.get_mset( 0, maxnum );
106
107 std::cout << "<ul>" << std::endl;
108
109 qCDebug(LOG) << "got" << mset.size() << "results";
110 for ( Xapian::MSetIterator i = mset.begin(); i != mset.end(); ++i ) {
111 const Xapian::Document doc = i.get_document();
112
113 std::string uid;
114 std::string html;
115 getDocInfo( doc, nullptr, &uid, &html );
116 const std::string title = doc.get_value( VALUE_TITLE );
117
118 const std::string::size_type slash = uid.find_last_of( '/' );
119 const std::string html_id = slash != std::string::npos
120 ? uid.substr( 0, slash + 1 ) + html
121 : html;
122 const std::string partial_id = html_id.substr(0, html_id.rfind("/"));
123
124 std::cout << "<li><a href=\"help:/" << html_id << "\">" << partial_id
125 << " - " << title << "</a></li>" << std::endl;
126 }
127
128 std::cout << "</ul>" << std::endl;
129
130 } catch ( const Xapian::Error& e ) {
131 qCCritical(LOG) << "Xapian error: " << e.get_description();
132 return 1;
133 }
134
135 return 0;
136 }
137