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

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

some changes to the way quoted queries are handled

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