OpenTREP Logo  0.09.01
C++ Open Travel Request Parsing Library
Loading...
Searching...
No Matches
QuerySlices.cpp
Go to the documentation of this file.
1// //////////////////////////////////////////////////////////////////////
2// Import section
3// //////////////////////////////////////////////////////////////////////
4// STL
5#include <cassert>
6#include <sstream>
7#include <set>
8// OpenTrep
16
17namespace OPENTREP {
18
19 // //////////////////////////////////////////////////////////////////////
20 QuerySlices::QuerySlices (const Xapian::Database& iDatabase,
21 const TravelQuery_T& iQueryString,
22 const OTransliterator& iTransliterator)
23 : _database (iDatabase), _queryString (iQueryString) {
24 init (iTransliterator);
25 }
26
27 // //////////////////////////////////////////////////////////////////////
30
31 // //////////////////////////////////////////////////////////////////////
32 void QuerySlices::push_back (const StringPartition& iStringPartition) {
33 if (iStringPartition.empty() == false) {
34 _slices.push_back (iStringPartition);
35 }
36 }
37
38 // //////////////////////////////////////////////////////////////////////
39 size_t QuerySlices::size() const {
40 return _slices.size();
41 }
42
43 // //////////////////////////////////////////////////////////////////////
44 bool QuerySlices::empty() const {
45 return _slices.empty();
46 }
47
48 // //////////////////////////////////////////////////////////////////////
50 _slices.clear();
51 }
52
53 // //////////////////////////////////////////////////////////////////////
54 std::string QuerySlices::describeKey() const {
55 std::ostringstream oStr;
56 oStr << "";
57 return oStr.str();
58 }
59
60 // //////////////////////////////////////////////////////////////////////
61 std::string QuerySlices::describe() const {
62 std::ostringstream oStr;
63 oStr << describeKey();
64
65 //
66 oStr << "[ ";
67
68 short idx_sublist = 0;
69 for (StringPartitionList_T::const_iterator itSlice = _slices.begin();
70 itSlice != _slices.end(); ++itSlice, ++idx_sublist) {
71 //
72 if (idx_sublist != 0) {
73 oStr << "; ";
74 }
75
76 //
77 const StringPartition& lStringPartition = *itSlice;
78
79 //
80 oStr << idx_sublist << ". " << lStringPartition;
81 }
82
83 //
84 oStr << " ]";
85
86 return oStr.str();
87 }
88
89 // //////////////////////////////////////////////////////////////////////
90 void QuerySlices::toStream (std::ostream& ioOut) const {
91 ioOut << describe();
92 }
93
94 // //////////////////////////////////////////////////////////////////////
95 void QuerySlices::fromStream (std::istream& ioIn) {
96 }
97
109 // //////////////////////////////////////////////////////////////////////
110 static unsigned int calculateEditDistance (const TravelQuery_T& iPhrase) {
111 NbOfErrors_T oEditDistance = 2;
112
113 const NbOfErrors_T lQueryStringSize = iPhrase.size();
114
115 oEditDistance = lQueryStringSize / K_DEFAULT_SIZE_FOR_SPELLING_ERROR_UNIT;
116 return oEditDistance;
117 }
118
122 // //////////////////////////////////////////////////////////////////////
123 bool doesMatch (const Xapian::Database& iDatabase,
124 const std::string& iWord1, const std::string& iWord2) {
125 bool oDoesMatch = false;
126
127 //
128 std::ostringstream oStr;
129 oStr << iWord1 << " " << iWord2;
130 const std::string lQueryString (oStr.str());
131
132 // Catch any Xapian::Error exceptions thrown
133 Xapian::MSet lMatchingSet;
134 try {
135
136 // Build the query object
137 Xapian::QueryParser lQueryParser;
138 lQueryParser.set_database (iDatabase);
139
145 // lQueryParser.set_default_op (Xapian::Query::OP_ADJ);
146 lQueryParser.set_default_op (Xapian::Query::OP_PHRASE);
147
148 // DEBUG
149 /*
150 OPENTREP_LOG_DEBUG ("Query parser `" << lQueryParser.get_description()
151 << "'");
152 */
153
154 // DEBUG
155 // OPENTREP_LOG_DEBUG (" --------");
156
157 // Start an enquire session
158 Xapian::Enquire enquire (iDatabase);
159 enquire.set_weighting_scheme(Xapian::BM25Weight(1.0, 0.0, 1.0, 0.0, 0.5));
160
167 const Xapian::Query& lXapianQuery =
168 lQueryParser.parse_query (lQueryString,
169 Xapian::QueryParser::FLAG_BOOLEAN
170 | Xapian::QueryParser::FLAG_PHRASE
171 | Xapian::QueryParser::FLAG_LOVEHATE);
172
173 // Give the query object to the enquire session
174 enquire.set_query (lXapianQuery);
175
176 // Get the top 20 results of the query
177 lMatchingSet = enquire.get_mset (0, K_DEFAULT_XAPIAN_MATCHING_SET_SIZE);
178
179 // Display the results
180 int nbMatches = lMatchingSet.size();
181
182 // DEBUG
183 /*
184 OPENTREP_LOG_DEBUG (" Query string: `" << lQueryString
185 << "', i.e.: `" << lXapianQuery.get_description()
186 << "' => " << nbMatches << " result(s) found");
187 */
188
189 if (nbMatches != 0) {
190 // There has been a matching
191 oDoesMatch = true;
192
193 // DEBUG
194 /*
195 OPENTREP_LOG_DEBUG (" Query string: `" << lQueryString
196 << "' provides " << nbMatches << " exact matches.");
197 */
198
199 return oDoesMatch;
200 }
201 assert (lMatchingSet.empty() == true);
202
208 const NbOfErrors_T& lAllowableEditDistance =
209 calculateEditDistance (lQueryString);
210
211 // Let Xapian find a spelling correction (if any)
212 const std::string& lCorrectedString =
213 iDatabase.get_spelling_suggestion (lQueryString, lAllowableEditDistance);
214
215 // If the correction is no better than the original string, there is
216 // no need to go further: there is no match.
217 if (lCorrectedString.empty() == true || lCorrectedString == lQueryString) {
218 // DEBUG
219 /*
220 OPENTREP_LOG_DEBUG (" Query string: `"
221 << lQueryString << "' provides no match, "
222 << "and there is no spelling suggestion, "
223 << "even with an edit distance of "
224 << lAllowableEditDistance);
225 */
226
227 // No match
228 return oDoesMatch;
229 }
230 assert (lCorrectedString.empty() == false
231 && lCorrectedString != lQueryString);
232
233 // Calculate the effective (Levenshtein) edit distance/error
234 const NbOfErrors_T& lEditDistance =
235 Levenshtein::getDistance (lQueryString, lCorrectedString);
236
244 const Xapian::Query& lCorrectedXapianQuery =
245 lQueryParser.parse_query (lCorrectedString,
246 Xapian::QueryParser::FLAG_BOOLEAN
247 | Xapian::QueryParser::FLAG_PHRASE
248 | Xapian::QueryParser::FLAG_LOVEHATE);
249
250 enquire.set_query (lCorrectedXapianQuery);
251 lMatchingSet = enquire.get_mset (0, K_DEFAULT_XAPIAN_MATCHING_SET_SIZE);
252
253 // Display the results
254 nbMatches = lMatchingSet.size();
255
256 // DEBUG
257 /*
258 OPENTREP_LOG_DEBUG (" Corrected query string: `" << lCorrectedString
259 << "', i.e.: `"
260 << lCorrectedXapianQuery.get_description()
261 << "' => " << nbMatches << " result(s) found");
262 */
263
264 if (nbMatches != 0) {
265 // DEBUG
266 /*
267 OPENTREP_LOG_DEBUG (" Query string: `"
268 << lQueryString << "', spelling suggestion: `"
269 << lCorrectedString
270 << "', with a Levenshtein edit distance of "
271 << lEditDistance
272 << " over an allowable edit distance of "
273 << lAllowableEditDistance << ", provides "
274 << nbMatches << " matches.");
275 */
276
277 //
278 oDoesMatch = true;
279 return oDoesMatch;
280 }
281
282 // Error
283 OPENTREP_LOG_ERROR (" Query string: `"
284 << lQueryString << "', spelling suggestion: `"
285 << lCorrectedString
286 << "', with a Levenshtein edit distance of "
287 << lEditDistance
288 << " over an allowable edit distance of "
289 << lAllowableEditDistance << ", provides no match, "
290 << "which is not consistent with the existence of "
291 << "the spelling correction.");
292 assert (false);
293
294 } catch (const Xapian::Error& error) {
295 // Error
296 OPENTREP_LOG_ERROR ("Exception: " << error.get_msg());
297 throw XapianException (error.get_msg());
298 }
299
300 return oDoesMatch;
301 }
302
303 // //////////////////////////////////////////////////////////////////////
304 void QuerySlices::init (const OTransliterator& iTransliterator) {
305 // 0. Initialisation
306 // 0.1. Stripping of the punctuation and quotation characters
307 _queryString = iTransliterator.unpunctuate (_queryString);
308 _queryString = iTransliterator.unquote (_queryString);
309
310 // 0.2. Initialisation of the tokenizer
311 WordList_T lWordList;
313 const unsigned short nbOfWords = lWordList.size();
314
315 // When the query has a single word, stop here, as there is a single slice
316 if (nbOfWords <= 1) {
317 _slices.push_back (_queryString);
318 return;
319 }
320
321 // 0.3. Re-create the initial phrase, without any (potential) seperator
322 const std::string lPhrase = createStringFromWordList (lWordList);
323
324 // 1. Browse the words, two by two, and check whether their association
325 // matches with the Xapian index
326 WordList_T::const_iterator itWord = lWordList.begin();
327 WordList_T::const_iterator itNextWord = lWordList.begin(); ++itNextWord;
328 // for (unsigned short idx = 1, idx_rel = 1; itNextWord != lWordList.end(); ++idx
329 for (unsigned short idx_rel = 1; itNextWord != lWordList.end();
330 ++itWord, ++itNextWord, ++idx_rel) {
331 const std::string& leftWord = *itWord;
332 const std::string& rightWord = *itNextWord;
333
334 // Store the left word in the staging string
335 if (idx_rel >= 2) {
336 _itLeftWords += " ";
337 }
338 _itLeftWords += leftWord;
339
340 // Check whether the juxtaposition of the two contiguous words matches
341 const bool lDoesMatch =
342 OPENTREP::doesMatch (_database, leftWord, rightWord);
343
344 if (lDoesMatch == true) {
345 // When the two words give a match, do nothing now, as at the next turn,
346 // the right word will become the left word and thus be added to the
347 // staging string
348
349 // DEBUG
350 /*
351 OPENTREP_LOG_DEBUG ("[" << idx << "][" << idx_rel
352 << "] Match - staging string: '"
353 << _itLeftWords << "'");
354 */
355
356 } else {
357 // DEBUG
358 /*
359 OPENTREP_LOG_DEBUG ("[" << idx << "][" << idx_rel
360 << "] No match - staging string: '"
361 << _itLeftWords << "'");
362 */
363
364 // When the two words give no match, add the content of the staging
365 // list to the list of slices. Then, empty the staging string.
366 _slices.push_back (_itLeftWords);
367 _itLeftWords = "";
368 idx_rel = 0;
369 }
370 }
371
372 // 2.
373 const std::string& leftWord = *itWord;
374 if (_itLeftWords.empty() == false) {
375 _itLeftWords += " ";
376 }
377 _itLeftWords += leftWord;
378 _slices.push_back (_itLeftWords);
379
380 // DEBUG
381 // OPENTREP_LOG_DEBUG ("Last staging string: '" << _itLeftWords << "'");
382 // OPENTREP_LOG_DEBUG ("Slices: " << *this);
383 }
384
385}
#define OPENTREP_LOG_ERROR(iToBeLogged)
Definition Logger.hpp:24
static int getDistance(const std::string &iSource, const std::string &iTarget)
XapianException(const std::string &iWhat)
unsigned short NbOfErrors_T
std::string createStringFromWordList(const WordList_T &iWordList, const NbOfWords_T iSplitIdx, const bool iFromBeginningFlag)
Definition Utilities.cpp:43
std::string TravelQuery_T
static unsigned int calculateEditDistance(const TravelQuery_T &iPhrase)
Helper function.
bool doesMatch(const Xapian::Database &iDatabase, const std::string &iWord1, const std::string &iWord2)
Helper function to query for a Xapian-based full text match.
const NbOfMatches_T K_DEFAULT_XAPIAN_MATCHING_SET_SIZE
const NbOfErrors_T K_DEFAULT_SIZE_FOR_SPELLING_ERROR_UNIT
std::vector< std::string > WordList_T
void tokeniseStringIntoWordList(const std::string &iPhrase, WordList_T &ioWordList)
void push_back(const StringPartition &iStringPartition)
TravelQuery_T _queryString
void toStream(std::ostream &ioOut) const
QuerySlices(const Xapian::Database &, const TravelQuery_T &, const OTransliterator &)
const Xapian::Database & _database
StringPartitionList_T _slices
void fromStream(std::istream &ioIn)
std::string describe() const
std::string describeKey() const
size_t size() const