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

Last change on this file since 928 was 928, checked in by kjm18, 24 years ago

search history stuff added.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 KB
Line 
1/**********************************************************************
2 *
3 * queryaction.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * $Id: queryaction.cpp 928 2000-02-15 22:53:52Z kjm18 $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.34 2000/02/15 22:53:52 kjm18
31 search history stuff added.
32
33 Revision 1.33 2000/01/24 22:57:59 sjboddie
34 fixed bug in cross-collection searching, tidied up a bit
35
36 Revision 1.32 1999/12/13 02:54:11 davidb
37 Support for cross collection searching (CCS)
38
39 Revision 1.31 1999/12/05 21:22:33 sjboddie
40 tidied up cross-collection searching a bit
41
42 Revision 1.30 1999/11/08 20:26:37 sjboddie
43 added multiplevalue option to cgiarginfo
44
45 Revision 1.29 1999/11/04 20:29:53 sjboddie
46 small change to cross-collection searching
47
48 Revision 1.28 1999/11/03 22:49:50 sjboddie
49 A few changes to cross-collection searching for fao
50
51 Revision 1.27 1999/11/01 21:53:27 sjboddie
52 added cross-collection searching capability - still needs lots of
53 work but the basic functionality is there
54
55 Revision 1.26 1999/10/10 08:14:10 sjboddie
56 - metadata now returns mp rather than array
57 - redesigned browsing support (although it's not finished so
58 won't currently work ;-)
59
60 Revision 1.25 1999/09/24 04:49:39 sjboddie
61 fixed up the query selection boxes to display properly if there's only
62 a single index/sub-collection
63
64 Revision 1.24 1999/09/22 03:44:31 sjboddie
65 EndResults query filter option may now take '-1' for 'all'
66
67 Revision 1.23 1999/09/21 11:34:42 sjboddie
68 added Maxdocs queryfilter option which may be -1 for 'all'
69
70 Revision 1.22 1999/09/07 23:08:51 rjmcnab
71 removed some compiler warnings
72
73 Revision 1.21 1999/09/07 04:56:57 sjboddie
74 added GPL notice
75
76 Revision 1.20 1999/08/25 04:47:55 sjboddie
77 added advanced search option - other minor changes
78
79 Revision 1.19 1999/08/13 04:17:24 sjboddie
80 small change to do with new collection-level metadata
81
82 Revision 1.18 1999/08/10 22:46:33 sjboddie
83 changed format option result to QueryResults and added QueryLinks option
84
85 Revision 1.17 1999/07/30 02:24:42 sjboddie
86 added collectinfo argument to some functions
87
88 Revision 1.16 1999/07/19 00:16:58 sjboddie
89 no longer display documents that don't match all phrases in query string
90
91 Revision 1.15 1999/07/16 08:33:36 rjmcnab
92 Changed the logic for getting the results string slightly
93
94 Revision 1.14 1999/07/16 03:41:29 sjboddie
95 changed isApprox
96
97 Revision 1.13 1999/07/16 00:19:01 sjboddie
98 some changes to the way quoted queries are handled
99
100 Revision 1.12 1999/07/09 02:17:55 rjmcnab
101 Setting macros needed for a second query.
102
103 Revision 1.11 1999/07/07 06:13:10 rjmcnab
104 Added ability to combine two independant queries.
105
106 Revision 1.10 1999/07/07 05:49:35 sjboddie
107 had another crack at the format string code - created a new formattools
108 module. It can now handle {If} and {Or} statements although there's a
109 bug preventing nested if's and or's.
110
111 Revision 1.9 1999/07/01 22:48:46 sjboddie
112 had a go at getting a query result format string working
113
114 Revision 1.8 1999/06/27 22:02:11 sjboddie
115 author is added to queryresults if there is one
116
117 Revision 1.7 1999/06/26 01:10:18 rjmcnab
118 Made h, i, and n arguments saved in the compressed arguments.
119
120 Revision 1.6 1999/06/24 05:12:25 sjboddie
121 lots of small changes
122
123 Revision 1.5 1999/06/16 04:03:48 sjboddie
124 Now sets "cl" arg to "search" when going to a document from a search
125 results page. This allows the close book icon (in hierarchy toc) to
126 take you back to the results page if that's where you came from.
127 If you got to the document page somehow other than from a
128 classification or a search (i.e. if "cl" isn't set) then the close
129 book icon is disabled
130
131 Revision 1.4 1999/06/16 02:08:38 sjboddie
132 got queryaction working
133
134 Revision 1.3 1999/03/25 03:06:45 sjboddie
135
136 altered receptionist slightly so it now passes *collectproto to
137 define_internal_macros and define_external_macros - need it
138 for browseaction
139
140 Revision 1.2 1999/03/03 20:26:50 rjmcnab
141
142 Modified stuff.
143
144 Revision 1.1 1999/02/28 22:45:21 rjmcnab
145
146 Initial revision.
147
148 */
149
150
151#include "queryaction.h"
152#include "querytools.h"
153#include "formattools.h"
154#include "cgiutils.h"
155#include "OIDtools.h"
156//#include "infodbclass.h"
157#include "fileutil.h"
158#include "text_t.h"
159#include "historydb.h"
160
161void colinfo_t::clear () {
162 formatlistptr = NULL;
163 browserptr = NULL;
164}
165
166void QueryResult_t::clear() {
167 doc.clear();
168 collection.clear();
169}
170
171queryaction::queryaction () {
172
173 num_phrases = 0;
174
175 // this action uses cgi variable "a"
176 cgiarginfo arg_ainfo;
177 arg_ainfo.shortname = "a";
178 arg_ainfo.longname = "action";
179 arg_ainfo.multiplechar = true;
180 arg_ainfo.defaultstatus = cgiarginfo::weak;
181 arg_ainfo.argdefault = "q";
182 arg_ainfo.savedarginfo = cgiarginfo::must;
183 argsinfo.addarginfo (NULL, arg_ainfo);
184
185 // "b" - 0 = simple, 1 = advanced
186 arg_ainfo.shortname = "b";
187 arg_ainfo.longname = "query mode";
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 // "h"
195 arg_ainfo.shortname = "h";
196 arg_ainfo.longname = "main index";
197 arg_ainfo.multiplechar = true;
198 arg_ainfo.defaultstatus = cgiarginfo::weak;
199 arg_ainfo.argdefault = "";
200 arg_ainfo.savedarginfo = cgiarginfo::must;
201 argsinfo.addarginfo (NULL, arg_ainfo);
202
203 // "h2"
204 arg_ainfo.shortname = "h2";
205 arg_ainfo.longname = "main index for second query";
206 arg_ainfo.multiplechar = true;
207 arg_ainfo.defaultstatus = cgiarginfo::weak;
208 arg_ainfo.argdefault = "";
209 arg_ainfo.savedarginfo = cgiarginfo::must;
210 argsinfo.addarginfo (NULL, arg_ainfo);
211
212 // "j"
213 arg_ainfo.shortname = "j";
214 arg_ainfo.longname = "sub collection index";
215 arg_ainfo.multiplechar = true;
216 arg_ainfo.defaultstatus = cgiarginfo::weak;
217 arg_ainfo.argdefault = "";
218 arg_ainfo.savedarginfo = cgiarginfo::must;
219 argsinfo.addarginfo (NULL, arg_ainfo);
220
221 // "j2"
222 arg_ainfo.shortname = "j2";
223 arg_ainfo.longname = "sub collection index for second query";
224 arg_ainfo.multiplechar = true;
225 arg_ainfo.defaultstatus = cgiarginfo::weak;
226 arg_ainfo.argdefault = "";
227 arg_ainfo.savedarginfo = cgiarginfo::must;
228 argsinfo.addarginfo (NULL, arg_ainfo);
229
230 // "n"
231 arg_ainfo.shortname = "n";
232 arg_ainfo.longname = "language index";
233 arg_ainfo.multiplechar = true;
234 arg_ainfo.defaultstatus = cgiarginfo::weak;
235 arg_ainfo.argdefault = "";
236 arg_ainfo.savedarginfo = cgiarginfo::must;
237 argsinfo.addarginfo (NULL, arg_ainfo);
238
239 // "n2"
240 arg_ainfo.shortname = "n2";
241 arg_ainfo.longname = "language index for second query";
242 arg_ainfo.multiplechar = true;
243 arg_ainfo.defaultstatus = cgiarginfo::weak;
244 arg_ainfo.argdefault = "";
245 arg_ainfo.savedarginfo = cgiarginfo::must;
246 argsinfo.addarginfo (NULL, arg_ainfo);
247
248 // "q"
249 arg_ainfo.shortname = "q";
250 arg_ainfo.longname = "query string";
251 arg_ainfo.multiplechar = true;
252 arg_ainfo.defaultstatus = cgiarginfo::weak;
253 arg_ainfo.argdefault = "";
254 arg_ainfo.savedarginfo = cgiarginfo::must;
255 argsinfo.addarginfo (NULL, arg_ainfo);
256
257 // "q2"
258 arg_ainfo.shortname = "q2";
259 arg_ainfo.longname = "query string for second query";
260 arg_ainfo.multiplechar = true;
261 arg_ainfo.defaultstatus = cgiarginfo::weak;
262 arg_ainfo.argdefault = "";
263 arg_ainfo.savedarginfo = cgiarginfo::must;
264 argsinfo.addarginfo (NULL, arg_ainfo);
265
266 // "cq2" ""=don't combine, "and", "or", "not"
267 arg_ainfo.shortname = "cq2";
268 arg_ainfo.longname = "combine queries";
269 arg_ainfo.multiplechar = true;
270 arg_ainfo.defaultstatus = cgiarginfo::weak;
271 arg_ainfo.argdefault = "";
272 arg_ainfo.savedarginfo = cgiarginfo::must;
273 argsinfo.addarginfo (NULL, arg_ainfo);
274
275 // "t" - 1 = ranked 0 = boolean
276 arg_ainfo.shortname = "t";
277 arg_ainfo.longname = "search type";
278 arg_ainfo.multiplechar = false;
279 arg_ainfo.defaultstatus = cgiarginfo::weak;
280 arg_ainfo.argdefault = "1";
281 arg_ainfo.savedarginfo = cgiarginfo::must;
282 argsinfo.addarginfo (NULL, arg_ainfo);
283
284 // "k"
285 arg_ainfo.shortname = "k";
286 arg_ainfo.longname = "casefolding";
287 arg_ainfo.multiplechar = false;
288 arg_ainfo.defaultstatus = cgiarginfo::weak;
289 arg_ainfo.argdefault = "1";
290 arg_ainfo.savedarginfo = cgiarginfo::must;
291 argsinfo.addarginfo (NULL, arg_ainfo);
292
293 // "s"
294 arg_ainfo.shortname = "s";
295 arg_ainfo.longname = "stemming";
296 arg_ainfo.multiplechar = false;
297 arg_ainfo.defaultstatus = cgiarginfo::weak;
298 arg_ainfo.argdefault ="0";
299 arg_ainfo.savedarginfo = cgiarginfo::must;
300 argsinfo.addarginfo (NULL, arg_ainfo);
301
302 // "m"
303 arg_ainfo.shortname = "m";
304 arg_ainfo.longname = "maximum number of documents";
305 arg_ainfo.multiplechar = true;
306 arg_ainfo.defaultstatus = cgiarginfo::weak;
307 arg_ainfo.argdefault = "50";
308 arg_ainfo.savedarginfo = cgiarginfo::must;
309 argsinfo.addarginfo (NULL, arg_ainfo);
310
311 // "o"
312 arg_ainfo.shortname = "o";
313 arg_ainfo.longname = "hits per page";
314 arg_ainfo.multiplechar = true;
315 arg_ainfo.defaultstatus = cgiarginfo::weak;
316 arg_ainfo.argdefault = "20";
317 arg_ainfo.savedarginfo = cgiarginfo::must;
318 argsinfo.addarginfo (NULL, arg_ainfo);
319
320 // "r"
321 arg_ainfo.shortname = "r";
322 arg_ainfo.longname = "start results from";
323 arg_ainfo.multiplechar = true;
324 arg_ainfo.defaultstatus = cgiarginfo::weak;
325 arg_ainfo.argdefault = "1";
326 arg_ainfo.savedarginfo = cgiarginfo::must;
327 argsinfo.addarginfo (NULL, arg_ainfo);
328
329 // "ccs"
330 arg_ainfo.shortname = "ccs";
331 arg_ainfo.longname = "cross collection searching";
332 arg_ainfo.multiplechar = false;
333 arg_ainfo.defaultstatus = cgiarginfo::weak;
334 arg_ainfo.argdefault = "0";
335 arg_ainfo.savedarginfo = cgiarginfo::must;
336 argsinfo.addarginfo (NULL, arg_ainfo);
337
338 // "ccp"
339 arg_ainfo.shortname = "ccp";
340 arg_ainfo.longname = "cross collection page";
341 arg_ainfo.multiplechar = false;
342 arg_ainfo.defaultstatus = cgiarginfo::weak;
343 arg_ainfo.argdefault = "0";
344 arg_ainfo.savedarginfo = cgiarginfo::must;
345 argsinfo.addarginfo (NULL, arg_ainfo);
346
347 // "cc"
348 arg_ainfo.shortname = "cc";
349 arg_ainfo.longname = "collections to search";
350 arg_ainfo.multiplechar = true;
351 arg_ainfo.multiplevalue = true;
352 arg_ainfo.defaultstatus = cgiarginfo::weak;
353 arg_ainfo.argdefault = "";
354 arg_ainfo.savedarginfo = cgiarginfo::must;
355 argsinfo.addarginfo (NULL, arg_ainfo);
356
357 // "hd" history display - search history only displayed when
358 // this var set.
359 arg_ainfo.shortname = "hd";
360 arg_ainfo.longname = "history display";
361 arg_ainfo.multiplechar = false;
362 arg_ainfo.defaultstatus = cgiarginfo::weak;
363 arg_ainfo.argdefault = "0";
364 arg_ainfo.savedarginfo = cgiarginfo::must;
365 argsinfo.addarginfo (NULL, arg_ainfo);
366
367
368 // "hs" save - set to 1 in query form, so only save when submit
369 // query
370 // 0 = no save 1 = save
371 arg_ainfo.shortname = "hs";
372 arg_ainfo.longname = "history save";
373 arg_ainfo.multiplechar = false;
374 arg_ainfo.defaultstatus = cgiarginfo::weak;
375 arg_ainfo.argdefault = "0";
376 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
377 argsinfo.addarginfo (NULL, arg_ainfo);
378
379
380 // "hcl" compress the list (show only the last 5)
381 // 0 = expand, 1 = compress
382
383 arg_ainfo.shortname = "hcl";
384 arg_ainfo.longname = "history compress list";
385 arg_ainfo.multiplechar = false;
386 arg_ainfo.defaultstatus = cgiarginfo::weak;
387 arg_ainfo.argdefault = "1";
388 arg_ainfo.savedarginfo = cgiarginfo::must;
389 argsinfo.addarginfo (NULL, arg_ainfo);
390
391
392
393}
394
395void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
396 action::configure (key, cfgline);
397}
398
399bool queryaction::init (ostream &logout) {
400 return action::init (logout);
401}
402
403bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
404 ostream &logout) {
405
406 // check t argument
407 int arg_t = args.getintarg("t");
408 if (arg_t != 0 && arg_t != 1) {
409 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
410 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
411 if (tinfo != NULL) args["t"] = tinfo->argdefault;
412 }
413
414 // check k argument
415 int arg_k = args.getintarg("k");
416 if (arg_k != 0 && arg_k != 1) {
417 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
418 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
419 if (kinfo != NULL) args["k"] = kinfo->argdefault;
420 }
421
422 // check s argument
423 int arg_s = args.getintarg("s");
424 if (arg_s != 0 && arg_s != 1) {
425 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
426 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
427 if (sinfo != NULL) args["s"] = sinfo->argdefault;
428 }
429
430 // check m argument
431 int arg_m = args.getintarg("m");
432 if (arg_m < -1) {
433 logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
434 cgiarginfo *minfo = argsinfo.getarginfo ("m");
435 if (minfo != NULL) args["m"] = minfo->argdefault;
436 }
437
438 // check o argument
439 int arg_o = args.getintarg("o");
440 if (arg_o < -1) {
441 logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
442 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
443 if (oinfo != NULL) args["o"] = oinfo->argdefault;
444 }
445
446 // check r argument
447 int arg_r = args.getintarg("r");
448 if (arg_r < 1) {
449 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
450 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
451 if (rinfo != NULL) args["r"] = rinfo->argdefault;
452 }
453 //check hd argument
454 int arg_hd = args.getintarg("hd");
455 if (arg_hd !=0 && arg_hd !=1) {
456 logout << "Warning: \"hd\" argument out of range (" << arg_hd << ")\n";
457 cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
458 if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
459 }
460 //check hs argument
461 int arg_hs = args.getintarg("hs");
462 if (arg_hs !=0 && arg_hs !=1) {
463 logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
464 cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
465 if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
466 }
467
468 // chech hcl argument
469 int arg_hcl = args.getintarg("hcl");
470 if (arg_hcl !=0 && arg_hcl !=1) {
471 logout << "Warning: \"hcl\" argument out of range (" << arg_hcl << ")\n";
472 cgiarginfo *hclinfo = argsinfo.getarginfo ("hcl");
473 if (hclinfo != NULL) args["hcl"] = hclinfo->argdefault;
474 }
475
476 return true;
477}
478
479void queryaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
480 response_t &response, text_t &response_data,
481 ostream &/*logout*/) {
482 response = content;
483 response_data = "text/html";
484}
485
486void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
487 recptprotolistclass * /*protos*/,
488 ostream &/*logout*/) {
489
490 // define_internal_macros sets the following macros:
491
492 // _quotedquery_ the part of the query string that was quoted for post-processing
493
494
495
496 // The following macros are set later (in define_query_macros) as they can't be set until
497 // the query has been done.
498
499 // _freqmsg_ the term frequency string
500
501 // _resultline_ the "x documents matched the query" string
502
503 // _prevfirst_ these are used when setting up the links to previous/next
504 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
505 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
506 // _nextlast_
507 // _thisfirst_
508 // _thislast_
509
510
511 // get the quoted bits of the query string and set _quotedquery_
512 text_tarray phrases;
513 get_phrases (args["q"], phrases);
514 num_phrases = phrases.size();
515 text_tarray::const_iterator phere = phrases.begin();
516 text_tarray::const_iterator pend = phrases.end();
517 bool first = true;
518 text_t quotedquery;
519 while (phere != pend) {
520 if (!first)
521 if ((phere +1) == pend) quotedquery += " and ";
522 else quotedquery += ", ";
523
524 quotedquery += "\"" + *phere + "\"";
525 first = false;
526 phere ++;
527 }
528 if (args.getintarg("s")) quotedquery += "_textstemon_";
529 disp.setmacro ("quotedquery", "query", quotedquery);
530}
531
532// sets the selection box macros _hselection_, _jselection_, and _nselection_.
533void queryaction::set_option_macro (const text_t &macroname, text_t current_value,
534 const FilterOption_t &option, displayclass &disp) {
535
536 if (option.validValues.empty()) return;
537 else if (option.validValues.size() == 1) {
538 disp.setmacro (macroname + "selection", "Global", "_" + option.defaultValue + "_");
539 return;
540 }
541 if (option.validValues.size() < 2) return;
542
543 text_t macrovalue = "<select name=\"" + macroname + "\">\n";
544
545 if (current_value.empty()) current_value = option.defaultValue;
546
547 text_tarray::const_iterator thisvalue = option.validValues.begin();
548 text_tarray::const_iterator endvalue = option.validValues.end();
549
550 while (thisvalue != endvalue) {
551 macrovalue += "<option value=\"" + *thisvalue + "\"";
552 if (*thisvalue == current_value)
553 macrovalue += " selected";
554 macrovalue += ">_" + *thisvalue + "_\n";
555 thisvalue ++;
556 }
557 macrovalue += "</select>\n";
558 disp.setmacro (macroname + "selection", "Global", macrovalue);
559}
560
561void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
562 recptprotolistclass *protos, ostream &logout) {
563
564 // define_external_macros sets the following macros:
565
566 // some or all of these may not be required to be set
567 // _hselection_, _h2selection_ the selection box for the main part of the index
568 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
569 // _nselection_, _n2selection_ the selection box for the language part of the index
570 // _cq2selection the selection box for combining two queries
571
572
573 // can't do anything if collectproto is null (i.e. no collection was specified)
574 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
575 if (collectproto == NULL) return;
576
577 comerror_t err;
578 InfoFilterOptionsResponse_t response;
579 InfoFilterOptionsRequest_t request;
580 request.filterName = "QueryFilter";
581
582 collectproto->get_filteroptions (args["c"], request, response, err, logout);
583 if (err == noError) {
584
585 FilterOption_tmap::const_iterator it;
586 FilterOption_tmap::const_iterator end = response.filterOptions.end();
587
588 // _hselection_ and _h2selection_ (Index)
589 it = response.filterOptions.find ("Index");
590 if (it != end) set_option_macro ("h", args["h"], (*it).second, disp);
591 if (it != end) set_option_macro ("h2", args["h2"], (*it).second, disp);
592
593 // _jselection_ and _j2selection_ (Subcollection)
594 it = response.filterOptions.find ("Subcollection");
595 if (it != end) set_option_macro ("j", args["j"], (*it).second, disp);
596 if (it != end) set_option_macro ("j2", args["j2"], (*it).second, disp);
597
598 // _nselection_ and _n2selection_ (Language)
599 it = response.filterOptions.find ("Language");
600 if (it != end) set_option_macro ("n", args["n"], (*it).second, disp);
601 if (it != end) set_option_macro ("n2", args["n2"], (*it).second, disp);
602
603 // _cq2selection_ (CombineQuery)
604 it = response.filterOptions.find ("CombineQuery");
605 if (it != end) set_option_macro ("cq2", args["cq2"], (*it).second, disp);
606
607 // define_history_macros(disp, args, protos, logout);
608 }
609} // define external macros
610
611void queryaction::define_history_macros(displayclass &disp, cgiargsclass &args,
612 recptprotolistclass *protos, ostream &logout) {
613
614 // defines the following macros
615
616 // _searchhistorylist_
617
618 text_t historylist;
619 int arghd = args.getintarg("hd");
620 if (arghd != 1) {
621 historylist="";
622 }
623 else {
624 historylist = "<!-- Search History List -->\n";
625
626 text_t userid = args["z"];
627 text_tarray entries;
628 if (get_history_info(userid, entries)) {
629 int count = 1;
630 text_tarray::iterator here = entries.begin();
631 text_tarray::iterator end = entries.end();
632 int size=(int)entries.size();
633 if (args["hcl"]==1&&size>5) { // compress the list
634 here = end-5;
635 count=size-4;
636 }
637 historylist += "<table align=center width=500 border=0>\n";
638 historylist += "<tr><td width=300 align=center>";
639 historylist += "<a href=\"_httpclearhistory_\">Clear History</a></td>\n";
640
641 if (size>5&&args["hcl"]==1) { //compress the list, show the expand button
642
643 historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=0\">Expand</a></td>\n";
644 }
645 else if (size >5 && args["hcl"]==0) { // expand the list, show contract button
646 historylist += "<td><a href=\"_gwcgi_?e=_compressedoptions_&a=q&hcl=1\">Contract</a></td>\n";
647 }
648 historylist += "</table>\n";
649 historylist += " <table align=center width=500 border=1> \n <tr><th colspan=4 align=center>";
650 historylist += "Search History </th></tr>\n";
651 historylist += "<tr><th width=40>#</th>\n<th width=340>Query</th>\n";
652 historylist += "<th width=60>Results</th><th width=60>View</th></tr>\n";
653
654 while (here !=end ) {
655 text_t c;
656 text_t query;
657 text_t q = "q";
658 text_t numdocs;
659 text_t cgiargs;
660 text_t userinfo;
661 split_saved_query(*here,c, numdocs, cgiargs);
662 parse_saved_args(cgiargs, q, query); // get query string out
663 decode_cgi_arg(query); // un cgisafe it
664
665 format_user_info(cgiargs, userinfo, protos, logout);
666
667 historylist += "<tr> <td width=40 align=center>"+c+"</td>\n";
668 historylist += "<td width=340 align=left>"+query+"</td><td width=60 align=center>"+numdocs+"</td>\n";
669 historylist += "<td width=60 align=center><a href=\"_gwcgi_?e=_compressedoptions_&";
670 historylist += *here+"\"><img name=\"display\" src=\"/gsdl/images/display.gif\" width=\"60\" ";
671 historylist += "height=\"20\" border=\"0\" alt=\"" + userinfo +"\"></a></td></tr>\n";
672 here++;
673 count++;
674 }
675 historylist+="</table>\n\n";
676
677 } // if
678 else {
679 historylist += "Search history not available\n";
680 }
681 historylist += "<p><! ---- end of history list ----->\n";
682 } // else display list
683 disp.setmacro("searchhistorylist", "query", historylist);
684
685} // define history macros
686
687void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
688 displayclass &disp, outconvertclass &outconvert,
689 ostream &textout, ostream &logout) {
690
691 ColInfoResponse_t cinfo;
692 comerror_t err;
693 InfoFilterOptionsResponse_t fresponse;
694 InfoFilterOptionsRequest_t frequest;
695 frequest.filterName = "QueryFilter";
696
697 text_t &index = args["h"];
698 text_t &subcollection = args["j"];
699 text_t &language = args["n"];
700
701 text_tset collections;
702 text_t arg_cc = args["cc"];
703 decode_cgi_arg (arg_cc);
704 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
705
706 textout << outconvert << disp << "_query:header_\n"
707 << "<center>_navigationbar_</center><br>\n"
708 << "<form name=QueryForm method=get action=\"_gwcgi_\">\n"
709 << "<input type=hidden name=a value=\"q\">\n"
710 << "<input type=hidden name=e value=\"_compressedoptions_\">\n"
711 << "<input type=hidden name=ccp value=\"1\">\n"
712 << "<center><table width=_pagewidth_><tr valign=top>\n"
713 << "<td>Select collections to search for \"" << args["q"]
714 << "\" <i>(index=" << index << " subcollection=" << subcollection
715 << " language=" << language << ")</i></td>\n"
716 << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
717 << "</tr></table></center>\n"
718 << "<center><table width=_pagewidth_>\n"
719 << "<tr><td>\n";
720
721 recptprotolistclass::iterator rprotolist_here = protos->begin();
722 recptprotolistclass::iterator rprotolist_end = protos->end();
723 while (rprotolist_here != rprotolist_end) {
724 if ((*rprotolist_here).p != NULL) {
725
726 text_tarray collist;
727 (*rprotolist_here).p->get_collection_list (collist, err, logout);
728 if (err == noError) {
729 text_tarray::iterator collist_here = collist.begin();
730 text_tarray::iterator collist_end = collist.end();
731 while (collist_here != collist_end) {
732
733 (*rprotolist_here).p->get_collectinfo (*collist_here, cinfo, err, logout);
734 // if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
735 if (err == noError && (cinfo.buildDate > 0)) {
736
737 (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
738 if (err == noError) {
739
740 FilterOption_tmap::const_iterator it;
741 FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
742 if (!index.empty()) {
743 it = fresponse.filterOptions.find ("Index");
744 if (it == end) {collist_here ++; continue;}
745 text_tarray::const_iterator there = (*it).second.validValues.begin();
746 text_tarray::const_iterator tend = (*it).second.validValues.end();
747 while (there != tend) {
748 if (*there == index) break;
749 there ++;
750 }
751 if (there == tend) {collist_here++; continue;}
752 }
753 if (!subcollection.empty()) {
754 it = fresponse.filterOptions.find ("Subcollection");
755 if (it == end) {collist_here++; continue;}
756 text_tarray::const_iterator there = (*it).second.validValues.begin();
757 text_tarray::const_iterator tend = (*it).second.validValues.end();
758 while (there != tend) {
759 if (*there == subcollection) break;
760 there ++;
761 }
762 if (there == tend) {collist_here++; continue;}
763 }
764 if (!language.empty()) {
765 it = fresponse.filterOptions.find ("Language");
766 if (it == end) {collist_here++; continue;}
767 text_tarray::const_iterator there = (*it).second.validValues.begin();
768 text_tarray::const_iterator tend = (*it).second.validValues.end();
769 while (there != tend) {
770 if (*there == language) break;
771 there ++;
772 }
773 if (there == tend) {collist_here++; continue;}
774 }
775
776 // we've got a matching collection
777 textout << "<input type=checkbox";
778
779 text_tset::const_iterator t = collections.find (*collist_here);
780 if (t != collections.end()) textout << " checked";
781
782 textout << outconvert
783 << " name=cc value=\"" << *collist_here << "\">";
784
785 if (!cinfo.collectionmeta["collectionname"].empty())
786 textout << outconvert << disp << cinfo.collectionmeta["collectionname"];
787 else
788 textout << outconvert << *collist_here;
789
790 textout << "<br>\n";
791
792 }
793 }
794 collist_here ++;
795 }
796 }
797 }
798 rprotolist_here ++;
799 }
800 textout << outconvert << disp
801 << "</td></tr></table></center>\n"
802 << "</form>\n"
803 << "_query:footer_\n";
804
805}
806
807bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos,
808 browsermapclass *browsers, displayclass &disp,
809 outconvertclass &outconvert, ostream &textout,
810 ostream &logout) {
811
812 if (args["ccs"] == "1") {
813 if (!args["cc"].empty()) {
814 // query the selected collections
815 text_t::const_iterator b = args["cc"].begin();
816 text_t::const_iterator e = args["cc"].end();
817 if (findchar (b, e, ',') != e) {
818 if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
819 textout, logout)) return false;
820 return true;
821 } else {
822 if (!search_single_collection (args, args["cc"], protos, browsers, disp,
823 outconvert, textout, logout)) return false;
824 return true;
825 }
826 }
827 }
828
829 // simply query the current collection
830 if (!search_single_collection (args, args["c"], protos, browsers, disp,
831 outconvert, textout, logout)) return false;
832 return true;
833}
834
835bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
836 browsermapclass *browsers, displayclass &disp,
837 outconvertclass &outconvert, ostream &textout,
838 ostream &logout) {
839
840 text_tarray collections;
841
842 text_t arg_cc = args["cc"];
843 decode_cgi_arg (arg_cc);
844 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
845
846 if (collections.empty()) {
847 logout << "queryaction::search_multiple_collections: No collections "
848 << "set for doing multiple query - will search current collection\n";
849 textout << outconvert << disp << "_query:textwarningnocollections_\n";
850 return search_single_collection (args, args["c"], protos, browsers, disp,
851 outconvert, textout, logout);
852 }
853
854 // queryaction uses "VList" browser to display results,
855 // a queries clasification is "Search"
856 text_t browsertype = "VList";
857 text_t classification = "Search";
858
859 QueryResult_tset results;
860 map<text_t, colinfo_t, lttext_t> colinfomap;
861
862 ColInfoResponse_t cinfo;
863 comerror_t err;
864 FilterRequest_t request;
865 FilterResponse_t response;
866 request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
867 text_t formattedstring = args["q"];
868 text_t freqmsg = "_textfreqmsg1_";
869 int numdocs = 0;
870 isapprox isApprox = Exact;
871
872 format_querystring (formattedstring, args.getintarg("b"));
873 set_queryfilter_options (request, formattedstring, args);
874
875 // need to retrieve maxdocs matches for each collection
876 // (will eventually want to tidy this up, do so caching etc.)
877 OptionValue_t option;
878 option.name = "StartResults";
879 option.value = "1";
880 request.filterOptions.push_back (option);
881
882 option.name = "EndResults";
883 option.value = args["m"];
884 request.filterOptions.push_back (option);
885
886 text_tarray::iterator col_here = collections.begin();
887 text_tarray::iterator col_end = collections.end();
888
889 map<text_t, int, lttext_t> termfreqs;
890 while (col_here != col_end) {
891
892 request.fields.erase (request.fields.begin(), request.fields.end());
893 request.getParents = false;
894
895 recptproto *collectproto = protos->getrecptproto (*col_here, logout);
896 if (collectproto == NULL) {
897 logout << outconvert << "queryaction::search_multiple_collections: " << *col_here
898 << " collection has a NULL collectproto, ignoring\n";
899 col_here ++;
900 continue;
901 }
902 collectproto->get_collectinfo (*col_here, cinfo, err, logout);
903
904 browserclass *bptr = browsers->getbrowser (browsertype);
905
906 // get the formatstring if there is one
907 text_t formatstring;
908 if (!get_formatstring (classification, browsertype,
909 cinfo.format, formatstring))
910 formatstring = bptr->get_default_formatstring();
911
912 bptr->load_metadata_defaults (request.fields);
913
914 format_t *formatlistptr = new format_t();
915 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
916
917 colinfo_t thiscolinfo;
918 thiscolinfo.formatlistptr = formatlistptr;
919 thiscolinfo.browserptr = bptr;
920 colinfomap[*col_here] = thiscolinfo;
921
922 // do the query
923 collectproto->filter (*col_here, request, response, err, logout);
924 if (err != noError) {
925 outconvertclass text_t2ascii;
926 logout << text_t2ascii
927 << "queryaction::search_multiple_collections: call to QueryFilter failed "
928 << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
929 return false;
930 }
931
932 if (isApprox == Exact)
933 isApprox = response.isApprox;
934 else if (isApprox == MoreThan)
935 if (response.isApprox == Approximate)
936 isApprox = response.isApprox;
937
938 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
939 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
940 while (this_term != end_term) {
941 termfreqs[(*this_term).term] += (*this_term).freq;
942 if ((col_here+1) == col_end) {
943 freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
944 if ((this_term+1) != end_term) freqmsg += ", ";
945 }
946 this_term ++;
947 }
948
949 if (response.numDocs > 0) {
950 numdocs += response.numDocs;
951
952 QueryResult_t thisresult;
953 thisresult.collection = *col_here;
954 ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
955 ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
956 while (doc_here != doc_end) {
957 thisresult.doc = *doc_here;
958 results.insert (thisresult);
959 doc_here ++;
960 }
961 }
962 col_here ++;
963 }
964
965 disp.setmacro ("freqmsg", "query", freqmsg);
966
967 text_t resline;
968 if (num_phrases > 0) isApprox = Exact;
969 if (isApprox == Approximate) resline = "_textapprox_";
970 else if (isApprox == MoreThan) resline = "_textmorethan_";
971
972 if (numdocs == 0) resline = "_textnodocs_";
973 else if (numdocs == 1) resline += "_text1doc_";
974 else resline += text_t(numdocs) + " _textlotsdocs_";
975 disp.setmacro("resultline", "query", resline);
976
977 QueryResult_tset::iterator res_here = results.begin();
978 QueryResult_tset::iterator res_end = results.end();
979 text_tset metadata; // empty !!
980 bool getParents = false; // don't care !!
981 recptproto *collectproto = NULL;
982 bool use_table;
983 ResultDocInfo_t thisdoc;
984 format_t *formatlistptr = NULL;
985 browserclass *browserptr = NULL;
986
987 int maxdocs = args.getintarg("m");
988 int firstdoc = args.getintarg("r");
989 int hitsperpage = args.getintarg("o");
990 if (numdocs > maxdocs) numdocs = maxdocs;
991 if (hitsperpage == -1) hitsperpage = numdocs;
992
993 // set up _thisfirst_ and _thislast_ macros
994 disp.setmacro ("thisfirst", "query", firstdoc);
995 int thislast = firstdoc + (hitsperpage - 1);
996 if (thislast > numdocs) thislast = numdocs;
997 disp.setmacro ("thislast", "query", thislast);
998
999 // set up _prevfirst_ and _prevlast_ macros
1000 if (firstdoc > 1) {
1001 disp.setmacro ("prevlast", "query", firstdoc - 1);
1002 int prevfirst = firstdoc - hitsperpage;
1003 if (prevfirst < 1) prevfirst = 1;
1004 disp.setmacro ("prevfirst", "query", prevfirst);
1005 }
1006
1007 // set up _nextfirst_ and _nextlast_ macros
1008 if (thislast < numdocs) {
1009 disp.setmacro ("nextfirst", "query", thislast + 1);
1010 int nextlast = thislast + hitsperpage;
1011 if (nextlast > numdocs) nextlast = numdocs;
1012 disp.setmacro ("nextlast", "query", nextlast);
1013 }
1014
1015 textout << outconvert << disp << "_query:header_\n"
1016 << "_query:content_";
1017
1018 int count = 1;
1019
1020 // output results
1021 while (res_here != res_end) {
1022 if (count < firstdoc) {count ++; res_here ++; continue;}
1023 if (count > thislast) break;
1024 formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
1025 browserptr = colinfomap[(*res_here).collection].browserptr;
1026 thisdoc = (*res_here).doc;
1027 use_table = is_table_content (formatlistptr);
1028 browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0,
1029 formatlistptr, use_table, metadata, getParents,
1030 collectproto, disp, outconvert, textout, logout);
1031 // textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
1032 res_here ++;
1033 count ++;
1034 }
1035
1036 textout << outconvert << disp << "_query:footer_";
1037
1038 // clean up the format_t pointers
1039 map<text_t, colinfo_t, lttext_t>::iterator here = colinfomap.begin();
1040 map<text_t, colinfo_t, lttext_t>::iterator end = colinfomap.end();
1041 while (here != end) {
1042 delete ((*here).second.formatlistptr);
1043 here ++;
1044 }
1045 return true;
1046}
1047
1048bool queryaction::search_single_collection (cgiargsclass &args, const text_t &collection,
1049 recptprotolistclass *protos, browsermapclass *browsers,
1050 displayclass &disp, outconvertclass &outconvert,
1051 ostream &textout, ostream &logout) {
1052
1053 recptproto *collectproto = protos->getrecptproto (collection, logout);
1054 if (collectproto == NULL) {
1055 logout << outconvert << "queryaction::search_single_collection: " << collection
1056 << " collection has a NULL collectproto\n";
1057 return false;
1058 }
1059
1060 // queryaction uses "VList" browser to display results,
1061 // a queries clasification is "Search"
1062 text_t browsertype = "VList";
1063 text_t classification = "Search";
1064
1065 ColInfoResponse_t cinfo;
1066 comerror_t err;
1067 collectproto->get_collectinfo (collection, cinfo, err, logout);
1068
1069 browserclass *bptr = browsers->getbrowser (browsertype);
1070
1071 // get the formatstring if there is one
1072 text_t formatstring;
1073 if (!get_formatstring (classification, browsertype,
1074 cinfo.format, formatstring))
1075 formatstring = bptr->get_default_formatstring();
1076
1077 FilterRequest_t request;
1078 FilterResponse_t response;
1079 bptr->set_filter_options (request, args);
1080 bptr->load_metadata_defaults (request.fields);
1081
1082 format_t *formatlistptr = new format_t();
1083 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1084
1085 // do the query
1086 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
1087 text_t formattedstring = args["q"];
1088 if (!combine_query(args["z"], formattedstring)) {
1089 args["q"]=formattedstring;
1090 }
1091 format_querystring (formattedstring, args.getintarg("b"));
1092 set_queryfilter_options (request, formattedstring, args);
1093 collectproto->filter (collection, request, response, err, logout);
1094 if (err != noError) {
1095 outconvertclass text_t2ascii;
1096 logout << text_t2ascii
1097 << "queryaction::search_single_collections: call to QueryFilter failed "
1098 << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
1099 return false;
1100 }
1101
1102
1103 define_query_macros (args, disp, response);
1104
1105 // save the query if appropriate
1106 if (!save_search_history(args, response))
1107 logout << "save failed";
1108
1109 define_history_macros(disp, args, protos, logout);
1110
1111 textout << outconvert << disp << "_query:header_\n"
1112 << "_query:content_";
1113
1114 // output the results
1115 bool use_table = is_table_content (formatlistptr);
1116 bptr->output_section_group (response, args, collection, 0, formatlistptr,
1117 use_table, request.fields, request.getParents,
1118 collectproto, disp, outconvert, textout, logout);
1119
1120
1121 textout << outconvert << disp << "_query:footer_";
1122
1123 delete (formatlistptr);
1124
1125 return true;
1126}
1127
1128// define_query_macros sets the macros that couldn't be set until the
1129// query had been done. Those macros are _freqmsg_, _quotedquery_,
1130// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
1131// _thisfirst_, and _thislast_
1132void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
1133 const FilterResponse_t &response) {
1134
1135 // set up _freqmsg_ and _quotedquery_ macros
1136 text_t freqmsg = "_textfreqmsg1_";
1137 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1138 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1139 while (this_term != end_term) {
1140 freqmsg += (*this_term).term + ": " + (*this_term).freq;
1141 if ((this_term + 1) != end_term)
1142 freqmsg += ", ";
1143 this_term ++;
1144 }
1145
1146 disp.setmacro ("freqmsg", "query", freqmsg);
1147
1148
1149 // set up _resultline_ macro
1150 text_t resline;
1151 int maxdocs = args.getintarg("m");
1152 int numdocs = response.numDocs;
1153 if (maxdocs == -1) maxdocs = response.numDocs;
1154 isapprox isApprox = response.isApprox;
1155
1156 // if there were phrases (post-processing) we're not going to include
1157 // those documents that didn't match
1158 if (num_phrases > 0) isApprox = Exact;
1159
1160 if (isApprox == Approximate) resline = "_textapprox_";
1161 else if (isApprox == MoreThan) resline = "_textmorethan_";
1162
1163 if (numdocs == 0) resline = "_textnodocs_";
1164 else if (numdocs == 1) resline += "_text1doc_";
1165 else resline += text_t(numdocs) + " _textlotsdocs_";
1166
1167 disp.setmacro("resultline", "query", resline);
1168
1169 int firstdoc = args.getintarg("r");
1170 int hitsperpage = args.getintarg("o");
1171 if (hitsperpage == -1) hitsperpage = numdocs;
1172
1173 // set up _thisfirst_ and _thislast_ macros
1174 disp.setmacro ("thisfirst", "query", firstdoc);
1175 int thislast = firstdoc + (hitsperpage - 1);
1176 if (thislast > numdocs) thislast = numdocs;
1177 disp.setmacro ("thislast", "query", thislast);
1178
1179 // set up _prevfirst_ and _prevlast_ macros
1180 if (firstdoc > 1) {
1181 disp.setmacro ("prevlast", "query", firstdoc - 1);
1182 int prevfirst = firstdoc - hitsperpage;
1183 if (prevfirst < 1) prevfirst = 1;
1184 disp.setmacro ("prevfirst", "query", prevfirst);
1185 }
1186
1187 // set up _nextfirst_ and _nextlast_ macros
1188 if (thislast < numdocs) {
1189 disp.setmacro ("nextfirst", "query", thislast + 1);
1190 int nextlast = thislast + hitsperpage;
1191 if (nextlast > numdocs) nextlast = numdocs;
1192 disp.setmacro ("nextlast", "query", nextlast);
1193 }
1194}
1195
1196bool queryaction::save_search_history (cgiargsclass &args, const FilterResponse_t &response)
1197{
1198 if (args["q"]=="") return true; // null query, dont save
1199 if (args["hs"]=="0") return true; // only save when submit query
1200
1201 // get userid
1202 text_t userid = args["z"];
1203
1204 // the number of docs goes on the front of the query string
1205 int numdocs= response.numDocs;
1206 text_t query = text_t(numdocs);
1207 if (response.isApprox==MoreThan) { // there were more docs found
1208 query.push_back('+');
1209 }
1210 query += "a=q";
1211 query += "&c="+args["c"];
1212 query += "&h="+args["h"];
1213 query += "&t="+args["t"];
1214 query += "&b="+args["b"];
1215 query += "&j="+args["j"];
1216 query += "&n="+args["n"];
1217 query += "&s="+args["s"];
1218 query += "&k="+args["k"];
1219
1220 text_t qstring = args["q"];
1221 text_t formattedquery =cgi_safe(qstring);
1222 query += "&q="+formattedquery;
1223
1224 if (set_history_info(userid, query)) return true;
1225 else return false;
1226
1227
1228}
1229
1230
1231
1232
1233
Note: See TracBrowser for help on using the repository browser.