source: trunk/gsdl/src/recpt/queryaction.cpp@ 398

Last change on this file since 398 was 398, checked in by sjboddie, 25 years ago

changed isApprox

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/**********************************************************************
2 *
3 * queryaction.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * PUT COPYRIGHT NOTICE HERE
7 *
8 * $Id: queryaction.cpp 398 1999-07-16 03:42:23Z sjboddie $
9 *
10 *********************************************************************/
11
12/*
13 $Log$
14 Revision 1.14 1999/07/16 03:41:29 sjboddie
15 changed isApprox
16
17 Revision 1.13 1999/07/16 00:19:01 sjboddie
18 some changes to the way quoted queries are handled
19
20 Revision 1.12 1999/07/09 02:17:55 rjmcnab
21 Setting macros needed for a second query.
22
23 Revision 1.11 1999/07/07 06:13:10 rjmcnab
24 Added ability to combine two independant queries.
25
26 Revision 1.10 1999/07/07 05:49:35 sjboddie
27 had another crack at the format string code - created a new formattools
28 module. It can now handle {If} and {Or} statements although there's a
29 bug preventing nested if's and or's.
30
31 Revision 1.9 1999/07/01 22:48:46 sjboddie
32 had a go at getting a query result format string working
33
34 Revision 1.8 1999/06/27 22:02:11 sjboddie
35 author is added to queryresults if there is one
36
37 Revision 1.7 1999/06/26 01:10:18 rjmcnab
38 Made h, i, and n arguments saved in the compressed arguments.
39
40 Revision 1.6 1999/06/24 05:12:25 sjboddie
41 lots of small changes
42
43 Revision 1.5 1999/06/16 04:03:48 sjboddie
44 Now sets "cl" arg to "search" when going to a document from a search
45 results page. This allows the close book icon (in hierarchy toc) to
46 take you back to the results page if that's where you came from.
47 If you got to the document page somehow other than from a
48 classification or a search (i.e. if "cl" isn't set) then the close
49 book icon is disabled
50
51 Revision 1.4 1999/06/16 02:08:38 sjboddie
52 got queryaction working
53
54 Revision 1.3 1999/03/25 03:06:45 sjboddie
55
56 altered receptionist slightly so it now passes *collectproto to
57 define_internal_macros and define_external_macros - need it
58 for browseaction
59
60 Revision 1.2 1999/03/03 20:26:50 rjmcnab
61
62 Modified stuff.
63
64 Revision 1.1 1999/02/28 22:45:21 rjmcnab
65
66 Initial revision.
67
68 */
69
70
71#include "queryaction.h"
72#include "querytools.h"
73#include "formattools.h"
74
75queryaction::queryaction () {
76 // this action uses cgi variable "a"
77 cgiarginfo arg_ainfo;
78 arg_ainfo.shortname = "a";
79 arg_ainfo.longname = "action";
80 arg_ainfo.multiplechar = true;
81 arg_ainfo.defaultstatus = cgiarginfo::weak;
82 arg_ainfo.argdefault = "q";
83 arg_ainfo.savedarginfo = cgiarginfo::must;
84 argsinfo.addarginfo (NULL, arg_ainfo);
85
86 // "h"
87 arg_ainfo.shortname = "h";
88 arg_ainfo.longname = "main index";
89 arg_ainfo.multiplechar = true;
90 arg_ainfo.defaultstatus = cgiarginfo::weak;
91 arg_ainfo.argdefault = "";
92 arg_ainfo.savedarginfo = cgiarginfo::must;
93 argsinfo.addarginfo (NULL, arg_ainfo);
94
95 // "h2"
96 arg_ainfo.shortname = "h2";
97 arg_ainfo.longname = "main index for second query";
98 arg_ainfo.multiplechar = true;
99 arg_ainfo.defaultstatus = cgiarginfo::weak;
100 arg_ainfo.argdefault = "";
101 arg_ainfo.savedarginfo = cgiarginfo::must;
102 argsinfo.addarginfo (NULL, arg_ainfo);
103
104 // "j"
105 arg_ainfo.shortname = "j";
106 arg_ainfo.longname = "sub collection index";
107 arg_ainfo.multiplechar = true;
108 arg_ainfo.defaultstatus = cgiarginfo::weak;
109 arg_ainfo.argdefault = "";
110 arg_ainfo.savedarginfo = cgiarginfo::must;
111 argsinfo.addarginfo (NULL, arg_ainfo);
112
113 // "j2"
114 arg_ainfo.shortname = "j2";
115 arg_ainfo.longname = "sub collection index for second query";
116 arg_ainfo.multiplechar = true;
117 arg_ainfo.defaultstatus = cgiarginfo::weak;
118 arg_ainfo.argdefault = "";
119 arg_ainfo.savedarginfo = cgiarginfo::must;
120 argsinfo.addarginfo (NULL, arg_ainfo);
121
122 // "n"
123 arg_ainfo.shortname = "n";
124 arg_ainfo.longname = "language index";
125 arg_ainfo.multiplechar = true;
126 arg_ainfo.defaultstatus = cgiarginfo::weak;
127 arg_ainfo.argdefault = "";
128 arg_ainfo.savedarginfo = cgiarginfo::must;
129 argsinfo.addarginfo (NULL, arg_ainfo);
130
131 // "n2"
132 arg_ainfo.shortname = "n2";
133 arg_ainfo.longname = "language index for second query";
134 arg_ainfo.multiplechar = true;
135 arg_ainfo.defaultstatus = cgiarginfo::weak;
136 arg_ainfo.argdefault = "";
137 arg_ainfo.savedarginfo = cgiarginfo::must;
138 argsinfo.addarginfo (NULL, arg_ainfo);
139
140 // "q"
141 arg_ainfo.shortname = "q";
142 arg_ainfo.longname = "query string";
143 arg_ainfo.multiplechar = true;
144 arg_ainfo.defaultstatus = cgiarginfo::weak;
145 arg_ainfo.argdefault = "";
146 arg_ainfo.savedarginfo = cgiarginfo::must;
147 argsinfo.addarginfo (NULL, arg_ainfo);
148
149 // "q2"
150 arg_ainfo.shortname = "q2";
151 arg_ainfo.longname = "query string for second query";
152 arg_ainfo.multiplechar = true;
153 arg_ainfo.defaultstatus = cgiarginfo::weak;
154 arg_ainfo.argdefault = "";
155 arg_ainfo.savedarginfo = cgiarginfo::must;
156 argsinfo.addarginfo (NULL, arg_ainfo);
157
158 // "cq2" ""=don't combine, "and", "or", "not"
159 arg_ainfo.shortname = "cq2";
160 arg_ainfo.longname = "combine queries";
161 arg_ainfo.multiplechar = true;
162 arg_ainfo.defaultstatus = cgiarginfo::weak;
163 arg_ainfo.argdefault = "";
164 arg_ainfo.savedarginfo = cgiarginfo::must;
165 argsinfo.addarginfo (NULL, arg_ainfo);
166
167 // "t" - 1 = ranked 0 = boolean
168 arg_ainfo.shortname = "t";
169 arg_ainfo.longname = "search type";
170 arg_ainfo.multiplechar = false;
171 arg_ainfo.defaultstatus = cgiarginfo::weak;
172 arg_ainfo.argdefault = "1";
173 arg_ainfo.savedarginfo = cgiarginfo::must;
174 argsinfo.addarginfo (NULL, arg_ainfo);
175
176 // "k"
177 arg_ainfo.shortname = "k";
178 arg_ainfo.longname = "casefolding";
179 arg_ainfo.multiplechar = false;
180 arg_ainfo.defaultstatus = cgiarginfo::weak;
181 arg_ainfo.argdefault = "1";
182 arg_ainfo.savedarginfo = cgiarginfo::must;
183 argsinfo.addarginfo (NULL, arg_ainfo);
184
185 // "s"
186 arg_ainfo.shortname = "s";
187 arg_ainfo.longname = "stemming";
188 arg_ainfo.multiplechar = false;
189 arg_ainfo.defaultstatus = cgiarginfo::weak;
190 arg_ainfo.argdefault ="0";
191 arg_ainfo.savedarginfo = cgiarginfo::must;
192 argsinfo.addarginfo (NULL, arg_ainfo);
193
194 // "m"
195 arg_ainfo.shortname = "m";
196 arg_ainfo.longname = "maximum number of documents";
197 arg_ainfo.multiplechar = true;
198 arg_ainfo.defaultstatus = cgiarginfo::weak;
199 arg_ainfo.argdefault = "50";
200 arg_ainfo.savedarginfo = cgiarginfo::must;
201 argsinfo.addarginfo (NULL, arg_ainfo);
202
203 // "o"
204 arg_ainfo.shortname = "o";
205 arg_ainfo.longname = "hits per page";
206 arg_ainfo.multiplechar = true;
207 arg_ainfo.defaultstatus = cgiarginfo::weak;
208 arg_ainfo.argdefault = "20";
209 arg_ainfo.savedarginfo = cgiarginfo::must;
210 argsinfo.addarginfo (NULL, arg_ainfo);
211
212 // "r"
213 arg_ainfo.shortname = "r";
214 arg_ainfo.longname = "start results from";
215 arg_ainfo.multiplechar = true;
216 arg_ainfo.defaultstatus = cgiarginfo::weak;
217 arg_ainfo.argdefault = "1";
218 arg_ainfo.savedarginfo = cgiarginfo::must;
219 argsinfo.addarginfo (NULL, arg_ainfo);
220}
221
222void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
223 action::configure (key, cfgline);
224}
225
226bool queryaction::init (ostream &logout) {
227 return action::init (logout);
228}
229
230bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
231 ostream &logout) {
232
233 // check t argument
234 int arg_t = args.getintarg("t");
235 if (arg_t != 0 && arg_t != 1) {
236 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
237 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
238 if (tinfo != NULL) args["t"] = tinfo->argdefault;
239 }
240
241 // check k argument
242 int arg_k = args.getintarg("k");
243 if (arg_k != 0 && arg_k != 1) {
244 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
245 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
246 if (kinfo != NULL) args["k"] = kinfo->argdefault;
247 }
248
249 // check s argument
250 int arg_s = args.getintarg("s");
251 if (arg_s != 0 && arg_s != 1) {
252 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
253 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
254 if (sinfo != NULL) args["s"] = sinfo->argdefault;
255 }
256
257 // check m argument
258 int arg_m = args.getintarg("m");
259 if (arg_m < 0) {
260 logout << "Warning: \"m\" argument less than 0 (" << arg_m << ")\n";
261 cgiarginfo *minfo = argsinfo.getarginfo ("m");
262 if (minfo != NULL) args["m"] = minfo->argdefault;
263 }
264
265 // check o argument
266 int arg_o = args.getintarg("o");
267 if (arg_o < 0) {
268 logout << "Warning: \"o\" argument less than 0 (" << arg_o << ")\n";
269 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
270 if (oinfo != NULL) args["o"] = oinfo->argdefault;
271 }
272
273 // check r argument
274 int arg_r = args.getintarg("r");
275 if (arg_r < 1) {
276 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
277 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
278 if (rinfo != NULL) args["r"] = rinfo->argdefault;
279 }
280
281 return true;
282}
283
284void queryaction::get_cgihead_info (cgiargsclass &/*args*/, response_t &response,
285 text_t &response_data, ostream &/*logout*/) {
286 response = content;
287 response_data = "text/html";
288}
289
290void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
291 recptproto */*collectproto*/, ostream &/*logout*/) {
292
293 // define_internal_macros doesn't sets the following macros.
294
295 // _quotedquery_ the part of the query string that was quoted for post-processing
296
297
298
299 // The following macros are set later (in define_query_macros) as they can't be set until
300 // the query has been done.
301
302 // _freqmsg_ the term frequency string
303
304 // _resultline_ the "x documents matched the query" string
305
306 // _prevfirst_ these are used when setting up the links to previous/next
307 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
308 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
309 // _nextlast_
310 // _thisfirst_
311 // _thislast_
312
313
314
315 // get the quoted bits of the query string and set _quotedquery_
316 text_t &arg_q = args["q"];
317 if (!arg_q.empty()) {
318
319 text_t::const_iterator end = arg_q.end();
320 text_t::const_iterator here = findchar (arg_q.begin(), end, '\"');
321 if (here != end) {
322 text_t quotedquery, tmptext;
323 bool first = true;
324 bool foundquote = false;
325 while (here != end) {
326 if (*here == '\"') {
327 if (foundquote) {
328 if (!tmptext.empty()) {
329 if (!first) quotedquery += " and ";
330 else first = false;
331 quotedquery += "\"" + tmptext + "\"";
332 tmptext.clear();
333 }
334 foundquote = false;
335 } else foundquote = true;
336 } else {
337 if (foundquote) tmptext.push_back (*here);
338 }
339 here ++;
340 }
341 disp.setmacro ("quotedquery", "query", quotedquery);
342 }
343 }
344}
345
346// sets the selection box macros _hselection_, _jselection_, and _nselection_.
347// each option will need an _optionxoption_ macro (i.e. an _hselection_ macro
348// with options stx and ptx will need _optionhstx_ and _optionhptx_ macros)
349void queryaction::set_option_macro (const text_t &macroname, text_t current_value,
350 const FilterOption_t &option, displayclass &disp) {
351
352 if (option.validValues.size() < 2) return;
353
354 text_t macrovalue = "<select name=\"" + macroname + "\">\n";
355
356 if (current_value.empty()) current_value = option.defaultValue;
357
358 text_tarray::const_iterator thisvalue = option.validValues.begin();
359 text_tarray::const_iterator endvalue = option.validValues.end();
360
361 while (thisvalue != endvalue) {
362 macrovalue += "<option value=\"" + *thisvalue + "\"";
363 if (*thisvalue == current_value)
364 macrovalue += " selected";
365 macrovalue += ">_option" + macroname + *thisvalue + "_\n";
366 thisvalue ++;
367 }
368 macrovalue += "</select>\n";
369 disp.setmacro (macroname + "selection", "Global", macrovalue);
370}
371
372void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
373 recptproto *collectproto, ostream &logout) {
374
375 // define_external_macros sets the following macros:
376
377 // some or all of these may not be required to be set
378 // _hselection_, _h2selection_ the selection box for the main part of the index
379 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
380 // _nselection_, _n2selection_ the selection box for the language part of the index
381 // _cq2selection the selection box for combining two queries
382
383
384 // can't do anything if collectproto is null (i.e. no collection was specified)
385 if (collectproto == NULL) return;
386
387 comerror_t err;
388 InfoFilterOptionsResponse_t response;
389 InfoFilterOptionsRequest_t request;
390 request.filterName = "QueryFilter";
391
392 collectproto->get_filteroptions (args["c"], request, response, err, logout);
393 if (err == noError) {
394
395 FilterOption_tmap::const_iterator it;
396 FilterOption_tmap::const_iterator end = response.filterOptions.end();
397
398 // _hselection_ and _h2selection_ (Index)
399 it = response.filterOptions.find ("Index");
400 if (it != end) set_option_macro ("h", args["h"], (*it).second, disp);
401 if (it != end) set_option_macro ("h2", args["h2"], (*it).second, disp);
402
403 // _jselection_ and _j2selection_ (Subcollection)
404 it = response.filterOptions.find ("Subcollection");
405 if (it != end) set_option_macro ("j", args["j"], (*it).second, disp);
406 if (it != end) set_option_macro ("j2", args["j2"], (*it).second, disp);
407
408 // _nselection_ and _n2selection_ (Language)
409 it = response.filterOptions.find ("Language");
410 if (it != end) set_option_macro ("n", args["n"], (*it).second, disp);
411 if (it != end) set_option_macro ("n2", args["n2"], (*it).second, disp);
412
413 // _cq2selection_ (CombineQuery)
414 it = response.filterOptions.find ("CombineQuery");
415 if (it != end) set_option_macro ("cq2", args["cq2"], (*it).second, disp);
416 }
417}
418
419bool queryaction::do_action (cgiargsclass &args, recptproto *collectproto,
420 displayclass &disp, outconvertclass &outconvert,
421 ostream &textout, ostream &logout) {
422
423 // if we have no format string see if the collection server has one
424 if (formatstring.empty()) {
425 ColInfoResponse_t collectinfo;
426 comerror_t err;
427 collectproto->get_collectinfo (args["c"], collectinfo, err, logout);
428 if (err == noError) {
429 text_tmap::const_iterator result = collectinfo.format.find("result");
430 if (result != collectinfo.format.end())
431 formatstring = (*result).second;
432 }
433 }
434 // if we still don't have a format string use the default
435 if (formatstring.empty())
436 formatstring = "<td valign=top nowrap>[link]_icontext_[/link]</td><td>[Title]</td>";
437
438 if (collectproto == NULL) {
439 logout << "queryaction::do_action called with NULL collectproto\n";
440 textout << outconvert << disp << "_query:header_\n"
441 << "Error: Attempt to do query without setting collection\n"
442 << "_query:footer_\n";
443 } else {
444
445 FilterRequest_t request;
446 FilterResponse_t response;
447 format_t *formatlistptr = new format_t();
448
449 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
450
451 // do the query
452 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
453 if (!do_query (request, args, collectproto, response, logout))
454 return false;
455
456 // set macros
457 define_query_macros (args, disp, response);
458
459 // output the header
460 textout << outconvert << disp << "_query:header_\n"
461 << "_query:content_";
462
463 // output the results
464 textout << "<table cellspacing=4>\n";
465 ResultDocInfo_tarray::const_iterator this_doc = response.docInfo.begin();
466 ResultDocInfo_tarray::const_iterator end_doc = response.docInfo.end();
467
468 while (this_doc != end_doc) {
469 textout << outconvert << disp << "<tr>\n"
470
471 // << "<td valign=top nowrap>r: " << (*this_doc).ranking
472 // << " t: " << (*this_doc).num_terms_matched << " p: "
473 // << (*this_doc).num_phrase_match << "</td>\n"
474
475 << get_formatted_string (*this_doc, formatlistptr) << "\n"
476 << "</tr>\n";
477 this_doc ++;
478 }
479 textout << "</table>\n";
480
481 delete (formatlistptr);
482
483 // output the footer
484 textout << outconvert << disp << "_query:footer_";
485 }
486
487 return true;
488}
489
490// define_query_macros sets the macros that couldn't be set until the
491// query had been done. Those macros are _freqmsg_, _quotedquery_,
492// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
493// _thisfirst_, and _thislast_
494void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
495 const FilterResponse_t &response) {
496
497 int numdocs = response.numDocs;
498 int arg_m = args.getintarg("m");
499 if (numdocs > arg_m) numdocs = arg_m;
500
501 // set up _freqmsg_ and _quotedquery_ macros
502 text_t freqmsg = "_textfreqmsg1_";
503 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
504 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
505 while (this_term != end_term) {
506 freqmsg += (*this_term).term + ": " + (*this_term).freq;
507 if ((this_term + 1) != end_term)
508 freqmsg += ", ";
509 this_term ++;
510 }
511
512 disp.setmacro ("freqmsg", "query", freqmsg);
513
514
515 // set up _resultline_ macro
516 text_t resline;
517 int maxdocs = args.getintarg("m");
518
519 if (response.isApprox == Approximate) resline = "_textapprox_";
520 else if ((response.isApprox == MoreThan) || (response.numDocs > maxdocs))
521 resline = "_textmorethan_";
522 if (numdocs == 0) resline = "_textnodocs_";
523 else if (numdocs == 1) resline += "_text1doc_";
524 else
525 if (response.numDocs > maxdocs)
526 resline += text_t(maxdocs) + " _textlotsdocs_";
527 else
528 resline += text_t(response.numDocs) + " _textlotsdocs_";
529
530 disp.setmacro("resultline", "query", resline);
531
532
533 int firstdoc = args.getintarg("r");
534 int hitsperpage = args.getintarg("o");
535
536 // set up _thisfirst_ and _thislast_ macros
537 disp.setmacro ("thisfirst", "query", firstdoc);
538 int thislast = firstdoc + (hitsperpage - 1);
539 if (thislast > numdocs) thislast = numdocs;
540 disp.setmacro ("thislast", "query", thislast);
541
542 // set up _prevfirst_ and _prevlast_ macros
543 if (firstdoc > 1) {
544 disp.setmacro ("prevlast", "query", firstdoc - 1);
545 int prevfirst = firstdoc - hitsperpage;
546 if (prevfirst < 1) prevfirst = 1;
547 disp.setmacro ("prevfirst", "query", prevfirst);
548 }
549
550 // set up _nextfirst_ and _nextlast_ macros
551 if (thislast < numdocs) {
552 disp.setmacro ("nextfirst", "query", thislast + 1);
553 int nextlast = thislast + hitsperpage;
554 if (nextlast > numdocs) nextlast = numdocs;
555 disp.setmacro ("nextlast", "query", nextlast);
556 }
557}
558
559
560
Note: See TracBrowser for help on using the repository browser.