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

Last change on this file since 828 was 828, checked in by davidb, 24 years ago

Support for cross collection searching (CCS)

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