16
16
17
17
using namespace Qt ::Literals::StringLiterals;
18
18
19
- QPair< QString, QList<SourceExcerpt>> BraveSearch::search (const QString &apiKey, const QString &query, int topK, unsigned long timeout)
19
+ QString BraveSearch::run (const QJsonObject ¶meters, qint64 timeout)
20
20
{
21
+ const QString apiKey = parameters[" apiKey" ].toString ();
22
+ const QString query = parameters[" query" ].toString ();
23
+ const int count = parameters[" count" ].toInt ();
21
24
QThread workerThread;
22
25
BraveAPIWorker worker;
23
26
worker.moveToThread (&workerThread);
24
27
connect (&worker, &BraveAPIWorker::finished, &workerThread, &QThread::quit, Qt::DirectConnection);
25
- connect (this , &BraveSearch::request, &worker, &BraveAPIWorker::request, Qt::QueuedConnection);
28
+ connect (&workerThread, &QThread::started, [&worker, apiKey, query, count]() {
29
+ worker.request (apiKey, query, count);
30
+ });
26
31
workerThread.start ();
27
- emit request (apiKey, query, topK);
28
32
workerThread.wait (timeout);
29
33
workerThread.quit ();
30
34
workerThread.wait ();
@@ -34,174 +38,97 @@ QPair<QString, QList<SourceExcerpt>> BraveSearch::search(const QString &apiKey,
34
38
void BraveAPIWorker::request (const QString &apiKey, const QString &query, int topK)
35
39
{
36
40
m_topK = topK;
41
+
42
+ // Documentation on the brave web search:
43
+ // https://api.search.brave.com/app/documentation/web-search/get-started
37
44
QUrl jsonUrl (" https://api.search.brave.com/res/v1/web/search" );
45
+
46
+ // Documentation on the query options:
47
+ // https://api.search.brave.com/app/documentation/web-search/query
38
48
QUrlQuery urlQuery;
39
49
urlQuery.addQueryItem (" q" , query);
50
+ urlQuery.addQueryItem (" count" , QString::number (topK));
51
+ urlQuery.addQueryItem (" result_filter" , " web" );
52
+ urlQuery.addQueryItem (" extra_snippets" , " true" );
40
53
jsonUrl.setQuery (urlQuery);
41
54
QNetworkRequest request (jsonUrl);
42
55
QSslConfiguration conf = request.sslConfiguration ();
43
56
conf.setPeerVerifyMode (QSslSocket::VerifyNone);
44
57
request.setSslConfiguration (conf);
45
-
46
58
request.setRawHeader (" X-Subscription-Token" , apiKey.toUtf8 ());
47
- // request.setRawHeader("Accept-Encoding", "gzip");
48
59
request.setRawHeader (" Accept" , " application/json" );
49
-
50
60
m_networkManager = new QNetworkAccessManager (this );
51
61
QNetworkReply *reply = m_networkManager->get (request);
52
62
connect (qGuiApp, &QCoreApplication::aboutToQuit, reply, &QNetworkReply::abort );
53
63
connect (reply, &QNetworkReply::finished, this , &BraveAPIWorker::handleFinished);
54
64
connect (reply, &QNetworkReply::errorOccurred, this , &BraveAPIWorker::handleErrorOccurred);
55
65
}
56
66
57
- static QPair< QString, QList<SourceExcerpt>> cleanBraveResponse (const QByteArray& jsonResponse, qsizetype topK = 1 )
67
+ static QString cleanBraveResponse (const QByteArray& jsonResponse, qsizetype topK = 1 )
58
68
{
69
+ // This parses the response from brave and formats it in json that conforms to the de facto
70
+ // standard in SourceExcerpts::fromJson(...)
59
71
QJsonParseError err;
60
72
QJsonDocument document = QJsonDocument::fromJson (jsonResponse, &err);
61
73
if (err.error != QJsonParseError::NoError) {
62
- qWarning () << " ERROR: Couldn't parse: " << jsonResponse << err.errorString ();
63
- return QPair< QString, QList<SourceExcerpt>> ();
74
+ qWarning () << " ERROR: Couldn't parse brave response : " << jsonResponse << err.errorString ();
75
+ return QString ();
64
76
}
65
77
78
+ QString query;
66
79
QJsonObject searchResponse = document.object ();
67
80
QJsonObject cleanResponse;
68
- QString query;
69
81
QJsonArray cleanArray;
70
82
71
- QList<SourceExcerpt> infos;
72
-
73
83
if (searchResponse.contains (" query" )) {
74
84
QJsonObject queryObj = searchResponse[" query" ].toObject ();
75
- if (queryObj.contains (" original" )) {
85
+ if (queryObj.contains (" original" ))
76
86
query = queryObj[" original" ].toString ();
77
- }
78
87
}
79
88
80
89
if (searchResponse.contains (" mixed" )) {
81
90
QJsonObject mixedResults = searchResponse[" mixed" ].toObject ();
82
91
QJsonArray mainResults = mixedResults[" main" ].toArray ();
92
+ QJsonObject resultsObject = searchResponse[" web" ].toObject ();
93
+ QJsonArray resultsArray = resultsObject[" results" ].toArray ();
83
94
84
- for (int i = 0 ; i < std::min (mainResults.size (), topK ); ++i) {
95
+ for (int i = 0 ; i < std::min (mainResults.size (), resultsArray. size () ); ++i) {
85
96
QJsonObject m = mainResults[i].toObject ();
86
97
QString r_type = m[" type" ].toString ();
87
- int idx = m[" index" ].toInt ();
88
- QJsonObject resultsObject = searchResponse[r_type].toObject ();
89
- QJsonArray resultsArray = resultsObject[" results" ].toArray ();
90
-
91
- QJsonValue cleaned;
92
- SourceExcerpt info;
93
- if (r_type == " web" ) {
94
- // For web data - add a single output from the search
95
- QJsonObject resultObj = resultsArray[idx].toObject ();
96
- QStringList selectedKeys = {" type" , " title" , " url" , " description" , " date" , " extra_snippets" };
97
- QJsonObject cleanedObj;
98
- for (const auto & key : selectedKeys) {
99
- if (resultObj.contains (key)) {
100
- cleanedObj.insert (key, resultObj[key]);
101
- }
102
- }
103
-
104
- QStringList textKeys = {" description" , " extra_snippets" };
105
- QJsonObject textObj;
106
- for (const auto & key : textKeys) {
107
- if (resultObj.contains (key)) {
108
- textObj.insert (key, resultObj[key]);
109
- }
98
+ Q_ASSERT (r_type == " web" );
99
+ const int idx = m[" index" ].toInt ();
100
+
101
+ QJsonObject resultObj = resultsArray[idx].toObject ();
102
+ QStringList selectedKeys = {" type" , " title" , " url" , " description" };
103
+ QJsonObject result;
104
+ for (const auto & key : selectedKeys)
105
+ if (resultObj.contains (key))
106
+ result.insert (key, resultObj[key]);
107
+
108
+ if (resultObj.contains (" page_age" ))
109
+ result.insert (" date" , resultObj[" page_age" ]);
110
+
111
+ QJsonArray excerpts;
112
+ if (resultObj.contains (" extra_snippets" )) {
113
+ QJsonArray snippets = resultObj[" extra_snippets" ].toArray ();
114
+ for (int i = 0 ; i < snippets.size (); ++i) {
115
+ QString snippet = snippets[i].toString ();
116
+ QJsonObject excerpt;
117
+ excerpt.insert (" text" , snippet);
118
+ excerpts.append (excerpt);
110
119
}
111
-
112
- QJsonDocument textObjDoc (textObj);
113
- info.date = resultObj[" date" ].toString ();
114
- info.text = textObjDoc.toJson (QJsonDocument::Indented);
115
- info.url = resultObj[" url" ].toString ();
116
- QJsonObject meta_url = resultObj[" meta_url" ].toObject ();
117
- info.favicon = meta_url[" favicon" ].toString ();
118
- info.title = resultObj[" title" ].toString ();
119
-
120
- cleaned = cleanedObj;
121
- } else if (r_type == " faq" ) {
122
- // For faq data - take a list of all the questions & answers
123
- QStringList selectedKeys = {" type" , " question" , " answer" , " title" , " url" };
124
- QJsonArray cleanedArray;
125
- for (const auto & q : resultsArray) {
126
- QJsonObject qObj = q.toObject ();
127
- QJsonObject cleanedObj;
128
- for (const auto & key : selectedKeys) {
129
- if (qObj.contains (key)) {
130
- cleanedObj.insert (key, qObj[key]);
131
- }
132
- }
133
- cleanedArray.append (cleanedObj);
134
- }
135
- cleaned = cleanedArray;
136
- } else if (r_type == " infobox" ) {
137
- QJsonObject resultObj = resultsArray[idx].toObject ();
138
- QStringList selectedKeys = {" type" , " title" , " url" , " description" , " long_desc" };
139
- QJsonObject cleanedObj;
140
- for (const auto & key : selectedKeys) {
141
- if (resultObj.contains (key)) {
142
- cleanedObj.insert (key, resultObj[key]);
143
- }
144
- }
145
- cleaned = cleanedObj;
146
- } else if (r_type == " videos" ) {
147
- QStringList selectedKeys = {" type" , " url" , " title" , " description" , " date" };
148
- QJsonArray cleanedArray;
149
- for (const auto & q : resultsArray) {
150
- QJsonObject qObj = q.toObject ();
151
- QJsonObject cleanedObj;
152
- for (const auto & key : selectedKeys) {
153
- if (qObj.contains (key)) {
154
- cleanedObj.insert (key, qObj[key]);
155
- }
156
- }
157
- cleanedArray.append (cleanedObj);
158
- }
159
- cleaned = cleanedArray;
160
- } else if (r_type == " locations" ) {
161
- QStringList selectedKeys = {" type" , " title" , " url" , " description" , " coordinates" , " postal_address" , " contact" , " rating" , " distance" , " zoom_level" };
162
- QJsonArray cleanedArray;
163
- for (const auto & q : resultsArray) {
164
- QJsonObject qObj = q.toObject ();
165
- QJsonObject cleanedObj;
166
- for (const auto & key : selectedKeys) {
167
- if (qObj.contains (key)) {
168
- cleanedObj.insert (key, qObj[key]);
169
- }
170
- }
171
- cleanedArray.append (cleanedObj);
172
- }
173
- cleaned = cleanedArray;
174
- } else if (r_type == " news" ) {
175
- QStringList selectedKeys = {" type" , " title" , " url" , " description" };
176
- QJsonArray cleanedArray;
177
- for (const auto & q : resultsArray) {
178
- QJsonObject qObj = q.toObject ();
179
- QJsonObject cleanedObj;
180
- for (const auto & key : selectedKeys) {
181
- if (qObj.contains (key)) {
182
- cleanedObj.insert (key, qObj[key]);
183
- }
184
- }
185
- cleanedArray.append (cleanedObj);
186
- }
187
- cleaned = cleanedArray;
188
- } else {
189
- cleaned = QJsonValue ();
190
120
}
191
-
192
- infos.append (info);
193
- cleanArray.append (cleaned);
121
+ result.insert (" excerpts" , excerpts);
122
+ cleanArray.append (QJsonValue (result));
194
123
}
195
124
}
196
125
197
126
cleanResponse.insert (" query" , query);
198
- cleanResponse.insert (" top_k " , cleanArray);
127
+ cleanResponse.insert (" results " , cleanArray);
199
128
QJsonDocument cleanedDoc (cleanResponse);
200
-
201
129
// qDebug().noquote() << document.toJson(QJsonDocument::Indented);
202
130
// qDebug().noquote() << cleanedDoc.toJson(QJsonDocument::Indented);
203
-
204
- return qMakePair (cleanedDoc.toJson (QJsonDocument::Indented), infos);
131
+ return cleanedDoc.toJson (QJsonDocument::Compact);
205
132
}
206
133
207
134
void BraveAPIWorker::handleFinished ()
0 commit comments