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

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

changed format option result to QueryResults and added QueryLinks option

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