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

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

small change to cross-collection searching

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