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

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

had a go at getting a query result format string working

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