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

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

small change to do with new collection-level metadata

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