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

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

A few changes to cross-collection searching for fao

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