source: main/trunk/greenstone2/runtime-src/src/recpt/queryaction.cpp@ 22984

Last change on this file since 22984 was 22984, checked in by ak19, 14 years ago
  1. Undoing commit of 22934 where decode_commas was called on stem and fold comma separated list: previously separated due to url-encoding of commas. Now that the problem has been fixed at the source, the decode_commas hack is no longer necessary. 2. Commas in stem and fold are no longer url-encoded because the multiple_value field of the continuously-reused struct arg_ainfo is always set back to the default false after ever being set to true. So it no longer subtly stays at true to affect Greenstone functioning in unforeseen ways (such as suddenly and unnecessarily URL-encoding commas where this is not wanted).
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 49.2 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 *********************************************************************/
25
26#include "queryaction.h"
27#include "querytools.h"
28#include "formattools.h"
29#include "cgiutils.h"
30#include "OIDtools.h"
31#include "fileutil.h"
32#include "text_t.h"
33#include "historydb.h"
34#include "htmlutils.h" // for html_safe in do_action
35#include "gsdltools.h"
36#include "phrases.h" // for get_phrases
37#include <stdlib.h> // for strtol
38#include <assert.h>
39
40
41queryaction::queryaction ()
42 : basequeryaction()
43{
44 num_phrases = 0;
45
46 cgiarginfo arg_ainfo;
47
48 // this action uses cgi variable "a"
49 arg_ainfo.shortname = "a";
50 arg_ainfo.longname = "action";
51 arg_ainfo.multiplechar = true;
52 arg_ainfo.multiplevalue = false;
53 arg_ainfo.defaultstatus = cgiarginfo::weak;
54 arg_ainfo.argdefault = "q";
55 arg_ainfo.savedarginfo = cgiarginfo::must;
56 argsinfo.addarginfo (NULL, arg_ainfo);
57
58 // "ct" - 0 = mg, 1 = mgpp, 2=lucene
59 arg_ainfo.shortname = "ct";
60 arg_ainfo.longname = "collection type";
61 arg_ainfo.multiplechar = true; // can be empty or single char
62 arg_ainfo.multiplevalue = false;
63 arg_ainfo.defaultstatus = cgiarginfo::weak;
64 arg_ainfo.argdefault = g_EmptyText;
65 arg_ainfo.savedarginfo = cgiarginfo::must;
66 argsinfo.addarginfo (NULL, arg_ainfo);
67
68 // "b" - 0 = simple, 1 = advanced
69 arg_ainfo.shortname = "b";
70 arg_ainfo.longname = "query mode";
71 arg_ainfo.multiplechar = false;
72 arg_ainfo.multiplevalue = false;
73 arg_ainfo.defaultstatus = cgiarginfo::weak;
74 arg_ainfo.argdefault = "0";
75 arg_ainfo.savedarginfo = cgiarginfo::must;
76 argsinfo.addarginfo (NULL, arg_ainfo);
77
78 // "h"
79 arg_ainfo.shortname = "h";
80 arg_ainfo.longname = "main index";
81 arg_ainfo.multiplechar = true;
82 arg_ainfo.multiplevalue = false;
83 arg_ainfo.defaultstatus = cgiarginfo::weak;
84 arg_ainfo.argdefault = g_EmptyText;
85 arg_ainfo.savedarginfo = cgiarginfo::must;
86 argsinfo.addarginfo (NULL, arg_ainfo);
87
88 // "h2"
89 arg_ainfo.shortname = "h2";
90 arg_ainfo.longname = "main index for second query";
91 arg_ainfo.multiplechar = true;
92 arg_ainfo.multiplevalue = false;
93 arg_ainfo.defaultstatus = cgiarginfo::weak;
94 arg_ainfo.argdefault = g_EmptyText;
95 arg_ainfo.savedarginfo = cgiarginfo::must;
96 argsinfo.addarginfo (NULL, arg_ainfo);
97
98 // "j"
99 arg_ainfo.shortname = "j";
100 arg_ainfo.longname = "sub collection index";
101 arg_ainfo.multiplechar = true;
102 arg_ainfo.multiplevalue = false;
103 arg_ainfo.defaultstatus = cgiarginfo::weak;
104 arg_ainfo.argdefault = g_EmptyText;
105 arg_ainfo.savedarginfo = cgiarginfo::must;
106 argsinfo.addarginfo (NULL, arg_ainfo);
107
108 // "j2"
109 arg_ainfo.shortname = "j2";
110 arg_ainfo.longname = "sub collection index for second query";
111 arg_ainfo.multiplechar = true;
112 arg_ainfo.multiplevalue = false;
113 arg_ainfo.defaultstatus = cgiarginfo::weak;
114 arg_ainfo.argdefault = g_EmptyText;
115 arg_ainfo.savedarginfo = cgiarginfo::must;
116 argsinfo.addarginfo (NULL, arg_ainfo);
117
118 // "n"
119 arg_ainfo.shortname = "n";
120 arg_ainfo.longname = "language index";
121 arg_ainfo.multiplechar = true;
122 arg_ainfo.multiplevalue = false;
123 arg_ainfo.defaultstatus = cgiarginfo::weak;
124 arg_ainfo.argdefault = g_EmptyText;
125 arg_ainfo.savedarginfo = cgiarginfo::must;
126 argsinfo.addarginfo (NULL, arg_ainfo);
127
128 // "n2"
129 arg_ainfo.shortname = "n2";
130 arg_ainfo.longname = "language index for second query";
131 arg_ainfo.multiplechar = true;
132 arg_ainfo.multiplevalue = false;
133 arg_ainfo.defaultstatus = cgiarginfo::weak;
134 arg_ainfo.argdefault = g_EmptyText;
135 arg_ainfo.savedarginfo = cgiarginfo::must;
136 argsinfo.addarginfo (NULL, arg_ainfo);
137
138
139 // "t" - 1 = ranked 0 = boolean
140 arg_ainfo.shortname = "t";
141 arg_ainfo.longname = "search type";
142 arg_ainfo.multiplechar = false;
143 arg_ainfo.multiplevalue = false;
144 arg_ainfo.defaultstatus = cgiarginfo::weak;
145 arg_ainfo.argdefault = "1";
146 arg_ainfo.savedarginfo = cgiarginfo::must;
147 argsinfo.addarginfo (NULL, arg_ainfo);
148
149 // "k"
150 arg_ainfo.shortname = "k";
151 arg_ainfo.longname = "casefolding";
152 arg_ainfo.multiplechar = false;
153 arg_ainfo.multiplevalue = false;
154 arg_ainfo.defaultstatus = cgiarginfo::weak;
155 arg_ainfo.argdefault = "1";
156 arg_ainfo.savedarginfo = cgiarginfo::must;
157 argsinfo.addarginfo (NULL, arg_ainfo);
158
159 // "ks"
160 arg_ainfo.shortname = "ks";
161 arg_ainfo.longname = "casefolding support";
162 arg_ainfo.multiplechar = false;
163 arg_ainfo.multiplevalue = false;
164 arg_ainfo.defaultstatus = cgiarginfo::weak;
165 arg_ainfo.argdefault = "0";
166 arg_ainfo.savedarginfo = cgiarginfo::must;
167 argsinfo.addarginfo (NULL, arg_ainfo);
168
169 // "s"
170 arg_ainfo.shortname = "s";
171 arg_ainfo.longname = "stemming";
172 arg_ainfo.multiplechar = false;
173 arg_ainfo.multiplevalue = false;
174 arg_ainfo.defaultstatus = cgiarginfo::weak;
175 arg_ainfo.argdefault = "0";
176 arg_ainfo.savedarginfo = cgiarginfo::must;
177 argsinfo.addarginfo (NULL, arg_ainfo);
178
179 // "ss"
180 arg_ainfo.shortname = "ss";
181 arg_ainfo.longname = "stemming support";
182 arg_ainfo.multiplechar = false;
183 arg_ainfo.multiplevalue = false;
184 arg_ainfo.defaultstatus = cgiarginfo::weak;
185 arg_ainfo.argdefault = "0";
186 arg_ainfo.savedarginfo = cgiarginfo::must;
187 argsinfo.addarginfo (NULL, arg_ainfo);
188
189 // "af"
190 arg_ainfo.shortname = "af";
191 arg_ainfo.longname = "accentfolding";
192 arg_ainfo.multiplechar = false;
193 arg_ainfo.multiplevalue = false;
194 arg_ainfo.defaultstatus = cgiarginfo::weak;
195 arg_ainfo.argdefault = "0";
196 arg_ainfo.savedarginfo = cgiarginfo::must;
197 argsinfo.addarginfo (NULL, arg_ainfo);
198
199 // "afs"
200 arg_ainfo.shortname = "afs";
201 arg_ainfo.longname = "accentfolding support";
202 arg_ainfo.multiplechar = false;
203 arg_ainfo.multiplevalue = false;
204 arg_ainfo.defaultstatus = cgiarginfo::weak;
205 arg_ainfo.argdefault = "0";
206 arg_ainfo.savedarginfo = cgiarginfo::must;
207 argsinfo.addarginfo (NULL, arg_ainfo);
208
209
210 // "ccs"
211 arg_ainfo.shortname = "ccs";
212 arg_ainfo.longname = "cross collection searching";
213 arg_ainfo.multiplechar = false;
214 arg_ainfo.multiplevalue = false;
215 arg_ainfo.defaultstatus = cgiarginfo::weak;
216 arg_ainfo.argdefault = "0";
217 arg_ainfo.savedarginfo = cgiarginfo::must;
218 argsinfo.addarginfo (NULL, arg_ainfo);
219
220 // "ccp"
221 arg_ainfo.shortname = "ccp";
222 arg_ainfo.longname = "cross collection page";
223 arg_ainfo.multiplechar = false;
224 arg_ainfo.multiplevalue = false;
225 arg_ainfo.defaultstatus = cgiarginfo::weak;
226 arg_ainfo.argdefault = "0";
227 arg_ainfo.savedarginfo = cgiarginfo::must;
228 argsinfo.addarginfo (NULL, arg_ainfo);
229
230 // "g" - new arg for granularity, for mgpp collections
231 arg_ainfo.shortname = "g";
232 arg_ainfo.longname = "granularity";
233 arg_ainfo.multiplechar = true;
234 arg_ainfo.multiplevalue = false;
235 arg_ainfo.defaultstatus = cgiarginfo::weak;
236 arg_ainfo.argdefault = g_EmptyText;
237 arg_ainfo.savedarginfo = cgiarginfo::must;
238 argsinfo.addarginfo (NULL, arg_ainfo);
239
240 // "ds" - start date
241 arg_ainfo.shortname = "ds";
242 arg_ainfo.longname = "start date";
243 arg_ainfo.multiplechar = true;
244 arg_ainfo.multiplevalue = false;
245 arg_ainfo.defaultstatus = cgiarginfo::weak;
246 arg_ainfo.argdefault = g_EmptyText;
247 arg_ainfo.savedarginfo = cgiarginfo::must;
248 argsinfo.addarginfo (NULL, arg_ainfo);
249
250 // "de" - end date
251 arg_ainfo.shortname = "de";
252 arg_ainfo.longname = "end date";
253 arg_ainfo.multiplechar = true;
254 arg_ainfo.multiplevalue = false;
255 arg_ainfo.defaultstatus = cgiarginfo::weak;
256 arg_ainfo.argdefault = g_EmptyText;
257 arg_ainfo.savedarginfo = cgiarginfo::must;
258 argsinfo.addarginfo (NULL, arg_ainfo);
259
260 // "dsbc" - whether or not start date is prechristian
261 arg_ainfo.shortname = "dsbc";
262 arg_ainfo.longname = "start date bc";
263 arg_ainfo.multiplechar = false;
264 arg_ainfo.multiplevalue = false;
265 arg_ainfo.defaultstatus = cgiarginfo::weak;
266 arg_ainfo.argdefault = "0";
267 arg_ainfo.savedarginfo = cgiarginfo::must;
268 argsinfo.addarginfo (NULL, arg_ainfo);
269
270 // "debc" - whether or not end date is prechristian
271 arg_ainfo.shortname = "debc";
272 arg_ainfo.longname = "end date bc";
273 arg_ainfo.multiplechar = false;
274 arg_ainfo.multiplevalue = false;
275 arg_ainfo.defaultstatus = cgiarginfo::weak;
276 arg_ainfo.argdefault = "0";
277 arg_ainfo.savedarginfo = cgiarginfo::must;
278 argsinfo.addarginfo (NULL, arg_ainfo);
279
280 // "qt" - 0 = text, 1 = form
281 arg_ainfo.shortname = "qt";
282 arg_ainfo.longname = "query type";
283 arg_ainfo.multiplechar = true; // can be empty or single char
284 arg_ainfo.multiplevalue = false;
285 arg_ainfo.defaultstatus = cgiarginfo::weak;
286 arg_ainfo.argdefault = g_EmptyText;
287 arg_ainfo.savedarginfo = cgiarginfo::must;
288 argsinfo.addarginfo (NULL, arg_ainfo);
289
290 // "qto" - 1 = text only, 2 = form only, 3 = text and form
291 arg_ainfo.shortname = "qto";
292 arg_ainfo.longname = "query type options";
293 arg_ainfo.multiplechar = true; // can be empty or single char
294 arg_ainfo.multiplevalue = false;
295 arg_ainfo.defaultstatus = cgiarginfo::weak;
296 arg_ainfo.argdefault = g_EmptyText;
297 arg_ainfo.savedarginfo = cgiarginfo::must;
298 argsinfo.addarginfo (NULL, arg_ainfo);
299
300 // "qb" - 0 = regular, 1 = large
301 arg_ainfo.shortname = "qb";
302 arg_ainfo.longname = "query box type";
303 arg_ainfo.multiplechar = false;
304 arg_ainfo.multiplevalue = false;
305 arg_ainfo.defaultstatus = cgiarginfo::weak;
306 arg_ainfo.argdefault = "0";
307 arg_ainfo.savedarginfo = cgiarginfo::must;
308 argsinfo.addarginfo (NULL, arg_ainfo);
309
310
311 // "fqs" - the list of stemming options in the form query
312 // - a comma separated list
313 arg_ainfo.shortname = "fqs";
314 arg_ainfo.longname = "form query stems";
315 arg_ainfo.multiplechar = true;
316 arg_ainfo.multiplevalue = false;
317 arg_ainfo.defaultstatus = cgiarginfo::weak;
318 arg_ainfo.argdefault = g_EmptyText;
319 arg_ainfo.savedarginfo = cgiarginfo::must;
320 argsinfo.addarginfo (NULL, arg_ainfo);
321
322
323 // "fqk" - the list of casefolding options in the form query
324 // - a comma separated list
325 arg_ainfo.shortname = "fqk";
326 arg_ainfo.longname = "form query casefolds";
327 arg_ainfo.multiplechar = true;
328 arg_ainfo.multiplevalue = false;
329 arg_ainfo.defaultstatus = cgiarginfo::weak;
330 arg_ainfo.argdefault = g_EmptyText;
331 arg_ainfo.savedarginfo = cgiarginfo::must;
332 argsinfo.addarginfo (NULL, arg_ainfo);
333
334 // "cc"
335 arg_ainfo.shortname = "cc";
336 arg_ainfo.longname = "collections to search";
337 arg_ainfo.multiplechar = true;
338 arg_ainfo.multiplevalue = true;
339 arg_ainfo.defaultstatus = cgiarginfo::weak;
340 arg_ainfo.argdefault = g_EmptyText;
341 arg_ainfo.savedarginfo = cgiarginfo::must;
342 argsinfo.addarginfo (NULL, arg_ainfo);
343
344
345 // ****
346 // should this even be here???
347 // seems to be mixed up between "sf" and "sqlsf"
348
349 // "sf" - Sort field. Set to field to be used for sorting search reult
350 // set (only implemented for lucene collections at present).
351 arg_ainfo.shortname = "sqlsf";
352 arg_ainfo.longname = "sql sort field";
353 arg_ainfo.multiplechar = true;
354 arg_ainfo.multiplevalue = false;
355 arg_ainfo.defaultstatus = cgiarginfo::weak;
356 arg_ainfo.argdefault = g_EmptyText;
357 arg_ainfo.savedarginfo = cgiarginfo::must;
358 argsinfo.addarginfo (NULL, arg_ainfo);
359
360
361}
362
363queryaction::~queryaction ()
364{
365}
366
367void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
368 basequeryaction::configure (key, cfgline);
369}
370
371bool queryaction::init (ostream &logout) {
372 return basequeryaction::init (logout);
373}
374
375bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
376 recptprotolistclass* protos, ostream &logout) {
377
378 // check t argument
379 int arg_t = args.getintarg("t");
380 if (arg_t != 0 && arg_t != 1) {
381 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
382 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
383 if (tinfo != NULL) args["t"] = tinfo->argdefault;
384 }
385
386 // check k argument
387 int arg_k = args.getintarg("k");
388 if (arg_k != 0 && arg_k != 1) {
389 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
390 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
391 if (kinfo != NULL) args["k"] = kinfo->argdefault;
392 }
393
394 // check s argument
395 int arg_s = args.getintarg("s");
396 if (arg_s != 0 && arg_s != 1) {
397 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
398 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
399 if (sinfo != NULL) args["s"] = sinfo->argdefault;
400 }
401
402
403 // check ct argument
404 int arg_ct = args.getintarg("ct");
405 if (arg_ct < 0 || arg_ct > 2) {
406 logout << "Warning: \"ct\" argument out of range (" << arg_ct << ")\n";
407 cgiarginfo *ctinfo = argsinfo.getarginfo ("ct");
408 if (ctinfo != NULL) args["ct"] = ctinfo->argdefault;
409 }
410
411 // check qt argument
412 int arg_qt = args.getintarg("qt");
413 if (arg_qt !=0 && arg_qt !=1) {
414 logout << "Warning: \"qt\" argument out of range (" << arg_qt << ")\n";
415 cgiarginfo *qtinfo = argsinfo.getarginfo ("qt");
416 if (qtinfo != NULL) args["qt"] = qtinfo->argdefault;
417 }
418
419 // check qb argument
420 int arg_qb = args.getintarg("qb");
421 if (arg_qb !=0 && arg_qb !=1) {
422 logout << "Warning: \"qb\" argument out of range (" << arg_qb << ")\n";
423 cgiarginfo *qbinfo = argsinfo.getarginfo ("qb");
424 if (qbinfo != NULL) args["qb"] = qbinfo->argdefault;
425 }
426
427 // check fqa argument
428 int arg_fqa = args.getintarg("fqa");
429 if (arg_fqa !=0 && arg_fqa !=1) {
430 logout << "Warning: \"fqa\" argument out of range (" << arg_fqa << ")\n";
431 cgiarginfo *fqainfo = argsinfo.getarginfo ("fqa");
432 if (fqainfo != NULL) args["fqa"] = fqainfo->argdefault;
433 }
434
435 // check fqn argument
436 int arg_fqn = args.getintarg("fqn");
437 if (arg_fqn < -1) {
438 logout << "Warning: \"fqn\" argument less than -1 (" << arg_fqn << ")\n";
439 cgiarginfo *fqninfo = argsinfo.getarginfo ("fqn");
440 if (fqninfo != NULL) args["fqn"] = fqninfo->argdefault;
441 }
442
443 return basequeryaction::check_cgiargs(argsinfo,args,protos,logout);
444
445}
446
447void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
448 recptprotolistclass * protos,
449 ostream &logout)
450{
451 basequeryaction::define_internal_macros(disp,args,protos,logout);
452
453 define_query_interface(disp, args, protos, logout);
454}
455
456void queryaction::define_query_interface(displayclass &disp,
457 cgiargsclass &args,
458 recptprotolistclass * protos,
459 ostream &logout){
460 text_t collection = args["c"];
461
462 //check that the protocol is alive
463 recptproto* colproto = protos->getrecptproto (collection, logout);
464 if(colproto == NULL) {
465 logout << "ERROR: Null collection protocol trying to query"
466 << collection.getcstr() << "\n";
467 return;
468 }
469
470 //check the collection is responding/in place
471 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(colproto, collection,
472 logout);
473 if(colinfo == NULL){
474 logout << "ERROR: Null returned for get_collectinfo_ptr on "
475 << collection.getcstr() << "in queryaction::define_query_interface\n";
476 return;
477 }
478
479 text_tmap::iterator check = colinfo->format.find("QueryInterface");
480 if(check != colinfo->format.end()){
481 if((*check).second=="DateSearch"){
482 text_t current = "_datesearch_";
483 disp.setmacro("optdatesearch","query",current);
484 }
485 }
486}
487
488
489
490void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
491 recptprotolistclass *protos, ostream &logout) {
492
493 // define_external_macros sets the following macros:
494
495 // some or all of these may not be required to be set
496 // _hselection_, _h2selection_ the selection box for the main part of the index
497 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
498 // _nselection_, _n2selection_ the selection box for the language part of the index
499 // _cq2selection the selection box for combining two queries
500
501 // _gselection_, the selection box forlevels (mgpp)
502 // _fqfselection_, the selection box for index/fields (mgpp)
503 // can't do anything if collectproto is null (i.e. no collection was specified)
504 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
505 if (collectproto == NULL) return;
506
507 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto,
508 args["c"],
509 logout);
510 set_query_type_args(colinfo, args);
511 set_stem_index_args(colinfo, args);
512
513 comerror_t err;
514 InfoFilterOptionsResponse_t response;
515 InfoFilterOptionsRequest_t request;
516 request.filterName = "QueryFilter";
517
518 collectproto->get_filteroptions (args["c"], request, response, err, logout);
519 if (err == noError) {
520
521 FilterOption_tmap::const_iterator it;
522 FilterOption_tmap::const_iterator end = response.filterOptions.end();
523
524 // _hselection_ and _h2selection_ (Index)
525 it = response.filterOptions.find ("Index");
526 if (it != end) set_option_macro ("h", args["h"], true, false, (*it).second, disp);
527 if (it != end) set_option_macro ("h2", args["h2"], true,false, (*it).second, disp);
528
529 // _jselection_ and _j2selection_ (Subcollection)
530 it = response.filterOptions.find ("Subcollection");
531 if (it != end) set_option_macro ("j", args["j"], true,false, (*it).second, disp);
532 if (it != end) set_option_macro ("j2", args["j2"], true,false, (*it).second, disp);
533
534 // _nselection_ and _n2selection_ (Language)
535 it = response.filterOptions.find ("Language");
536 if (it != end) set_option_macro ("n", args["n"], true,false, (*it).second, disp);
537 if (it != end) set_option_macro ("n2", args["n2"], true,false, (*it).second, disp);
538
539 // _cq2selection_ (CombineQuery)
540 it = response.filterOptions.find ("CombineQuery");
541 if (it != end) set_option_macro ("cq2", args["cq2"], true,false, (*it).second, disp);
542
543 if ((args["ct"] == "1") || (args["ct"] == "2")) { // mgpp/lucene collections
544 // _gselection_ (Level)
545 it = response.filterOptions.find("Level");
546 if (it!=end) {
547 set_option_macro("g", args["g"], false, false, (*it).second, disp);
548 if (args["qt"]=="1") { // form search
549 set_gformselection_macro(args["g"], (*it).second, disp);
550 }
551 }
552 // _fqfselection_ field list
553 it = response.filterOptions.find("IndexField");
554 if (it!=end) {
555 bool form_search = false;
556 if (args["qto"]=="2" || args["qt"]=="1") {
557 form_search = true;
558 }
559 set_option_macro ("fqf", args["fqf"], true, form_search, (*it).second, disp);
560 if (args["ct"] == "2") {// lucene
561 // set the sort field macro
562 set_sfselection_macro(args["sf"], (*it).second, disp);
563 }
564 }
565 }
566
567 // add a queryterms macro for plain version of search terms
568 if (!args["q"].empty()|| !args["fqv"].empty()) {
569 text_t query_arg = "";
570 if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
571 query_arg = args["q"];
572 }
573 else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
574
575 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
576 query_arg = args["q"];
577 }
578 else { // form search
579 query_arg = args["fqv"];
580 }
581 }
582 disp.setmacro ("queryterms", displayclass::defaultpackage, get_plain_query_terms(query_arg, args["ct"]));
583 }
584
585 }
586} // define external macros
587
588
589void queryaction::set_sfselection_macro(text_t current_value,
590 const FilterOption_t &option,
591 displayclass &disp) {
592
593 // we need at least one option here to continue
594 if (option.validValues.size() < 1) {
595 return;
596 }
597
598 text_t macrovalue = "<select name=\"sf\">\n";
599
600 if (current_value.empty()) current_value = "";
601
602 // we give a rank option first
603 macrovalue += "<option value=\"\"";
604 if (current_value == "") {
605 macrovalue += " selected";
606 }
607 macrovalue += ">_query:textsortbyrank_\n";
608
609 text_tarray::const_iterator thisvalue = option.validValues.begin();
610 text_tarray::const_iterator endvalue = option.validValues.end();
611 int valid_count = 0;
612 while (thisvalue != endvalue) {
613 if (*thisvalue != "ZZ" && *thisvalue != "TX") {
614 ++valid_count;
615 macrovalue += "<option value=\"by" + *thisvalue + "\"";
616 if (current_value == "by"+*thisvalue)
617 macrovalue += " selected";
618 macrovalue += ">_" + *thisvalue + "_\n";
619 }
620 ++thisvalue;
621 }
622 macrovalue += "</select>";
623 if (valid_count > 0) {
624 disp.setmacro ("sfselection", displayclass::defaultpackage, macrovalue);
625 }
626
627}
628
629// sets the selection box macro _gformselection_.
630// the default for _gformselection_ is _gselection_
631void queryaction::set_gformselection_macro (text_t current_value,
632 const FilterOption_t &option,
633 displayclass &disp) {
634
635 if (option.validValues.size() <= 1) {
636 return;
637 }
638 // we need to check to see if there is paragraph present
639 text_tarray::const_iterator thisvalue = option.validValues.begin();
640 text_tarray::const_iterator endvalue = option.validValues.end();
641
642 bool has_paras = false;
643 while (thisvalue != endvalue) {
644 if (*thisvalue == "Para") {
645 has_paras = true;
646 break;
647 }
648 ++thisvalue;
649 }
650 if (!has_paras) return; // there is no difference between the form selection and the normal one
651
652 if (option.validValues.size() == 2) {
653 // we will only have one value, but we will still put it in as a text string
654 int opt = 0;
655 if (option.validValues[0] == "Para") {
656 opt = 1;
657 }
658 disp.setmacro ("gformselection", displayclass::defaultpackage, "_"+option.validValues[opt]+"_");
659 return;
660 }
661
662 // there will be a select box
663 text_t macrovalue = "<select name=\"g\">\n";
664
665 if (current_value.empty()) current_value = option.defaultValue;
666
667 thisvalue = option.validValues.begin();
668
669 while (thisvalue != endvalue) {
670 if (*thisvalue != "Para") {
671 macrovalue += "<option value=\"" + *thisvalue + "\"";
672 if (*thisvalue == current_value)
673 macrovalue += " selected";
674 macrovalue += ">_" + *thisvalue + "_\n";
675 }
676 ++thisvalue;
677 }
678 macrovalue += "</select>\n";
679 disp.setmacro ("gformselection", displayclass::defaultpackage, macrovalue);
680}
681
682void queryaction::define_form_macros (displayclass &disp, cgiargsclass &args,
683 recptprotolistclass *protos,
684 ostream &logout)
685{
686
687 // defines the following macros
688 // _regformlist_
689 // _advformlist_
690
691 if (args["ct"]=="0" || args["qto"]=="1" || (args["qto"]=="3" && args["qt"] == "0") ) // mg, or mgpp/lucene with plain only, or mgpp with both, but set to plain
692 return; // dont need these macros
693
694 text_t form = "";
695 int argfqn = args.getintarg("fqn");
696
697 if (args["b"] == "1") { // advanced form
698 form += "_firstadvformelement_\n";
699 for (int i=1; i<argfqn; ++i) {
700 form += "_advformelement_\n";
701 }
702 disp.setmacro("advformlist", "query", form);
703 }
704 else { // simple form
705 for (int i=0; i<argfqn; ++i) {
706 form += "_regformelement_\n";
707 }
708 disp.setmacro("regformlist", "query", form);
709 }
710
711}
712
713void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
714 displayclass &disp, outconvertclass &outconvert,
715 ostream &textout, ostream &logout) {
716
717 ColInfoResponse_t *cinfo = NULL;
718 comerror_t err;
719 InfoFilterOptionsResponse_t fresponse;
720 InfoFilterOptionsRequest_t frequest;
721 frequest.filterName = "QueryFilter";
722
723 text_t &index = args["h"];
724 text_t &subcollection = args["j"];
725 text_t &language = args["n"];
726
727 text_tset collections;
728 text_t arg_cc = args["cc"];
729 decode_cgi_arg (arg_cc);
730 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
731
732 textout << outconvert << disp << "_query:header_\n"
733 << "<center>_navigationbar_</center><br>\n"
734 << "<form name=\"QueryForm\" method=\"get\" action=\"_gwcgi_\">\n"
735 << "<input type=\"hidden\" name=\"a\" value=\"q\">\n"
736 << "<input type=\"hidden\" name=\"site\" value=\"_cgiargsite_\"\n"
737 << "<input type=\"hidden\" name=\"e\" value=\"_compressedoptions_\">\n"
738 << "<input type=\"hidden\" name=\"ccp\" value=\"1\">\n"
739 << "<center><table width=\"_pagewidth_\"><tr valign=\"top\">\n"
740 << "<td>Select collections to search for \"" << args["q"]
741 << "\" <i>(index=" << index << " subcollection=" << subcollection
742 << " language=" << language << ")</i></td>\n"
743 << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
744 << "</tr></table></center>\n"
745 << "<center><table width=\"_pagewidth_\">\n"
746 << "<tr><td>\n";
747
748 recptprotolistclass::iterator rprotolist_here = protos->begin();
749 recptprotolistclass::iterator rprotolist_end = protos->end();
750 while (rprotolist_here != rprotolist_end) {
751 if ((*rprotolist_here).p != NULL) {
752
753 text_tarray collist;
754 (*rprotolist_here).p->get_collection_list (collist, err, logout);
755 if (err == noError) {
756 text_tarray::iterator collist_here = collist.begin();
757 text_tarray::iterator collist_end = collist.end();
758 while (collist_here != collist_end) {
759
760 cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
761 // if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
762 if (cinfo != NULL && (cinfo->buildDate > 0)) {
763
764 (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
765 if (err == noError) {
766
767 FilterOption_tmap::const_iterator it;
768 FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
769 if (!index.empty()) {
770 it = fresponse.filterOptions.find ("Index");
771 if (it == end) {++collist_here; continue;}
772 text_tarray::const_iterator there = (*it).second.validValues.begin();
773 text_tarray::const_iterator tend = (*it).second.validValues.end();
774 while (there != tend) {
775 if (*there == index) break;
776 ++there;
777 }
778 if (there == tend) {++collist_here; continue;}
779 }
780 if (!subcollection.empty()) {
781 it = fresponse.filterOptions.find ("Subcollection");
782 if (it == end) {++collist_here; continue;}
783 text_tarray::const_iterator there = (*it).second.validValues.begin();
784 text_tarray::const_iterator tend = (*it).second.validValues.end();
785 while (there != tend) {
786 if (*there == subcollection) break;
787 ++there;
788 }
789 if (there == tend) {++collist_here; continue;}
790 }
791 if (!language.empty()) {
792 it = fresponse.filterOptions.find ("Language");
793 if (it == end) {++collist_here; continue;}
794 text_tarray::const_iterator there = (*it).second.validValues.begin();
795 text_tarray::const_iterator tend = (*it).second.validValues.end();
796 while (there != tend) {
797 if (*there == language) break;
798 ++there;
799 }
800 if (there == tend) {++collist_here; continue;}
801 }
802
803 // we've got a matching collection
804 textout << outconvert << "<input type=\"checkbox\"";
805
806 text_tset::const_iterator t = collections.find (*collist_here);
807 if (t != collections.end()) textout << outconvert << " checked";
808
809 text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
810 if (collectionname.empty()) {
811 collectionname = *collist_here;
812 }
813 textout << outconvert << disp
814 << " name=\"cc\" value=\"" << *collist_here << "\">"
815 << collectionname << "<br>\n";
816
817
818 }
819 }
820 ++collist_here;
821 }
822 }
823 }
824 ++rprotolist_here;
825 }
826 textout << outconvert << disp
827 << "</td></tr></table></center>\n"
828 << "</form>\n"
829 << "_query:footer_\n";
830
831}
832
833bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos,
834 browsermapclass *browsers, displayclass &disp,
835 outconvertclass &outconvert, ostream &textout,
836 ostream &logout) {
837
838 if (recpt == NULL) {
839 logout << "ERROR (queryaction::do_action): This action does not contain information\n"
840 << " about any receptionists. The method set_receptionist was probably\n"
841 << " not called from the module which instantiated this action.\n";
842 return true;
843 }
844
845 if (args["ccs"] == "1") {
846 if (!args["cc"].empty()) {
847 // query the selected collections
848 text_t::const_iterator b = args["cc"].begin();
849 text_t::const_iterator e = args["cc"].end();
850 if (findchar (b, e, ',') != e) {
851 if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
852 textout, logout)) return false;
853 return true;
854 } else {
855 if (!search_single_collection (args, args["cc"], protos, browsers, disp,
856 outconvert, textout, logout)) return false;
857 return true;
858 }
859 }
860 }
861
862 // simply query the current collection
863 if (!search_single_collection (args, args["c"], protos, browsers, disp,
864 outconvert, textout, logout)) return false;
865 return true;
866}
867
868
869
870// request.filterResultOptions and request.fields (if required) should
871// be set from the calling code
872void queryaction::set_queryfilter_options (FilterRequest_t &request,
873 const text_t &querystring,
874 cgiargsclass &args)
875{
876 set_fulltext_queryfilter_options(request,querystring,args);
877}
878
879
880
881void queryaction::set_queryfilter_options (FilterRequest_t &request,
882 const text_t &querystring1,
883 const text_t &querystring2,
884 cgiargsclass &args)
885{
886 set_fulltext_queryfilter_options(request,querystring1,querystring2,args);
887}
888
889
890
891bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
892 browsermapclass *browsers, displayclass &disp,
893 outconvertclass &outconvert, ostream &textout,
894 ostream &logout) {
895
896 text_tarray collections;
897
898 text_t arg_cc = args["cc"];
899 decode_cgi_arg (arg_cc);
900 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
901
902 if (collections.empty()) {
903 logout << "queryaction::search_multiple_collections: No collections "
904 << "set for doing multiple query - will search current collection\n";
905 textout << outconvert << disp << "_query:textwarningnocollections_\n";
906 return search_single_collection (args, args["c"], protos, browsers, disp,
907 outconvert, textout, logout);
908 }
909
910 // queryaction uses "VList" browser to display results,
911 // a queries clasification is "Search"
912 text_t browsertype = "VList";
913 text_t classification = "Search";
914
915 QueryResult_tset results;
916 map<text_t, colinfo_t, lttext_t> colinfomap;
917
918 ColInfoResponse_t *cinfo = NULL;
919 recptproto *collectproto = NULL;
920 comerror_t err;
921 FilterRequest_t request;
922 FilterResponse_t response;
923 request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
924 text_t freqmsg = "_textfreqmsg1_";
925 int numdocs = 0;
926 isapprox isApprox = Exact;
927
928 // what to do about segmentation for multiple colls??
929 bool segment = false;
930 text_t formattedstring = "";
931 get_formatted_query_string(formattedstring, segment, args, disp, logout);
932
933 if (formattedstring.empty()) {
934 // dont bother doing a query if no query string
935 define_history_macros (disp, args, protos, logout);
936 textout << outconvert << disp << "_query:header_\n"
937 << "_query:content_";
938 textout << outconvert << disp << "_query:footer_";
939
940 return true;
941 }
942 bool syntax_error = false;
943
944 set_queryfilter_options (request, formattedstring, args);
945
946 // need to retrieve maxdocs matches for each collection
947 // (will eventually want to tidy this up, do so caching etc.)
948 OptionValue_t option;
949 option.name = "StartResults";
950 option.value = "1";
951 request.filterOptions.push_back (option);
952
953 option.name = "EndResults";
954 option.value = args["m"];
955 request.filterOptions.push_back (option);
956
957 text_tarray::iterator col_here = collections.begin();
958 text_tarray::iterator col_end = collections.end();
959
960 map<text_t, int, lttext_t> termfreqs;
961
962 // just check the main col for formatting info - use individual format statements, or the main one?
963
964 browserclass *bptr = browsers->getbrowser (browsertype);
965
966 text_t main_col = args["c"];
967 cinfo = recpt->get_collectinfo_ptr (collectproto, main_col, logout);
968 if (cinfo == NULL) {
969 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL for '"<<main_col<<"'\n";
970 return false;
971 }
972
973 bool use_main_col_format = false;
974 if (cinfo->ccsOptions & CCSUniformSearchResultsFormatting) {
975 use_main_col_format = true;
976 }
977
978 request.fields.erase (request.fields.begin(), request.fields.end());
979 request.getParents = false;
980 bptr->load_metadata_defaults (request.fields);
981
982 text_t formatstring;
983 format_t *formatlistptr = new format_t();
984 if (use_main_col_format) {
985 // just get one format for main coll and use it for each subcol
986 if (!get_formatstring (classification, browsertype,
987 cinfo->format, formatstring)) {
988 formatstring = bptr->get_default_formatstring();
989 }
990
991 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
992 }
993
994 while (col_here != col_end) {
995
996 collectproto = protos->getrecptproto (*col_here, logout);
997 if (collectproto == NULL) {
998 logout << outconvert << "queryaction::search_multiple_collections: " << *col_here
999 << " collection has a NULL collectproto, ignoring\n";
1000 ++col_here;
1001 continue;
1002 }
1003 cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
1004 if (cinfo == NULL) {
1005 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL\n";
1006 ++col_here;
1007 continue;
1008 }
1009
1010 if (!use_main_col_format) {
1011 request.fields.erase (request.fields.begin(), request.fields.end());
1012 request.getParents = false;
1013 bptr->load_metadata_defaults (request.fields);
1014
1015 //browserclass *bptr = browsers->getbrowser (browsertype);
1016
1017 // get the formatstring if there is one
1018 if (!get_formatstring (classification, browsertype,
1019 cinfo->format, formatstring)) {
1020 formatstring = bptr->get_default_formatstring();
1021 }
1022
1023 formatlistptr = new format_t();
1024 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1025 }
1026
1027 colinfo_t thiscolinfo;
1028 thiscolinfo.formatlistptr = formatlistptr;
1029 thiscolinfo.browserptr = bptr;
1030 colinfomap[*col_here] = thiscolinfo;
1031
1032 // do the query
1033 collectproto->filter (*col_here, request, response, err, logout);
1034 if (err != noError && err != syntaxError) {
1035 outconvertclass text_t2ascii;
1036 logout << text_t2ascii
1037 << "queryaction::search_multiple_collections: call to QueryFilter failed "
1038 << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
1039 return false;
1040 }
1041
1042 if (err == syntaxError) {
1043 syntax_error = true;
1044 freqmsg = "_textinvalidquery_";
1045 // assume the syntax will be invalid for all colls
1046 break;
1047 }
1048 if (response.error_message == "TOO_MANY_CLAUSES") {
1049 freqmsg = "_textlucenetoomanyclauses_";
1050 break;
1051 }
1052 if (isApprox == Exact)
1053 isApprox = response.isApprox;
1054 else if (isApprox == MoreThan)
1055 if (response.isApprox == Approximate)
1056 isApprox = response.isApprox;
1057
1058 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1059 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1060 while (this_term != end_term) {
1061 termfreqs[(*this_term).term] += (*this_term).freq;
1062 if ((col_here+1) == col_end) {
1063 freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
1064 if ((this_term+1) != end_term) freqmsg += ", ";
1065 }
1066 ++this_term;
1067 }
1068
1069 if (response.numDocs > 0) {
1070 numdocs += response.numDocs;
1071
1072 QueryResult_t thisresult;
1073 thisresult.collection = *col_here;
1074 ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
1075 ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
1076 while (doc_here != doc_end) {
1077 thisresult.doc = *doc_here;
1078 results.insert (thisresult);
1079 ++doc_here;
1080 }
1081 }
1082 ++col_here;
1083 } // for each coll
1084
1085 text_t numdocs_t = numdocs;
1086 args["nmd"] = numdocs_t;
1087
1088 disp.setmacro ("freqmsg", "query", freqmsg);
1089
1090 define_query_macros( args, disp, numdocs, isApprox);
1091 // save the query if appropriate
1092 save_search_history(args, numdocs, isApprox);
1093 define_history_macros (disp, args, protos, logout);
1094
1095 textout << outconvert << disp << "_query:header_\n"
1096 << "_query:content_";
1097
1098 if (!syntax_error) {
1099
1100 // now go through each result and output it
1101 QueryResult_tset::iterator res_here = results.begin();
1102 QueryResult_tset::iterator res_end = results.end();
1103 text_tset metadata; // empty !!
1104 bool getParents = false; // don't care !!
1105 bool use_table;
1106 ResultDocInfo_t thisdoc;
1107 format_t *formatlistptr = NULL;
1108 browserclass *browserptr = NULL;
1109
1110 int count = 1;
1111 int firstdoc = args.getintarg("r");
1112 int hitsperpage = args.getintarg("o");
1113 int thislast = firstdoc + (hitsperpage - 1);
1114
1115 // output results
1116 while (res_here != res_end) {
1117 if (count < firstdoc) {++count; ++res_here; continue;}
1118 if (count > thislast) break;
1119
1120 formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
1121 browserptr = colinfomap[(*res_here).collection].browserptr;
1122 thisdoc = (*res_here).doc;
1123 use_table = is_table_content (formatlistptr);
1124
1125 collectproto = protos->getrecptproto ((*res_here).collection, logout);
1126 if (collectproto == NULL) {
1127 logout << outconvert << "queryaction::search_multiple_collections: " << (*res_here).collection
1128 << " collection has a NULL collectproto, ignoring results\n";
1129 ++res_here;
1130 continue;
1131 }
1132
1133 browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0,
1134 formatlistptr, use_table, metadata, getParents,
1135 collectproto, disp, outconvert, textout, logout);
1136 // textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
1137 ++res_here;
1138 ++count;
1139 }
1140 }
1141 textout << outconvert << disp << "_query:footer_";
1142
1143 // clean up the format_t pointers
1144 map<text_t, colinfo_t, lttext_t>::iterator here = colinfomap.begin();
1145 map<text_t, colinfo_t, lttext_t>::iterator end = colinfomap.end();
1146 while (here != end) {
1147 delete ((*here).second.formatlistptr);
1148 ++here;
1149 }
1150 return true;
1151}
1152
1153bool queryaction::search_single_collection (cgiargsclass &args, const text_t &collection,
1154 recptprotolistclass *protos, browsermapclass *browsers,
1155 displayclass &disp, outconvertclass &outconvert,
1156 ostream &textout, ostream &logout) {
1157
1158 recptproto *collectproto = protos->getrecptproto (collection, logout);
1159 if (collectproto == NULL) {
1160 logout << outconvert << "queryaction::search_single_collection: " << collection
1161 << " collection has a NULL collectproto\n";
1162
1163 // Display the "this collection is not installed on this system" page
1164 disp.setmacro("cvariable", displayclass::defaultpackage, collection);
1165 disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
1166
1167 textout << outconvert << disp << "_query:header_\n"
1168 << "_query:content_\n" << "_query:footer_\n";
1169 return true;
1170 }
1171
1172 // queryaction uses "VList" browser to display results,
1173 // a queries clasification is "Search"
1174 text_t browsertype = "VList";
1175 text_t classification = "Search";
1176
1177 comerror_t err;
1178 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
1179
1180 if (cinfo == NULL) {
1181 logout << "ERROR (query_action::search_single_collection): get_collectinfo_ptr returned NULL\n";
1182 return false;
1183 }
1184
1185 bool segment = cinfo->isSegmented;
1186 browserclass *bptr = browsers->getbrowser (browsertype);
1187
1188 // get the formatstring if there is one
1189 text_t formatstring;
1190 if (!get_formatstring (classification, browsertype,
1191 cinfo->format, formatstring)) {
1192 formatstring = bptr->get_default_formatstring();
1193 }
1194 FilterRequest_t request;
1195 FilterResponse_t response;
1196
1197 text_t hits_per_page_old = args["o"];
1198 text_t start_results_from_old = args["r"];
1199
1200 // if the "ifl" argument is set to 1, we only want to get one document
1201 // this may be the first search result (from "I feel lucky") or maybe a
1202 // specified search result (from next/prev search result link)
1203 if (args["ifl"] == "1") {
1204 args["r"] = args["ifln"]; // the document number we want
1205 args["o"] = "1";
1206 }
1207
1208 bptr->set_filter_options (request, args);
1209 bptr->load_metadata_defaults (request.fields);
1210
1211 format_t *formatlistptr = new format_t();
1212 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1213
1214 // do the query
1215 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
1216 text_t formattedstring = "";
1217 get_formatted_query_string(formattedstring, segment, args, disp, logout);
1218
1219
1220 if (!formattedstring.empty()) { // do the query
1221 // note! formattedstring is in unicode! mg and mgpp must convert!
1222 set_queryfilter_options (request, formattedstring, args);
1223
1224 collectproto->filter (collection, request, response, err, logout);
1225
1226 if (err != noError) {
1227 outconvertclass text_t2ascii;
1228 logout << text_t2ascii
1229 << "queryaction::search_single_collections: call to QueryFilter failed "
1230 << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
1231
1232 }
1233
1234 // Perform the "I'm feeling lucky" trick if the "ifl" argument is set
1235 if (err == noError && !args["ifl"].empty()) {
1236 //Restore the "r" and "o" arg
1237 args["r"] = start_results_from_old;
1238 args["o"] = hits_per_page_old;
1239
1240 //Find whether DocumentSearchResultLinks is enabled
1241 bool show_links = false;
1242 text_tmap::const_iterator format_here = cinfo->format.begin();
1243 text_tmap::const_iterator format_end = cinfo->format.end();
1244
1245 while (format_here != format_end) {
1246 if (((*format_here).first == "DocumentSearchResultLinks") &&
1247 ((*format_here).second == "true")){
1248 show_links = true;
1249 break;
1250 }
1251 ++format_here;
1252 }
1253
1254 if (args["ifl"] == 1 || (args["ifl"] == 2 && response.numDocs == 1)) {
1255
1256 // The first search result is the one we want
1257 if (response.docInfo.begin() != response.docInfo.end()) {
1258
1259 ResultDocInfo_tarray::iterator section = response.docInfo.begin();
1260
1261 // We still need to set "srn" and "srp" values (next and prev search result numbers) if we are showing them
1262 int srn = 0;
1263 int srp = 0;
1264 if (show_links) {
1265 int ifln = args["ifln"].getint();
1266 srn = ifln + 1;
1267 if (srn > response.numDocs) {
1268 srn = 0;
1269 }
1270 srp = ifln - 1;
1271 if (srp < 0) {
1272 srp = 0;
1273 }
1274 }
1275
1276 textout << outconvert << disp
1277 << "Location: _gwcgi_?e=_compressedoptions_&a=d&c="
1278 << collection << "&cl=search&d=" << (*section).OID
1279 << "&srn=" << srn << "&srp=" << srp << "\n\n";
1280 textout << flush;
1281
1282 return true;
1283 }
1284 }
1285
1286 // There weren't enough (or any) matching documents
1287 // We'll just carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
1288 // done the right thing (because ifl was set), so we need to make sure the output is html
1289 textout << "Content-type: text/html\n\n";
1290
1291 }
1292
1293 if (err != noError) {
1294 disp.setmacro("resultline", "query", "_textnodocs_");
1295 if (err == syntaxError) {
1296 disp.setmacro ("freqmsg", "query", "_textinvalidquery_");
1297 } else {
1298 disp.setmacro ("freqmsg", "query", "");
1299 }
1300 } else {
1301
1302 define_query_macros (args, disp, response.numDocs, response.isApprox);
1303 define_single_query_macros(args, disp, response);
1304 // save the query if appropriate
1305 save_search_history(args, response.numDocs, response.isApprox);
1306 }
1307
1308 // If Lucene threw a TooManyClauses exception, tell the user about it
1309 if (args["ct"] == 2 && response.error_message == "TOO_MANY_CLAUSES") {
1310 disp.setmacro ("freqmsg", "query", "_textlucenetoomanyclauses_");
1311 }
1312 }
1313
1314 //Restore the "r" and "o" arg in case they have been changed and we still get here
1315 args["r"] = start_results_from_old;
1316 args["o"] = hits_per_page_old;
1317
1318 define_history_macros (disp, args, protos, logout);
1319
1320 textout << outconvert << disp << "_query:header_\n"
1321 << "_query:content_";
1322
1323 if (err == noError) {
1324 // output the results
1325 text_t numdocs_t = response.numDocs;
1326 args["nmd"] = numdocs_t;
1327 bool use_table = is_table_content (formatlistptr);
1328 bptr->output_section_group (response, args, collection, 0, formatlistptr,
1329 use_table, request.fields, request.getParents,
1330 collectproto, disp, outconvert, textout, logout);
1331 }
1332
1333 textout << outconvert << disp << "_query:footer_";
1334
1335 delete (formatlistptr);
1336
1337 return true;
1338}
1339
1340// does the formatting of the query string - either uses q for a text search
1341// or the form values for an form search
1342// also adds dates if appropriate in text search
1343void queryaction::get_formatted_query_string (text_t &formattedstring,
1344 bool segment,
1345 cgiargsclass &args,
1346 displayclass &disp,
1347 ostream &logout) {
1348 if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
1349 formattedstring = args["q"];
1350 // remove & | ! for simple search,do segmentation if necessary
1351 format_querystring (formattedstring, args.getintarg("b"), segment);
1352 if (args["ct"]!=0) { // mgpp and lucene - need to add in tag info if appropriate
1353 format_field_info(formattedstring, args["fqf"], args.getintarg("ct"),
1354 args.getintarg("t"), args.getintarg("b"));
1355 }
1356
1357 add_dates(formattedstring, args.getintarg("ds"), args.getintarg("de"),
1358 args.getintarg("dsbc"), args.getintarg("debc"),
1359 args.getintarg("ct"));
1360 args["q"] = formattedstring;
1361
1362 }
1363 else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
1364
1365 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
1366 formattedstring = args["q"];
1367 }
1368 else { // form search
1369 if (args["b"]=="0") { // regular form
1370 parse_reg_query_form(formattedstring, args, segment);
1371 }
1372 else { // advanced form
1373 parse_adv_query_form(formattedstring, args, segment);
1374 }
1375 args["q"] = formattedstring;
1376
1377 // reset the cgiargfqv macro - need to escape any quotes in it
1378 disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
1379
1380 // also reset the _cgiargq_ macro as it has changed now
1381 disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
1382
1383 // reset the compressed options to include the q arg
1384 text_t compressedoptions = recpt->get_compressed_arg(args, logout);
1385 if (!compressedoptions.empty()) {
1386 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1387 // need a decoded version of compressedoptions for use within forms
1388 // as browsers encode values from forms before sending to server
1389 // (e.g. %25 becomes %2525)
1390 decode_cgi_arg (compressedoptions);
1391 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
1392 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
1393 compressedoptions = to_uni(compressedoptions);
1394 }
1395
1396 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1397 }
1398 } // form search
1399 } // args["qt"]=1
1400 else {
1401 logout << "ERROR (queryaction::get_formatted_query_string): querytype not defined\n";
1402 }
1403}
1404
1405
1406// define_query_macros sets the macros that couldn't be set until the
1407// query had been done. Those macros are
1408// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
1409// _thisfirst_, and _thislast_ and _quotedquery_
1410// this has been simplified so it can be used with both search_single_coll
1411// and search_multiple_coll
1412void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
1413 int numdocs, isapprox isApprox)
1414{
1415 // The following 'if' statatment is placed here to be keep the semantics
1416 // the same as the version before basequeryaction was introduced
1417
1418 if (num_phrases > 0) isApprox = Exact;
1419
1420 basequeryaction::define_query_macros(args,disp,numdocs,isApprox);
1421
1422 if (args["ct"]==0) { // mg queries only, not mgpp
1423 // get the quoted bits of the query string and set _quotedquery_
1424 text_tarray phrases;
1425 get_phrases (args["q"], phrases);
1426 num_phrases = phrases.size();
1427 text_tarray::const_iterator phere = phrases.begin();
1428 text_tarray::const_iterator pend = phrases.end();
1429 bool first = true;
1430 text_t quotedquery;
1431 while (phere != pend) {
1432 if (!first)
1433 if ((phere +1) == pend) quotedquery += " and ";
1434 else quotedquery += ", ";
1435
1436 quotedquery += "\"" + *phere + "\"";
1437 first = false;
1438 ++phere;
1439 }
1440 if (args.getintarg("s") && !quotedquery.empty()) quotedquery += "_textstemon_";
1441 disp.setmacro ("quotedquery", "query", quotedquery);
1442 }
1443
1444}
1445
1446// should this change for cross coll search??
1447bool queryaction::save_search_history (cgiargsclass &args, int numdocs,
1448 isapprox isApprox) {
1449 if (args["q"]=="") return true; // null query, dont save
1450 if (args["hs"]=="0") return true; // only save when submit query pressed
1451
1452 // get userid
1453 text_t userid = args["z"];
1454
1455 // the number of docs goes on the front of the query string
1456 text_t query = text_t(numdocs);
1457 if (isApprox==MoreThan) { // there were more docs found
1458 query.push_back('+');
1459 }
1460 query += "c="+args["c"];
1461 query += ";h="+args["h"];
1462 query += ";t="+args["t"];
1463 query += ";b="+args["b"];
1464 query += ";j="+args["j"];
1465 query += ";n="+args["n"];
1466 query += ";s="+args["s"];
1467 query += ";k="+args["k"];
1468 query += ";g="+args["g"];
1469
1470 text_t qstring = args["q"];
1471 //text_t formattedquery =cgi_safe(qstring);
1472 //query += "&amp;q="+formattedquery;
1473 query += ";q="+qstring;
1474 bool display=false;
1475 int hd = args.getintarg("hd");
1476 if (hd > 0) display=true;
1477 if (set_history_info(userid, query, dbhome, display)) return true;
1478 else return false;
1479}
1480
Note: See TracBrowser for help on using the repository browser.