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

Last change on this file since 776 was 776, checked in by sjboddie, 24 years ago

added multiplevalue option to cgiarginfo

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