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

Last change on this file since 13526 was 13526, checked in by mdewsnip, 17 years ago

The "srn" and "srp" CGI arguments must be multicharacter otherwise they stop working after search result 9!

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 61.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 "infodbclass.h"
32#include "fileutil.h"
33#include "text_t.h"
34#include "historydb.h"
35#include "htmlutils.h" // for html_safe in do_action
36#include "gsdltools.h"
37#include "phrases.h" // for get_phrases
38#include <stdlib.h> // for strtol
39#include <assert.h>
40
41void colinfo_t::clear () {
42 formatlistptr = NULL;
43 browserptr = NULL;
44}
45
46void QueryResult_t::clear() {
47 doc.clear();
48 collection.clear();
49}
50
51queryaction::queryaction () {
52
53 recpt = NULL;
54 num_phrases = 0;
55
56 // this action uses cgi variable "a"
57 cgiarginfo arg_ainfo;
58 arg_ainfo.shortname = "a";
59 arg_ainfo.longname = "action";
60 arg_ainfo.multiplechar = true;
61 arg_ainfo.defaultstatus = cgiarginfo::weak;
62 arg_ainfo.argdefault = "q";
63 arg_ainfo.savedarginfo = cgiarginfo::must;
64 argsinfo.addarginfo (NULL, arg_ainfo);
65
66 // "ct" - 0 = mg, 1 = mgpp, 2=lucene
67 arg_ainfo.shortname = "ct";
68 arg_ainfo.longname = "collection type";
69 arg_ainfo.multiplechar = true; // can be empty or single char
70 arg_ainfo.defaultstatus = cgiarginfo::weak;
71 arg_ainfo.argdefault = g_EmptyText;
72 arg_ainfo.savedarginfo = cgiarginfo::must;
73 argsinfo.addarginfo (NULL, arg_ainfo);
74
75 // "b" - 0 = simple, 1 = advanced
76 arg_ainfo.shortname = "b";
77 arg_ainfo.longname = "query mode";
78 arg_ainfo.multiplechar = false;
79 arg_ainfo.defaultstatus = cgiarginfo::weak;
80 arg_ainfo.argdefault = "0";
81 arg_ainfo.savedarginfo = cgiarginfo::must;
82 argsinfo.addarginfo (NULL, arg_ainfo);
83
84 // "h"
85 arg_ainfo.shortname = "h";
86 arg_ainfo.longname = "main index";
87 arg_ainfo.multiplechar = true;
88 arg_ainfo.defaultstatus = cgiarginfo::weak;
89 arg_ainfo.argdefault = g_EmptyText;
90 arg_ainfo.savedarginfo = cgiarginfo::must;
91 argsinfo.addarginfo (NULL, arg_ainfo);
92
93 // "h2"
94 arg_ainfo.shortname = "h2";
95 arg_ainfo.longname = "main index for second query";
96 arg_ainfo.multiplechar = true;
97 arg_ainfo.defaultstatus = cgiarginfo::weak;
98 arg_ainfo.argdefault = g_EmptyText;
99 arg_ainfo.savedarginfo = cgiarginfo::must;
100 argsinfo.addarginfo (NULL, arg_ainfo);
101
102 // "j"
103 arg_ainfo.shortname = "j";
104 arg_ainfo.longname = "sub collection index";
105 arg_ainfo.multiplechar = true;
106 arg_ainfo.defaultstatus = cgiarginfo::weak;
107 arg_ainfo.argdefault = g_EmptyText;
108 arg_ainfo.savedarginfo = cgiarginfo::must;
109 argsinfo.addarginfo (NULL, arg_ainfo);
110
111 // "j2"
112 arg_ainfo.shortname = "j2";
113 arg_ainfo.longname = "sub collection index for second query";
114 arg_ainfo.multiplechar = true;
115 arg_ainfo.defaultstatus = cgiarginfo::weak;
116 arg_ainfo.argdefault = g_EmptyText;
117 arg_ainfo.savedarginfo = cgiarginfo::must;
118 argsinfo.addarginfo (NULL, arg_ainfo);
119
120 // "n"
121 arg_ainfo.shortname = "n";
122 arg_ainfo.longname = "language index";
123 arg_ainfo.multiplechar = true;
124 arg_ainfo.defaultstatus = cgiarginfo::weak;
125 arg_ainfo.argdefault = g_EmptyText;
126 arg_ainfo.savedarginfo = cgiarginfo::must;
127 argsinfo.addarginfo (NULL, arg_ainfo);
128
129 // "n2"
130 arg_ainfo.shortname = "n2";
131 arg_ainfo.longname = "language index for second query";
132 arg_ainfo.multiplechar = true;
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 // "q"
140 arg_ainfo.shortname = "q";
141 arg_ainfo.longname = "query string";
142 arg_ainfo.multiplechar = true;
143 arg_ainfo.defaultstatus = cgiarginfo::weak;
144 arg_ainfo.argdefault = g_EmptyText;
145 arg_ainfo.savedarginfo = cgiarginfo::must;
146 argsinfo.addarginfo (NULL, arg_ainfo);
147
148 // "q2"
149 arg_ainfo.shortname = "q2";
150 arg_ainfo.longname = "query string for second query";
151 arg_ainfo.multiplechar = true;
152 arg_ainfo.defaultstatus = cgiarginfo::weak;
153 arg_ainfo.argdefault = g_EmptyText;
154 arg_ainfo.savedarginfo = cgiarginfo::must;
155 argsinfo.addarginfo (NULL, arg_ainfo);
156
157 // "cq2" ""=don't combine, "and", "or", "not"
158 arg_ainfo.shortname = "cq2";
159 arg_ainfo.longname = "combine queries";
160 arg_ainfo.multiplechar = true;
161 arg_ainfo.defaultstatus = cgiarginfo::weak;
162 arg_ainfo.argdefault = g_EmptyText;
163 arg_ainfo.savedarginfo = cgiarginfo::must;
164 argsinfo.addarginfo (NULL, arg_ainfo);
165
166 // "t" - 1 = ranked 0 = boolean
167 arg_ainfo.shortname = "t";
168 arg_ainfo.longname = "search type";
169 arg_ainfo.multiplechar = false;
170 arg_ainfo.defaultstatus = cgiarginfo::weak;
171 arg_ainfo.argdefault = "1";
172 arg_ainfo.savedarginfo = cgiarginfo::must;
173 argsinfo.addarginfo (NULL, arg_ainfo);
174
175 // "k"
176 arg_ainfo.shortname = "k";
177 arg_ainfo.longname = "casefolding";
178 arg_ainfo.multiplechar = false;
179 arg_ainfo.defaultstatus = cgiarginfo::weak;
180 arg_ainfo.argdefault = "1";
181 arg_ainfo.savedarginfo = cgiarginfo::must;
182 argsinfo.addarginfo (NULL, arg_ainfo);
183
184 // "ks"
185 arg_ainfo.shortname = "ks";
186 arg_ainfo.longname = "casefolding support";
187 arg_ainfo.multiplechar = false;
188 arg_ainfo.defaultstatus = cgiarginfo::weak;
189 arg_ainfo.argdefault = "0";
190 arg_ainfo.savedarginfo = cgiarginfo::must;
191 argsinfo.addarginfo (NULL, arg_ainfo);
192
193 // "s"
194 arg_ainfo.shortname = "s";
195 arg_ainfo.longname = "stemming";
196 arg_ainfo.multiplechar = false;
197 arg_ainfo.defaultstatus = cgiarginfo::weak;
198 arg_ainfo.argdefault = "0";
199 arg_ainfo.savedarginfo = cgiarginfo::must;
200 argsinfo.addarginfo (NULL, arg_ainfo);
201
202 // "ss"
203 arg_ainfo.shortname = "ss";
204 arg_ainfo.longname = "stemming support";
205 arg_ainfo.multiplechar = false;
206 arg_ainfo.defaultstatus = cgiarginfo::weak;
207 arg_ainfo.argdefault = "0";
208 arg_ainfo.savedarginfo = cgiarginfo::must;
209 argsinfo.addarginfo (NULL, arg_ainfo);
210
211 // "af"
212 arg_ainfo.shortname = "af";
213 arg_ainfo.longname = "accentfolding";
214 arg_ainfo.multiplechar = 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 // "afs"
221 arg_ainfo.shortname = "afs";
222 arg_ainfo.longname = "accentfolding support";
223 arg_ainfo.multiplechar = false;
224 arg_ainfo.defaultstatus = cgiarginfo::weak;
225 arg_ainfo.argdefault = "0";
226 arg_ainfo.savedarginfo = cgiarginfo::must;
227 argsinfo.addarginfo (NULL, arg_ainfo);
228
229 // "m"
230 arg_ainfo.shortname = "m";
231 arg_ainfo.longname = "maximum number of documents";
232 arg_ainfo.multiplechar = true;
233 arg_ainfo.defaultstatus = cgiarginfo::weak;
234 arg_ainfo.argdefault = "50";
235 arg_ainfo.savedarginfo = cgiarginfo::must;
236 argsinfo.addarginfo (NULL, arg_ainfo);
237
238 // "o"
239 arg_ainfo.shortname = "o";
240 arg_ainfo.longname = "hits per page";
241 arg_ainfo.multiplechar = true;
242 arg_ainfo.defaultstatus = cgiarginfo::weak;
243 arg_ainfo.argdefault = "20";
244 arg_ainfo.savedarginfo = cgiarginfo::must;
245 argsinfo.addarginfo (NULL, arg_ainfo);
246
247 // "r"
248 arg_ainfo.shortname = "r";
249 arg_ainfo.longname = "start results from";
250 arg_ainfo.multiplechar = true;
251 arg_ainfo.defaultstatus = cgiarginfo::weak;
252 arg_ainfo.argdefault = "1";
253 arg_ainfo.savedarginfo = cgiarginfo::must;
254 argsinfo.addarginfo (NULL, arg_ainfo);
255
256 // "ccs"
257 arg_ainfo.shortname = "ccs";
258 arg_ainfo.longname = "cross collection searching";
259 arg_ainfo.multiplechar = false;
260 arg_ainfo.defaultstatus = cgiarginfo::weak;
261 arg_ainfo.argdefault = "0";
262 arg_ainfo.savedarginfo = cgiarginfo::must;
263 argsinfo.addarginfo (NULL, arg_ainfo);
264
265 // "ccp"
266 arg_ainfo.shortname = "ccp";
267 arg_ainfo.longname = "cross collection page";
268 arg_ainfo.multiplechar = false;
269 arg_ainfo.defaultstatus = cgiarginfo::weak;
270 arg_ainfo.argdefault = "0";
271 arg_ainfo.savedarginfo = cgiarginfo::must;
272 argsinfo.addarginfo (NULL, arg_ainfo);
273
274 // "cc"
275 arg_ainfo.shortname = "cc";
276 arg_ainfo.longname = "collections to search";
277 arg_ainfo.multiplechar = true;
278 arg_ainfo.multiplevalue = true;
279 arg_ainfo.defaultstatus = cgiarginfo::weak;
280 arg_ainfo.argdefault = g_EmptyText;
281 arg_ainfo.savedarginfo = cgiarginfo::must;
282 argsinfo.addarginfo (NULL, arg_ainfo);
283
284 // "hd" history display - search history only displayed when
285 // this var set to something other than 0
286 // this number of records is displayed
287 arg_ainfo.shortname = "hd";
288 arg_ainfo.longname = "history display";
289 arg_ainfo.multiplechar = true;
290 arg_ainfo.multiplevalue = false;
291 arg_ainfo.defaultstatus = cgiarginfo::weak;
292 arg_ainfo.argdefault = "0";
293 arg_ainfo.savedarginfo = cgiarginfo::must;
294 argsinfo.addarginfo (NULL, arg_ainfo);
295
296 // "hs" save - set to 1 in query form, so only save when submit
297 // query
298 // 0 = no save 1 = save
299 arg_ainfo.shortname = "hs";
300 arg_ainfo.longname = "history save";
301 arg_ainfo.multiplechar = false;
302 arg_ainfo.defaultstatus = cgiarginfo::weak;
303 arg_ainfo.argdefault = "0";
304 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
305 argsinfo.addarginfo (NULL, arg_ainfo);
306
307 // "g" - new arg for granularity, for mgpp collections
308 arg_ainfo.shortname = "g";
309 arg_ainfo.longname = "granularity";
310 arg_ainfo.multiplechar = true;
311 arg_ainfo.defaultstatus = cgiarginfo::weak;
312 arg_ainfo.argdefault = g_EmptyText;
313 arg_ainfo.savedarginfo = cgiarginfo::must;
314 argsinfo.addarginfo (NULL, arg_ainfo);
315
316 // "ds" - start date
317 arg_ainfo.shortname = "ds";
318 arg_ainfo.longname = "start date";
319 arg_ainfo.multiplechar = true;
320 arg_ainfo.defaultstatus = cgiarginfo::weak;
321 arg_ainfo.argdefault = g_EmptyText;
322 arg_ainfo.savedarginfo = cgiarginfo::must;
323 argsinfo.addarginfo (NULL, arg_ainfo);
324
325 // "de" - end date
326 arg_ainfo.shortname = "de";
327 arg_ainfo.longname = "end date";
328 arg_ainfo.multiplechar = true;
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 // "dsbc" - whether or not start date is prechristian
335 arg_ainfo.shortname = "dsbc";
336 arg_ainfo.longname = "start date bc";
337 arg_ainfo.multiplechar = false;
338 arg_ainfo.defaultstatus = cgiarginfo::weak;
339 arg_ainfo.argdefault = "0";
340 arg_ainfo.savedarginfo = cgiarginfo::must;
341 argsinfo.addarginfo (NULL, arg_ainfo);
342
343 // "debc" - whether or not end date is prechristian
344 arg_ainfo.shortname = "debc";
345 arg_ainfo.longname = "end date bc";
346 arg_ainfo.multiplechar = false;
347 arg_ainfo.defaultstatus = cgiarginfo::weak;
348 arg_ainfo.argdefault = "0";
349 arg_ainfo.savedarginfo = cgiarginfo::must;
350 argsinfo.addarginfo (NULL, arg_ainfo);
351
352 // "qt" - 0 = text, 1 = form
353 arg_ainfo.shortname = "qt";
354 arg_ainfo.longname = "query type";
355 arg_ainfo.multiplechar = true; // can be empty or single char
356 arg_ainfo.defaultstatus = cgiarginfo::weak;
357 arg_ainfo.argdefault = g_EmptyText;
358 arg_ainfo.savedarginfo = cgiarginfo::must;
359 argsinfo.addarginfo (NULL, arg_ainfo);
360
361 // "qto" - 1 = text only, 2 = form only, 3 = text and form
362 arg_ainfo.shortname = "qto";
363 arg_ainfo.longname = "query type options";
364 arg_ainfo.multiplechar = true; // can be empty or single char
365 arg_ainfo.defaultstatus = cgiarginfo::weak;
366 arg_ainfo.argdefault = g_EmptyText;
367 arg_ainfo.savedarginfo = cgiarginfo::must;
368 argsinfo.addarginfo (NULL, arg_ainfo);
369
370 // "qb" - 0 = regular, 1 = large
371 arg_ainfo.shortname = "qb";
372 arg_ainfo.longname = "query box type";
373 arg_ainfo.multiplechar = false;
374 arg_ainfo.defaultstatus = cgiarginfo::weak;
375 arg_ainfo.argdefault = "0";
376 arg_ainfo.savedarginfo = cgiarginfo::must;
377 argsinfo.addarginfo (NULL, arg_ainfo);
378
379 // "fqn" - number of fields in the query form
380 arg_ainfo.shortname = "fqn";
381 arg_ainfo.longname = "form query num fields";
382 arg_ainfo.multiplechar = true;
383 arg_ainfo.defaultstatus = cgiarginfo::weak;
384 arg_ainfo.argdefault = "4";
385 arg_ainfo.savedarginfo = cgiarginfo::must;
386 argsinfo.addarginfo (NULL, arg_ainfo);
387
388 // "fqf" - the list of field names in the form query
389 // - a comma separated list
390 arg_ainfo.shortname = "fqf";
391 arg_ainfo.longname = "form query fields";
392 arg_ainfo.multiplechar = true;
393 arg_ainfo.defaultstatus = cgiarginfo::weak;
394 arg_ainfo.argdefault = g_EmptyText;
395 arg_ainfo.savedarginfo = cgiarginfo::must;
396 argsinfo.addarginfo (NULL, arg_ainfo);
397
398 // "fqv" - the list of values in the form query
399 // - a comma separated list
400 arg_ainfo.shortname = "fqv";
401 arg_ainfo.longname = "form query values";
402 arg_ainfo.multiplechar = true;
403 arg_ainfo.defaultstatus = cgiarginfo::weak;
404 arg_ainfo.argdefault = g_EmptyText;
405 arg_ainfo.savedarginfo = cgiarginfo::must;
406 argsinfo.addarginfo (NULL, arg_ainfo);
407
408
409 // "fqs" - the list of stemming options in the form query
410 // - a comma separated list
411 arg_ainfo.shortname = "fqs";
412 arg_ainfo.longname = "form query stems";
413 arg_ainfo.multiplechar = true;
414 arg_ainfo.defaultstatus = cgiarginfo::weak;
415 arg_ainfo.argdefault = g_EmptyText;
416 arg_ainfo.savedarginfo = cgiarginfo::must;
417 argsinfo.addarginfo (NULL, arg_ainfo);
418
419
420 // "fqk" - the list of casefolding options in the form query
421 // - a comma separated list
422 arg_ainfo.shortname = "fqk";
423 arg_ainfo.longname = "form query casefolds";
424 arg_ainfo.multiplechar = true;
425 arg_ainfo.defaultstatus = cgiarginfo::weak;
426 arg_ainfo.argdefault = g_EmptyText;
427 arg_ainfo.savedarginfo = cgiarginfo::must;
428 argsinfo.addarginfo (NULL, arg_ainfo);
429
430 // "fqc" - the list of boolean operators in the form query
431 // - a comma separated list
432 arg_ainfo.shortname = "fqc";
433 arg_ainfo.longname = "form query combines";
434 arg_ainfo.multiplechar = true;
435 arg_ainfo.defaultstatus = cgiarginfo::weak;
436 arg_ainfo.argdefault = g_EmptyText;
437 arg_ainfo.savedarginfo = cgiarginfo::must;
438 argsinfo.addarginfo (NULL, arg_ainfo);
439
440 // "fqa" - form query advanced - for "run query"
441 arg_ainfo.shortname = "fqa";
442 arg_ainfo.longname = "form query advanced query";
443 arg_ainfo.multiplechar = false;
444 arg_ainfo.defaultstatus = cgiarginfo::weak;
445 arg_ainfo.argdefault = "0";
446 arg_ainfo.savedarginfo = cgiarginfo::must;
447 argsinfo.addarginfo (NULL, arg_ainfo);
448
449 // "ifl" - I'm feeling lucky! (Go directly to a matching document)
450 arg_ainfo.shortname = "ifl";
451 arg_ainfo.longname = "i'm feeling lucky";
452 arg_ainfo.multiplechar = false;
453 arg_ainfo.defaultstatus = cgiarginfo::weak;
454 arg_ainfo.argdefault = g_EmptyText;
455 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
456 argsinfo.addarginfo (NULL, arg_ainfo);
457
458 // "ifln" - I'm feeling lucky number (Go directly to the nth matching document)
459 arg_ainfo.shortname = "ifln";
460 arg_ainfo.longname = "i'm feeling lucky number";
461 arg_ainfo.multiplechar = true;
462 arg_ainfo.defaultstatus = cgiarginfo::weak;
463 arg_ainfo.argdefault = "1";
464 arg_ainfo.savedarginfo = cgiarginfo::mustnot;
465 argsinfo.addarginfo (NULL, arg_ainfo);
466
467 // "srn" - the next search result
468 arg_ainfo.shortname = "srn";
469 arg_ainfo.longname = "the next search result number";
470 arg_ainfo.multiplechar = true;
471 arg_ainfo.defaultstatus = cgiarginfo::weak;
472 arg_ainfo.argdefault = "0";
473 arg_ainfo.savedarginfo = cgiarginfo::must;
474 argsinfo.addarginfo (NULL, arg_ainfo);
475
476 // "srp" - the previous search result
477 arg_ainfo.shortname = "srp";
478 arg_ainfo.longname = "the previous search result number";
479 arg_ainfo.multiplechar = false;
480 arg_ainfo.defaultstatus = cgiarginfo::weak;
481 arg_ainfo.argdefault = "0";
482 arg_ainfo.savedarginfo = cgiarginfo::must;
483 argsinfo.addarginfo (NULL, arg_ainfo);
484
485 // "sf" - Sort field. Set to field to be used for sorting search reult
486 // set (only implemented for lucene collections at present).
487 arg_ainfo.shortname = "sf";
488 arg_ainfo.longname = "sort field";
489 arg_ainfo.multiplechar = true;
490 arg_ainfo.defaultstatus = cgiarginfo::weak;
491 arg_ainfo.argdefault = g_EmptyText;
492 arg_ainfo.savedarginfo = cgiarginfo::must;
493 argsinfo.addarginfo (NULL, arg_ainfo);
494
495 // "fuzziness" controls how closely the search terms must match
496 // 100 = exact match, 0 = very inexact match (only implemented for Lucene)
497 arg_ainfo.shortname = "fuzziness";
498 arg_ainfo.longname = "Lucene fuzziness value";
499 arg_ainfo.multiplechar = true;
500 arg_ainfo.defaultstatus = cgiarginfo::weak;
501 arg_ainfo.argdefault = g_EmptyText;
502 arg_ainfo.savedarginfo = cgiarginfo::must;
503 argsinfo.addarginfo (NULL, arg_ainfo);
504}
505
506void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
507 action::configure (key, cfgline);
508}
509
510bool queryaction::init (ostream &logout) {
511 return action::init (logout);
512}
513
514bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
515 recptprotolistclass * /*protos*/, ostream &logout) {
516
517 // check t argument
518 int arg_t = args.getintarg("t");
519 if (arg_t != 0 && arg_t != 1) {
520 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
521 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
522 if (tinfo != NULL) args["t"] = tinfo->argdefault;
523 }
524
525 // check k argument
526 int arg_k = args.getintarg("k");
527 if (arg_k != 0 && arg_k != 1) {
528 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
529 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
530 if (kinfo != NULL) args["k"] = kinfo->argdefault;
531 }
532
533 // check s argument
534 int arg_s = args.getintarg("s");
535 if (arg_s != 0 && arg_s != 1) {
536 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
537 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
538 if (sinfo != NULL) args["s"] = sinfo->argdefault;
539 }
540
541 // check m argument
542 int arg_m = args.getintarg("m");
543 if (arg_m < -1) {
544 logout << "Warning: \"m\" argument less than -1 (" << arg_m << ")\n";
545 cgiarginfo *minfo = argsinfo.getarginfo ("m");
546 if (minfo != NULL) args["m"] = minfo->argdefault;
547 }
548
549 // check o argument
550 int arg_o = args.getintarg("o");
551 if (arg_o < -1) {
552 logout << "Warning: \"o\" argument less than -1 (" << arg_o << ")\n";
553 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
554 if (oinfo != NULL) args["o"] = oinfo->argdefault;
555 }
556
557 // check r argument
558 int arg_r = args.getintarg("r");
559 if (arg_r < 1) {
560 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
561 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
562 if (rinfo != NULL) args["r"] = rinfo->argdefault;
563 }
564 //check hd argument
565 int arg_hd = args.getintarg("hd");
566 if (arg_hd <0 ) {
567 logout << "Warning: \"hd\" argument less than 0 (" << arg_hd << ")\n";
568 cgiarginfo *hdinfo = argsinfo.getarginfo ("hd");
569 if (hdinfo != NULL) args["hd"] = hdinfo->argdefault;
570 }
571
572 //check hs argument
573 int arg_hs = args.getintarg("hs");
574 if (arg_hs !=0 && arg_hs !=1) {
575 logout << "Warning: \"hs\" argument out of range (" << arg_hs << ")\n";
576 cgiarginfo *hsinfo = argsinfo.getarginfo ("hs");
577 if (hsinfo != NULL) args["hs"] = hsinfo->argdefault;
578 }
579
580 // check ct argument
581 int arg_ct = args.getintarg("ct");
582 if (arg_ct < 0 || arg_ct > 2) {
583 logout << "Warning: \"ct\" argument out of range (" << arg_ct << ")\n";
584 cgiarginfo *ctinfo = argsinfo.getarginfo ("ct");
585 if (ctinfo != NULL) args["ct"] = ctinfo->argdefault;
586 }
587
588 // check qt argument
589 int arg_qt = args.getintarg("qt");
590 if (arg_qt !=0 && arg_qt !=1) {
591 logout << "Warning: \"qt\" argument out of range (" << arg_qt << ")\n";
592 cgiarginfo *qtinfo = argsinfo.getarginfo ("qt");
593 if (qtinfo != NULL) args["qt"] = qtinfo->argdefault;
594 }
595
596 // check qb argument
597 int arg_qb = args.getintarg("qb");
598 if (arg_qb !=0 && arg_qb !=1) {
599 logout << "Warning: \"qb\" argument out of range (" << arg_qb << ")\n";
600 cgiarginfo *qbinfo = argsinfo.getarginfo ("qb");
601 if (qbinfo != NULL) args["qb"] = qbinfo->argdefault;
602 }
603
604 // check fqa argument
605 int arg_fqa = args.getintarg("fqa");
606 if (arg_fqa !=0 && arg_fqa !=1) {
607 logout << "Warning: \"fqa\" argument out of range (" << arg_fqa << ")\n";
608 cgiarginfo *fqainfo = argsinfo.getarginfo ("fqa");
609 if (fqainfo != NULL) args["fqa"] = fqainfo->argdefault;
610 }
611
612 // check fqn argument
613 int arg_fqn = args.getintarg("fqn");
614 if (arg_fqn < -1) {
615 logout << "Warning: \"fqn\" argument less than -1 (" << arg_fqn << ")\n";
616 cgiarginfo *fqninfo = argsinfo.getarginfo ("fqn");
617 if (fqninfo != NULL) args["fqn"] = fqninfo->argdefault;
618 }
619
620 return true;
621}
622
623void queryaction::get_cgihead_info (cgiargsclass &args, recptprotolistclass * /*protos*/,
624 response_t &response, text_t &response_data,
625 ostream &/*logout*/) {
626 // If this is an "I'm feeling lucky" request, we don't know the target location until later
627 if (!args["ifl"].empty()) {
628 response = undecided_location;
629 return;
630 }
631
632 response = content;
633 response_data = "text/html";
634}
635
636void queryaction::define_internal_macros (displayclass &disp, cgiargsclass &args,
637 recptprotolistclass * protos,
638 ostream &logout) {
639
640 // define_internal_macros sets the following macros:
641
642 // The following macros are set later (in define_query_macros) as they can't be set until
643 // the query has been done.
644 // _quotedquery_ the part of the query string that was quoted for post-processing
645 // _freqmsg_ the term frequency string
646
647 // _resultline_ the "x documents matched the query" string
648
649 // _prevfirst_ these are used when setting up the links to previous/next
650 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
651 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
652 // _nextlast_
653 // _thisfirst_
654 // _thislast_
655
656
657 define_form_macros(disp, args, protos, logout);
658
659 define_query_interface(disp, args, protos, logout);
660
661
662}
663
664void queryaction::define_query_interface(displayclass &disp,
665 cgiargsclass &args,
666 recptprotolistclass * protos,
667 ostream &logout){
668 text_t collection = args["c"];
669
670 //check that the protocol is alive
671 recptproto* colproto = protos->getrecptproto (collection, logout);
672 if(colproto == NULL) {
673 logout << "ERROR: Null collection protocol trying to query"
674 << collection.getcstr() << "\n";
675 return;
676 }
677
678 //check the collection is responding/in place
679 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(colproto, collection,
680 logout);
681 if(colinfo == NULL){
682 logout << "ERROR: Null returned for get_collectinfo_ptr on "
683 << collection.getcstr() << "in queryaction::define_query_interface\n";
684 return;
685 }
686
687 text_tmap::iterator check = colinfo->format.find("QueryInterface");
688 if(check != colinfo->format.end()){
689 if((*check).second=="DateSearch"){
690 text_t current = "_datesearch_";
691 disp.setmacro("optdatesearch","query",current);
692 }
693 }
694}
695
696
697// sets the selection box macros _hselection_, _jselection_, _nselection_ _gselection_, fqfselection_
698void queryaction::set_option_macro (const text_t &macroname,
699 text_t current_value,
700 bool display_single,
701 bool add_js_update,
702 const FilterOption_t &option,
703 displayclass &disp) {
704
705 if (option.validValues.empty()) return;
706 if (option.validValues.size() == 1) {
707 if (display_single) {
708 disp.setmacro (macroname + "selection", displayclass::defaultpackage, "_" + option.defaultValue + "_");
709 }
710 return;
711 }
712 if (option.validValues.size() < 2) return;
713
714 text_t macrovalue = "<select name=\"" + macroname + "\"";
715 if (add_js_update) {
716 macrovalue += " onChange=\"update"+macroname+"();\"";
717 }
718 macrovalue += ">\n";
719
720 if (current_value.empty()) current_value = option.defaultValue;
721
722 text_tarray::const_iterator thisvalue = option.validValues.begin();
723 text_tarray::const_iterator endvalue = option.validValues.end();
724
725 while (thisvalue != endvalue) {
726 macrovalue += "<option value=\"" + *thisvalue + "\"";
727 if (*thisvalue == current_value)
728 macrovalue += " selected";
729 macrovalue += ">_" + *thisvalue + "_\n";
730 ++thisvalue;
731 }
732 macrovalue += "</select>\n";
733 disp.setmacro (macroname + "selection", displayclass::defaultpackage, macrovalue);
734}
735
736
737void queryaction::define_external_macros (displayclass &disp, cgiargsclass &args,
738 recptprotolistclass *protos, ostream &logout) {
739
740 // define_external_macros sets the following macros:
741
742 // some or all of these may not be required to be set
743 // _hselection_, _h2selection_ the selection box for the main part of the index
744 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
745 // _nselection_, _n2selection_ the selection box for the language part of the index
746 // _cq2selection the selection box for combining two queries
747
748 // _gselection_, the selection box forlevels (mgpp)
749 // _fqfselection_, the selection box for index/fields (mgpp)
750 // can't do anything if collectproto is null (i.e. no collection was specified)
751 recptproto *collectproto = protos->getrecptproto (args["c"], logout);
752 if (collectproto == NULL) return;
753
754 ColInfoResponse_t *colinfo = recpt->get_collectinfo_ptr(collectproto,
755 args["c"],
756 logout);
757 set_query_type_args(colinfo, args);
758 set_stem_index_args(colinfo, args);
759
760 comerror_t err;
761 InfoFilterOptionsResponse_t response;
762 InfoFilterOptionsRequest_t request;
763 request.filterName = "QueryFilter";
764
765 collectproto->get_filteroptions (args["c"], request, response, err, logout);
766 if (err == noError) {
767
768 FilterOption_tmap::const_iterator it;
769 FilterOption_tmap::const_iterator end = response.filterOptions.end();
770
771 // _hselection_ and _h2selection_ (Index)
772 it = response.filterOptions.find ("Index");
773 if (it != end) set_option_macro ("h", args["h"], true, false, (*it).second, disp);
774 if (it != end) set_option_macro ("h2", args["h2"], true,false, (*it).second, disp);
775
776 // _jselection_ and _j2selection_ (Subcollection)
777 it = response.filterOptions.find ("Subcollection");
778 if (it != end) set_option_macro ("j", args["j"], true,false, (*it).second, disp);
779 if (it != end) set_option_macro ("j2", args["j2"], true,false, (*it).second, disp);
780
781 // _nselection_ and _n2selection_ (Language)
782 it = response.filterOptions.find ("Language");
783 if (it != end) set_option_macro ("n", args["n"], true,false, (*it).second, disp);
784 if (it != end) set_option_macro ("n2", args["n2"], true,false, (*it).second, disp);
785
786 // _cq2selection_ (CombineQuery)
787 it = response.filterOptions.find ("CombineQuery");
788 if (it != end) set_option_macro ("cq2", args["cq2"], true,false, (*it).second, disp);
789
790 if ((args["ct"] == "1") || (args["ct"] == "2")) { // mgpp/lucene collections
791 // _gselection_ (Level)
792 it = response.filterOptions.find("Level");
793 if (it!=end) {
794 set_option_macro("g", args["g"], false, false, (*it).second, disp);
795 if (args["qt"]=="1") { // form search
796 set_gformselection_macro(args["g"], (*it).second, disp);
797 }
798 }
799 // _fqfselection_ field list
800 it = response.filterOptions.find("IndexField");
801 if (it!=end) {
802 bool form_search = false;
803 if (args["qto"]=="2" || args["qt"]=="1") {
804 form_search = true;
805 }
806 set_option_macro ("fqf", args["fqf"], true, form_search, (*it).second, disp);
807 if (args["ct"] == "2") {// lucene
808 // set the sort field macro
809 set_sfselection_macro(args["sf"], (*it).second, disp);
810 }
811 }
812 }
813 }
814} // define external macros
815
816void queryaction::set_sfselection_macro(text_t current_value,
817 const FilterOption_t &option,
818 displayclass &disp) {
819
820 // we need two or more options to continue
821 if (option.validValues.size() <= 2) {
822 return;
823 }
824
825 text_t macrovalue = "<select name=\"sf\">\n";
826
827 if (current_value.empty()) current_value = "";
828
829 // we give a rank option first
830 macrovalue += "<option value=\"\"";
831 if (current_value == "") {
832 macrovalue += " selected";
833 }
834 macrovalue += ">_query:textsortbyrank_\n";
835
836 text_tarray::const_iterator thisvalue = option.validValues.begin();
837 text_tarray::const_iterator endvalue = option.validValues.end();
838 int valid_count = 0;
839 while (thisvalue != endvalue) {
840 if (*thisvalue != "ZZ" && *thisvalue != "TX") {
841 ++valid_count;
842 macrovalue += "<option value=\"by" + *thisvalue + "\"";
843 if (current_value == "by"+*thisvalue)
844 macrovalue += " selected";
845 macrovalue += ">_" + *thisvalue + "_\n";
846 }
847 ++thisvalue;
848 }
849 macrovalue += "</select>";
850 if (valid_count > 0) {
851 disp.setmacro ("sfselection", displayclass::defaultpackage, macrovalue);
852 }
853
854}
855
856// sets the selection box macro _gformselection_.
857// the default for _gformselection_ is _gselection_
858void queryaction::set_gformselection_macro (text_t current_value,
859 const FilterOption_t &option,
860 displayclass &disp) {
861
862 if (option.validValues.size() <= 1) {
863 return;
864 }
865 // we need to check to see if there is paragraph present
866 text_tarray::const_iterator thisvalue = option.validValues.begin();
867 text_tarray::const_iterator endvalue = option.validValues.end();
868
869 bool has_paras = false;
870 while (thisvalue != endvalue) {
871 if (*thisvalue == "Para") {
872 has_paras = true;
873 break;
874 }
875 ++thisvalue;
876 }
877 if (!has_paras) return; // there is no difference between the form selection and the normal one
878
879 if (option.validValues.size() == 2) {
880 // we will only have one value, but we will still put it in as a text string
881 int opt = 0;
882 if (option.validValues[0] == "Para") {
883 opt = 1;
884 }
885 disp.setmacro ("gformselection", displayclass::defaultpackage, "_"+option.validValues[opt]+"_");
886 return;
887 }
888
889 // there will be a select box
890 text_t macrovalue = "<select name=\"g\">\n";
891
892 if (current_value.empty()) current_value = option.defaultValue;
893
894 thisvalue = option.validValues.begin();
895
896 while (thisvalue != endvalue) {
897 if (*thisvalue != "Para") {
898 macrovalue += "<option value=\"" + *thisvalue + "\"";
899 if (*thisvalue == current_value)
900 macrovalue += " selected";
901 macrovalue += ">_" + *thisvalue + "_\n";
902 }
903 ++thisvalue;
904 }
905 macrovalue += "</select>\n";
906 disp.setmacro ("gformselection", displayclass::defaultpackage, macrovalue);
907}
908void queryaction::define_form_macros (displayclass &disp, cgiargsclass &args,
909 recptprotolistclass *protos, ostream &logout) {
910
911 // defines the following macros
912 // _regformlist_
913 // _advformlist_
914
915 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
916 return; // dont need these macros
917
918 text_t form = "";
919 int argfqn = args.getintarg("fqn");
920
921 if (args["b"] == "1") { // advanced form
922 form += "_firstadvformelement_\n";
923 for (int i=1; i<argfqn; ++i) {
924 form += "_advformelement_\n";
925 }
926 disp.setmacro("advformlist", "query", form);
927 }
928 else { // simple form
929 for (int i=0; i<argfqn; ++i) {
930 form += "_regformelement_\n";
931 }
932 disp.setmacro("regformlist", "query", form);
933 }
934
935}
936
937void queryaction::define_history_macros (displayclass &disp, cgiargsclass &args,
938 recptprotolistclass *protos, ostream &logout) {
939
940 // defines the following macros
941 // _searchhistorylist_
942
943 text_t historylist;
944 int arghd = args.getintarg("hd");
945 if (arghd == 0) {
946 historylist="";
947 }
948 else {
949 historylist = "<!-- Search History List -->\n";
950
951 text_t userid = args["z"];
952 text_tarray entries;
953 if (get_history_info (userid, entries, gdbmhome, logout)) {
954 int count = 1;
955 text_tarray::iterator here = entries.begin();
956 text_tarray::iterator end = entries.end();
957 int numrecords=(int)entries.size();
958 if (numrecords>arghd) { // only display some of them
959 numrecords = arghd;
960 }
961 historylist += "<form action=\"_gwcgi_\" name=\"HistoryForm\"><table width=\"537\">\n";
962
963 for (int i=0; i<numrecords;++i) {
964 text_t query;
965 text_t numdocs;
966 text_t cgiargs;
967 text_t userinfo;
968 text_t escquery;
969 split_saved_query(entries[i],numdocs,cgiargs);
970 parse_saved_args(cgiargs, "q", query); // get query string out
971 decode_cgi_arg(query); // un cgisafe it
972 escquery = escape_quotes(query); // escape the quotes and newlines
973 text_t histvalue = "histvalue";
974 histvalue += i;
975 disp.setmacro(histvalue, "query", escquery);
976 format_user_info(cgiargs, userinfo, args, protos, logout);
977
978 historylist += "<tr><td align=\"right\">_imagehistbutton_(";
979 historylist += i;
980 historylist += ")</td>\n";
981 historylist += "<td><table border=\"1\" cellspacing=\"0\" ";
982 historylist += "cellpadding=\"0\"><tr><td width=\"365\" align=\"left\">"
983 + query
984 + "</td></tr></table></td><td width=\"110\" align=\"center\"><small>"
985 + numdocs;
986 if (numdocs == 1) historylist += " _texthresult_";
987 else historylist += " _texthresults_";
988 if (!userinfo.empty()) {
989 historylist += "<br>( "+userinfo+" )";
990 }
991 historylist += "</small></td>\n";
992 }
993 historylist+="</table></form>\n\n";
994
995 } // if get history info
996 else {
997 historylist += "_textnohistory_";
998 }
999 historylist += "<! ---- end of history list ----->\n";
1000 } // else display list
1001 disp.setmacro("searchhistorylist", "query", historylist);
1002
1003} // define history macros
1004
1005void queryaction::output_ccp (cgiargsclass &args, recptprotolistclass *protos,
1006 displayclass &disp, outconvertclass &outconvert,
1007 ostream &textout, ostream &logout) {
1008
1009 ColInfoResponse_t *cinfo = NULL;
1010 comerror_t err;
1011 InfoFilterOptionsResponse_t fresponse;
1012 InfoFilterOptionsRequest_t frequest;
1013 frequest.filterName = "QueryFilter";
1014
1015 text_t &index = args["h"];
1016 text_t &subcollection = args["j"];
1017 text_t &language = args["n"];
1018
1019 text_tset collections;
1020 text_t arg_cc = args["cc"];
1021 decode_cgi_arg (arg_cc);
1022 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
1023
1024 textout << outconvert << disp << "_query:header_\n"
1025 << "<center>_navigationbar_</center><br>\n"
1026 << "<form name=\"QueryForm\" method=\"get\" action=\"_gwcgi_\">\n"
1027 << "<input type=\"hidden\" name=\"a\" value=\"q\">\n"
1028 << "<input type=\"hidden\" name=\"site\" value=\"_cgiargsite_\"\n"
1029 << "<input type=\"hidden\" name=\"e\" value=\"_compressedoptions_\">\n"
1030 << "<input type=\"hidden\" name=\"ccp\" value=\"1\">\n"
1031 << "<center><table width=\"_pagewidth_\"><tr valign=\"top\">\n"
1032 << "<td>Select collections to search for \"" << args["q"]
1033 << "\" <i>(index=" << index << " subcollection=" << subcollection
1034 << " language=" << language << ")</i></td>\n"
1035 << "<td><input type=\"submit\" value=\"_query:textbeginsearch_\"></td>\n"
1036 << "</tr></table></center>\n"
1037 << "<center><table width=\"_pagewidth_\">\n"
1038 << "<tr><td>\n";
1039
1040 recptprotolistclass::iterator rprotolist_here = protos->begin();
1041 recptprotolistclass::iterator rprotolist_end = protos->end();
1042 while (rprotolist_here != rprotolist_end) {
1043 if ((*rprotolist_here).p != NULL) {
1044
1045 text_tarray collist;
1046 (*rprotolist_here).p->get_collection_list (collist, err, logout);
1047 if (err == noError) {
1048 text_tarray::iterator collist_here = collist.begin();
1049 text_tarray::iterator collist_end = collist.end();
1050 while (collist_here != collist_end) {
1051
1052 cinfo = recpt->get_collectinfo_ptr ((*rprotolist_here).p, *collist_here, logout);
1053 // if (err == noError && cinfo.isPublic && (cinfo.buildDate > 0)) {
1054 if (cinfo != NULL && (cinfo->buildDate > 0)) {
1055
1056 (*rprotolist_here).p->get_filteroptions (*collist_here, frequest, fresponse, err, logout);
1057 if (err == noError) {
1058
1059 FilterOption_tmap::const_iterator it;
1060 FilterOption_tmap::const_iterator end = fresponse.filterOptions.end();
1061 if (!index.empty()) {
1062 it = fresponse.filterOptions.find ("Index");
1063 if (it == end) {++collist_here; continue;}
1064 text_tarray::const_iterator there = (*it).second.validValues.begin();
1065 text_tarray::const_iterator tend = (*it).second.validValues.end();
1066 while (there != tend) {
1067 if (*there == index) break;
1068 ++there;
1069 }
1070 if (there == tend) {++collist_here; continue;}
1071 }
1072 if (!subcollection.empty()) {
1073 it = fresponse.filterOptions.find ("Subcollection");
1074 if (it == end) {++collist_here; continue;}
1075 text_tarray::const_iterator there = (*it).second.validValues.begin();
1076 text_tarray::const_iterator tend = (*it).second.validValues.end();
1077 while (there != tend) {
1078 if (*there == subcollection) break;
1079 ++there;
1080 }
1081 if (there == tend) {++collist_here; continue;}
1082 }
1083 if (!language.empty()) {
1084 it = fresponse.filterOptions.find ("Language");
1085 if (it == end) {++collist_here; continue;}
1086 text_tarray::const_iterator there = (*it).second.validValues.begin();
1087 text_tarray::const_iterator tend = (*it).second.validValues.end();
1088 while (there != tend) {
1089 if (*there == language) break;
1090 ++there;
1091 }
1092 if (there == tend) {++collist_here; continue;}
1093 }
1094
1095 // we've got a matching collection
1096 textout << outconvert << "<input type=\"checkbox\"";
1097
1098 text_tset::const_iterator t = collections.find (*collist_here);
1099 if (t != collections.end()) textout << outconvert << " checked";
1100
1101 text_t collectionname = cinfo->get_collectionmeta("collectionname", args["l"]);
1102 if (collectionname.empty()) {
1103 collectionname = *collist_here;
1104 }
1105 textout << outconvert << disp
1106 << " name=\"cc\" value=\"" << *collist_here << "\">"
1107 << collectionname << "<br>\n";
1108
1109
1110 }
1111 }
1112 ++collist_here;
1113 }
1114 }
1115 }
1116 ++rprotolist_here;
1117 }
1118 textout << outconvert << disp
1119 << "</td></tr></table></center>\n"
1120 << "</form>\n"
1121 << "_query:footer_\n";
1122
1123}
1124
1125bool queryaction::do_action (cgiargsclass &args, recptprotolistclass *protos,
1126 browsermapclass *browsers, displayclass &disp,
1127 outconvertclass &outconvert, ostream &textout,
1128 ostream &logout) {
1129
1130 if (recpt == NULL) {
1131 logout << "ERROR (queryaction::do_action): This action does not contain information\n"
1132 << " about any receptionists. The method set_receptionist was probably\n"
1133 << " not called from the module which instantiated this action.\n";
1134 return true;
1135 }
1136
1137
1138
1139 if (args["ccs"] == "1") {
1140 if (!args["cc"].empty()) {
1141 // query the selected collections
1142 text_t::const_iterator b = args["cc"].begin();
1143 text_t::const_iterator e = args["cc"].end();
1144 if (findchar (b, e, ',') != e) {
1145 if (!search_multiple_collections (args, protos, browsers, disp, outconvert,
1146 textout, logout)) return false;
1147 return true;
1148 } else {
1149 if (!search_single_collection (args, args["cc"], protos, browsers, disp,
1150 outconvert, textout, logout)) return false;
1151 return true;
1152 }
1153 }
1154 }
1155
1156 // simply query the current collection
1157 if (!search_single_collection (args, args["c"], protos, browsers, disp,
1158 outconvert, textout, logout)) return false;
1159 return true;
1160}
1161
1162bool queryaction::search_multiple_collections (cgiargsclass &args, recptprotolistclass *protos,
1163 browsermapclass *browsers, displayclass &disp,
1164 outconvertclass &outconvert, ostream &textout,
1165 ostream &logout) {
1166
1167 text_tarray collections;
1168
1169 text_t arg_cc = args["cc"];
1170 decode_cgi_arg (arg_cc);
1171 splitchar (arg_cc.begin(), arg_cc.end(), ',', collections);
1172
1173 if (collections.empty()) {
1174 logout << "queryaction::search_multiple_collections: No collections "
1175 << "set for doing multiple query - will search current collection\n";
1176 textout << outconvert << disp << "_query:textwarningnocollections_\n";
1177 return search_single_collection (args, args["c"], protos, browsers, disp,
1178 outconvert, textout, logout);
1179 }
1180
1181 // queryaction uses "VList" browser to display results,
1182 // a queries clasification is "Search"
1183 text_t browsertype = "VList";
1184 text_t classification = "Search";
1185
1186 QueryResult_tset results;
1187 map<text_t, colinfo_t, lttext_t> colinfomap;
1188
1189 ColInfoResponse_t *cinfo = NULL;
1190 recptproto *collectproto = NULL;
1191 comerror_t err;
1192 FilterRequest_t request;
1193 FilterResponse_t response;
1194 request.filterResultOptions = FROID | FRmetadata | FRtermFreq | FRranking;
1195 text_t freqmsg = "_textfreqmsg1_";
1196 int numdocs = 0;
1197 isapprox isApprox = Exact;
1198
1199 // what to do about segmentation for multiple colls??
1200 bool segment = false;
1201 text_t formattedstring = "";
1202 get_formatted_query_string(formattedstring, segment, args, disp, logout);
1203
1204 if (formattedstring.empty()) {
1205 // dont bother doing a query if no query string
1206 define_history_macros (disp, args, protos, logout);
1207 textout << outconvert << disp << "_query:header_\n"
1208 << "_query:content_";
1209 textout << outconvert << disp << "_query:footer_";
1210
1211 return true;
1212 }
1213 bool syntax_error = false;
1214
1215 set_queryfilter_options (request, formattedstring, args);
1216
1217 // need to retrieve maxdocs matches for each collection
1218 // (will eventually want to tidy this up, do so caching etc.)
1219 OptionValue_t option;
1220 option.name = "StartResults";
1221 option.value = "1";
1222 request.filterOptions.push_back (option);
1223
1224 option.name = "EndResults";
1225 option.value = args["m"];
1226 request.filterOptions.push_back (option);
1227
1228 text_tarray::iterator col_here = collections.begin();
1229 text_tarray::iterator col_end = collections.end();
1230
1231 map<text_t, int, lttext_t> termfreqs;
1232
1233 // just check the main col for formatting info - use individual format statements, or the main one?
1234
1235 browserclass *bptr = browsers->getbrowser (browsertype);
1236
1237 text_t main_col = args["c"];
1238 cinfo = recpt->get_collectinfo_ptr (collectproto, main_col, logout);
1239 if (cinfo == NULL) {
1240 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL for '"<<main_col<<"'\n";
1241 return false;
1242 }
1243
1244 bool use_main_col_format = false;
1245 if (cinfo->ccsOptions & CCSUniformSearchResultsFormatting) {
1246 use_main_col_format = true;
1247 }
1248
1249 request.fields.erase (request.fields.begin(), request.fields.end());
1250 request.getParents = false;
1251 bptr->load_metadata_defaults (request.fields);
1252
1253 text_t formatstring;
1254 format_t *formatlistptr = new format_t();
1255 if (use_main_col_format) {
1256 // just get one format for main coll and use it for each subcol
1257 if (!get_formatstring (classification, browsertype,
1258 cinfo->format, formatstring)) {
1259 formatstring = bptr->get_default_formatstring();
1260 }
1261
1262 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1263 }
1264
1265 while (col_here != col_end) {
1266
1267 collectproto = protos->getrecptproto (*col_here, logout);
1268 if (collectproto == NULL) {
1269 logout << outconvert << "queryaction::search_multiple_collections: " << *col_here
1270 << " collection has a NULL collectproto, ignoring\n";
1271 ++col_here;
1272 continue;
1273 }
1274 cinfo = recpt->get_collectinfo_ptr (collectproto, *col_here, logout);
1275 if (cinfo == NULL) {
1276 logout << "ERROR (query_action::search_multiple_collections): get_collectinfo_ptr returned NULL\n";
1277 ++col_here;
1278 continue;
1279 }
1280
1281 if (!use_main_col_format) {
1282 request.fields.erase (request.fields.begin(), request.fields.end());
1283 request.getParents = false;
1284 bptr->load_metadata_defaults (request.fields);
1285
1286 //browserclass *bptr = browsers->getbrowser (browsertype);
1287
1288 // get the formatstring if there is one
1289 if (!get_formatstring (classification, browsertype,
1290 cinfo->format, formatstring)) {
1291 formatstring = bptr->get_default_formatstring();
1292 }
1293
1294 formatlistptr = new format_t();
1295 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1296 }
1297
1298 colinfo_t thiscolinfo;
1299 thiscolinfo.formatlistptr = formatlistptr;
1300 thiscolinfo.browserptr = bptr;
1301 colinfomap[*col_here] = thiscolinfo;
1302
1303 // do the query
1304 collectproto->filter (*col_here, request, response, err, logout);
1305 if (err != noError && err != syntaxError) {
1306 outconvertclass text_t2ascii;
1307 logout << text_t2ascii
1308 << "queryaction::search_multiple_collections: call to QueryFilter failed "
1309 << "for " << *col_here << " collection (" << get_comerror_string (err) << ")\n";
1310 return false;
1311 }
1312
1313 if (err == syntaxError) {
1314 syntax_error = true;
1315 freqmsg = "_textinvalidquery_";
1316 // assume the syntax will be invalid for all colls
1317 break;
1318 }
1319 if (response.error_message == "TOO_MANY_CLAUSES") {
1320 freqmsg = "_textlucenetoomanyclauses_";
1321 break;
1322 }
1323 if (isApprox == Exact)
1324 isApprox = response.isApprox;
1325 else if (isApprox == MoreThan)
1326 if (response.isApprox == Approximate)
1327 isApprox = response.isApprox;
1328
1329 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1330 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1331 while (this_term != end_term) {
1332 termfreqs[(*this_term).term] += (*this_term).freq;
1333 if ((col_here+1) == col_end) {
1334 freqmsg += (*this_term).term + ": " + termfreqs[(*this_term).term];
1335 if ((this_term+1) != end_term) freqmsg += ", ";
1336 }
1337 ++this_term;
1338 }
1339
1340 if (response.numDocs > 0) {
1341 numdocs += response.numDocs;
1342
1343 QueryResult_t thisresult;
1344 thisresult.collection = *col_here;
1345 ResultDocInfo_tarray::iterator doc_here = response.docInfo.begin();
1346 ResultDocInfo_tarray::iterator doc_end = response.docInfo.end();
1347 while (doc_here != doc_end) {
1348 thisresult.doc = *doc_here;
1349 results.insert (thisresult);
1350 ++doc_here;
1351 }
1352 }
1353 ++col_here;
1354 } // for each coll
1355
1356 text_t numdocs_t = numdocs;
1357 args["nmd"] = numdocs_t;
1358
1359 disp.setmacro ("freqmsg", "query", freqmsg);
1360
1361 define_query_macros( args, disp, numdocs, isApprox);
1362 // save the query if appropriate
1363 save_search_history(args, numdocs, isApprox);
1364 define_history_macros (disp, args, protos, logout);
1365
1366 textout << outconvert << disp << "_query:header_\n"
1367 << "_query:content_";
1368
1369 if (!syntax_error) {
1370
1371 // now go through each result and output it
1372 QueryResult_tset::iterator res_here = results.begin();
1373 QueryResult_tset::iterator res_end = results.end();
1374 text_tset metadata; // empty !!
1375 bool getParents = false; // don't care !!
1376 bool use_table;
1377 ResultDocInfo_t thisdoc;
1378 format_t *formatlistptr = NULL;
1379 browserclass *browserptr = NULL;
1380
1381 int count = 1;
1382 int firstdoc = args.getintarg("r");
1383 int hitsperpage = args.getintarg("o");
1384 int thislast = firstdoc + (hitsperpage - 1);
1385
1386 // output results
1387 while (res_here != res_end) {
1388 if (count < firstdoc) {++count; ++res_here; continue;}
1389 if (count > thislast) break;
1390
1391 formatlistptr = colinfomap[(*res_here).collection].formatlistptr;
1392 browserptr = colinfomap[(*res_here).collection].browserptr;
1393 thisdoc = (*res_here).doc;
1394 use_table = is_table_content (formatlistptr);
1395
1396 collectproto = protos->getrecptproto ((*res_here).collection, logout);
1397 if (collectproto == NULL) {
1398 logout << outconvert << "queryaction::search_multiple_collections: " << (*res_here).collection
1399 << " collection has a NULL collectproto, ignoring results\n";
1400 ++res_here;
1401 continue;
1402 }
1403
1404 browserptr->output_section_group (thisdoc, args, (*res_here).collection, 0,
1405 formatlistptr, use_table, metadata, getParents,
1406 collectproto, disp, outconvert, textout, logout);
1407 // textout << outconvert << "(ranking: " << (*res_here).doc.ranking << ")\n";
1408 ++res_here;
1409 ++count;
1410 }
1411 }
1412 textout << outconvert << disp << "_query:footer_";
1413
1414 // clean up the format_t pointers
1415 map<text_t, colinfo_t, lttext_t>::iterator here = colinfomap.begin();
1416 map<text_t, colinfo_t, lttext_t>::iterator end = colinfomap.end();
1417 while (here != end) {
1418 delete ((*here).second.formatlistptr);
1419 ++here;
1420 }
1421 return true;
1422}
1423
1424bool queryaction::search_single_collection (cgiargsclass &args, const text_t &collection,
1425 recptprotolistclass *protos, browsermapclass *browsers,
1426 displayclass &disp, outconvertclass &outconvert,
1427 ostream &textout, ostream &logout) {
1428
1429 recptproto *collectproto = protos->getrecptproto (collection, logout);
1430 if (collectproto == NULL) {
1431 logout << outconvert << "queryaction::search_single_collection: " << collection
1432 << " collection has a NULL collectproto\n";
1433
1434 // Display the "this collection is not installed on this system" page
1435 disp.setmacro("cvariable", displayclass::defaultpackage, collection);
1436 disp.setmacro("content", "query", "<p>_textbadcollection_<p>");
1437
1438 textout << outconvert << disp << "_query:header_\n"
1439 << "_query:content_\n" << "_query:footer_\n";
1440 return true;
1441 }
1442
1443 // queryaction uses "VList" browser to display results,
1444 // a queries clasification is "Search"
1445 text_t browsertype = "VList";
1446 text_t classification = "Search";
1447
1448 comerror_t err;
1449 ColInfoResponse_t *cinfo = recpt->get_collectinfo_ptr (collectproto, collection, logout);
1450
1451 if (cinfo == NULL) {
1452 logout << "ERROR (query_action::search_single_collection): get_collectinfo_ptr returned NULL\n";
1453 return false;
1454 }
1455
1456 bool segment = cinfo->isSegmented;
1457 browserclass *bptr = browsers->getbrowser (browsertype);
1458
1459 // get the formatstring if there is one
1460 text_t formatstring;
1461 if (!get_formatstring (classification, browsertype,
1462 cinfo->format, formatstring)) {
1463 formatstring = bptr->get_default_formatstring();
1464 }
1465 FilterRequest_t request;
1466 FilterResponse_t response;
1467
1468 text_t maxDoc_old = args["o"];
1469 text_t starts_old = args["r"];
1470
1471 //if the "ifl" argument is set ignore the "r" and "o" arg
1472 if(!args["ifl"].empty()){
1473 args["r"] = 1;
1474 args["o"] = args["m"];
1475 }
1476
1477
1478 bptr->set_filter_options (request, args);
1479 bptr->load_metadata_defaults (request.fields);
1480
1481 format_t *formatlistptr = new format_t();
1482 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
1483
1484 // do the query
1485 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
1486 text_t formattedstring = "";
1487 get_formatted_query_string(formattedstring, segment, args, disp, logout);
1488
1489
1490 if (!formattedstring.empty()) { // do the query
1491 // note! formattedstring is in unicode! mg and mgpp must convert!
1492 set_queryfilter_options (request, formattedstring, args);
1493
1494 collectproto->filter (collection, request, response, err, logout);
1495
1496 if (err != noError) {
1497 outconvertclass text_t2ascii;
1498 logout << text_t2ascii
1499 << "queryaction::search_single_collections: call to QueryFilter failed "
1500 << "for " << collection << " collection (" << get_comerror_string (err) << ")\n";
1501
1502 }
1503
1504 // Perform the "I'm feeling lucky" trick if the "ifl" argument is set
1505 if (err == noError && !args["ifl"].empty()) {
1506 //Restore the "r" and "o" arg
1507 args["r"] = starts_old;
1508 args["o"] = maxDoc_old ;
1509
1510 //Find whether DocumentSearchResultLinks is enabled
1511 bool show_links = false;
1512 text_tmap::const_iterator format_here = cinfo->format.begin();
1513 text_tmap::const_iterator format_end = cinfo->format.end();
1514
1515 while (format_here != format_end) {
1516 if (((*format_here).first == "DocumentSearchResultLinks") &&
1517 ((*format_here).second == "true")){
1518 show_links = true;
1519 break;
1520 }
1521 ++format_here;
1522 }
1523
1524 // Find the search result specified by the ifln argument
1525 ResultDocInfo_tarray::iterator thissection = response.docInfo.begin();
1526 int section_number = 1;
1527 int ifln = args["ifln"].getint();
1528
1529 while (section_number != ifln && thissection != response.docInfo.end()) {
1530 thissection++;
1531 section_number++;
1532 }
1533 // If the search result exists, go directly to it
1534 if (section_number == ifln && thissection != response.docInfo.end()) {
1535 // Location response (this URL must have "&" and not "&amp;"!)
1536 int num_docs = response.numDocs;
1537 int srn = 0;
1538 int srp = 0;
1539 if (show_links && section_number < num_docs ) {
1540 srn = section_number + 1;
1541 }
1542 if (show_links && section_number > 1 ) {
1543 srp = section_number - 1;
1544 }
1545
1546 textout << outconvert << disp << "Location: _gwcgi_?e=_compressedoptions_&a=d&c=" << collection << "&d=" << (*thissection).OID <<"&srn="<<srn<<"&srp="<<srp<< "\n\n";
1547 textout << flush;
1548 return true;
1549 }
1550
1551 // There weren't enough (or any) matching documents
1552 // We'll just carry on as if ifl wasn't set. The only catch is that get_cgihead_info won't have
1553 // done the right thing (because ifl was set), so we need to make sure the output is html
1554 textout << "Content-type: text/html\n\n";
1555
1556 }
1557
1558 if (err != noError) {
1559 disp.setmacro("resultline", "query", "_textnodocs_");
1560 if (err == syntaxError) {
1561 disp.setmacro ("freqmsg", "query", "_textinvalidquery_");
1562 } else {
1563 disp.setmacro ("freqmsg", "query", "");
1564 }
1565 } else {
1566
1567 define_query_macros (args, disp, response.numDocs, response.isApprox);
1568 define_single_query_macros(args, disp, response);
1569 // save the query if appropriate
1570 save_search_history(args, response.numDocs, response.isApprox);
1571 }
1572
1573 // If Lucene threw a TooManyClauses exception, tell the user about it
1574 if (args["ct"] == 2 && response.error_message == "TOO_MANY_CLAUSES") {
1575 disp.setmacro ("freqmsg", "query", "_textlucenetoomanyclauses_");
1576 }
1577 }
1578 define_history_macros (disp, args, protos, logout);
1579
1580 textout << outconvert << disp << "_query:header_\n"
1581 << "_query:content_";
1582
1583 if (err == noError) {
1584 // output the results
1585 text_t numdocs_t = response.numDocs;
1586 args["nmd"] = numdocs_t;
1587 bool use_table = is_table_content (formatlistptr);
1588 bptr->output_section_group (response, args, collection, 0, formatlistptr,
1589 use_table, request.fields, request.getParents,
1590 collectproto, disp, outconvert, textout, logout);
1591 }
1592
1593 textout << outconvert << disp << "_query:footer_";
1594
1595 delete (formatlistptr);
1596
1597 return true;
1598}
1599
1600// does the formatting of the query string - either uses q for a text search
1601// or the form values for an form search
1602// also adds dates if appropriate in text search
1603void queryaction::get_formatted_query_string (text_t &formattedstring,
1604 bool segment,
1605 cgiargsclass &args,
1606 displayclass &disp,
1607 ostream &logout) {
1608 if (args["qt"]=="0" && args["qto"] != "2") { // normal text search
1609 formattedstring = args["q"];
1610 // remove & | ! for simple search,do segmentation if necessary
1611 format_querystring (formattedstring, args.getintarg("b"), segment);
1612 if (args["ct"]!=0) { // mgpp and lucene - need to add in tag info if appropriate
1613 format_field_info(formattedstring, args["fqf"], args.getintarg("ct"),
1614 args.getintarg("t"), args.getintarg("b"));
1615 }
1616
1617 add_dates(formattedstring, args.getintarg("ds"), args.getintarg("de"),
1618 args.getintarg("dsbc"), args.getintarg("debc"),
1619 args.getintarg("ct"));
1620 args["q"] = formattedstring;
1621
1622 }
1623 else if (args["qt"]=="1" || args["qto"]=="2"){ // form search
1624
1625 if (args["b"]=="1" && args["fqa"]=="1") { // explicit query
1626 formattedstring = args["q"];
1627 }
1628 else { // form search
1629 if (args["b"]=="0") { // regular form
1630 parse_reg_query_form(formattedstring, args, segment);
1631 }
1632 else { // advanced form
1633 parse_adv_query_form(formattedstring, args, segment);
1634 }
1635 args["q"] = formattedstring;
1636
1637 // reset the cgiargfqv macro - need to escape any quotes in it
1638 disp.setmacro("cgiargfqv", "query", escape_quotes(args["fqv"]));
1639
1640 // also reset the _cgiargq_ macro as it has changed now
1641 disp.setmacro("cgiargq", displayclass::defaultpackage, html_safe(args["q"]));
1642
1643 // reset the compressed options to include the q arg
1644 text_t compressedoptions = recpt->get_compressed_arg(args, logout);
1645 if (!compressedoptions.empty()) {
1646 disp.setmacro ("compressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1647 // need a decoded version of compressedoptions for use within forms
1648 // as browsers encode values from forms before sending to server
1649 // (e.g. %25 becomes %2525)
1650 decode_cgi_arg (compressedoptions);
1651 if (args["w"] == "utf-8") { // if the encoding was utf-8, then compressed options was utf-8, and we need unicode.
1652 // if encoding wasn't utf-8, then compressed opotions may be screwed up, but seems to work for 8 bit encodings?
1653 compressedoptions = to_uni(compressedoptions);
1654 }
1655
1656 disp.setmacro ("decodedcompressedoptions", displayclass::defaultpackage, dm_safe(compressedoptions));
1657 }
1658 } // form search
1659 } // args["qt"]=1
1660 else {
1661 logout << "ERROR (query_action::get_formatted_query_string): querytype not defined\n";
1662 }
1663}
1664
1665
1666// define_query_macros sets the macros that couldn't be set until the
1667// query had been done. Those macros are
1668// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
1669// _thisfirst_, and _thislast_ and _quotedquery_
1670// this has been simplified so it can be used with both search_single_coll
1671// and search_multiple_coll
1672void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
1673 int numdocs, isapprox isApprox) {
1674
1675 // set up _resultline_ macro
1676 text_t resline;
1677 int maxdocs = args.getintarg("m");
1678 if (num_phrases > 0) isApprox = Exact;
1679 if (maxdocs == -1) maxdocs = numdocs;
1680 else if (numdocs > maxdocs) {
1681 numdocs = maxdocs;
1682 isApprox = MoreThan;
1683 }
1684
1685 if (isApprox == Approximate) resline = "_textapprox_";
1686 else if (isApprox == MoreThan) resline = "_textmorethan_";
1687
1688 if (numdocs == 0) resline = "_textnodocs_";
1689 else if (numdocs == 1) resline += "_text1doc_";
1690 else resline += text_t(numdocs) + " _textlotsdocs_";
1691
1692 disp.setmacro("resultline", "query", resline);
1693
1694 int firstdoc = args.getintarg("r");
1695 int hitsperpage = args.getintarg("o");
1696 if (hitsperpage == -1) hitsperpage = numdocs;
1697
1698 // set up _thisfirst_ and _thislast_ macros
1699 disp.setmacro ("thisfirst", "query", firstdoc);
1700 int thislast = firstdoc + (hitsperpage - 1);
1701 if (thislast > numdocs) thislast = numdocs;
1702 disp.setmacro ("thislast", "query", thislast);
1703
1704 // set up _prevfirst_ and _prevlast_ macros
1705 if (firstdoc > 1) {
1706 disp.setmacro ("prevlast", "query", firstdoc - 1);
1707 int prevfirst = firstdoc - hitsperpage;
1708 if (prevfirst < 1) prevfirst = 1;
1709 disp.setmacro ("prevfirst", "query", prevfirst);
1710 }
1711
1712 // set up _nextfirst_ and _nextlast_ macros
1713 if (thislast < numdocs) {
1714 disp.setmacro ("nextfirst", "query", thislast + 1);
1715 int nextlast = thislast + hitsperpage;
1716 if (nextlast > numdocs) nextlast = numdocs;
1717 disp.setmacro ("nextlast", "query", nextlast);
1718 }
1719
1720 // do quoted query here cos we may have added quotes during query pre-processing
1721 if (args["ct"]==0) { // mg queries only, not mgpp
1722 // get the quoted bits of the query string and set _quotedquery_
1723 text_tarray phrases;
1724 get_phrases (args["q"], phrases);
1725 num_phrases = phrases.size();
1726 text_tarray::const_iterator phere = phrases.begin();
1727 text_tarray::const_iterator pend = phrases.end();
1728 bool first = true;
1729 text_t quotedquery;
1730 while (phere != pend) {
1731 if (!first)
1732 if ((phere +1) == pend) quotedquery += " and ";
1733 else quotedquery += ", ";
1734
1735 quotedquery += "\"" + *phere + "\"";
1736 first = false;
1737 ++phere;
1738 }
1739 if (args.getintarg("s") && !quotedquery.empty()) quotedquery += "_textstemon_";
1740 disp.setmacro ("quotedquery", "query", quotedquery);
1741 }
1742
1743}
1744
1745// define_single_query_macros sets the extra macros for search_single_coll
1746// that couldn't be set until the query had been done. Those macros are
1747// _freqmsg_ and _stopwordsmsg_
1748void queryaction::define_single_query_macros (cgiargsclass &args,
1749 displayclass &disp,
1750 const FilterResponse_t &response) {
1751 // set up _freqmsg_ and _stopwordsmsg_ macros
1752
1753 text_t freqmsg = "";
1754 freqmsg = "_textfreqmsg1_";
1755 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
1756 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
1757 while (this_term != end_term) {
1758 freqmsg += (*this_term).term + ": " + (*this_term).freq;
1759 if ((this_term + 1) != end_term)
1760 freqmsg += ", ";
1761 ++this_term;
1762 }
1763 disp.setmacro ("freqmsg", "query", freqmsg);
1764
1765 text_tset::const_iterator this_stopword = response.stopwords.begin();
1766 text_tset::const_iterator end_stopword = response.stopwords.end();
1767 if (this_stopword != end_stopword) {
1768 text_t stopwordsmsg = "_textstopwordsmsg_ ";
1769 while (this_stopword != end_stopword) {
1770 if (stopwordsmsg != "_textstopwordsmsg_ ") {
1771 stopwordsmsg += ", ";
1772 }
1773 stopwordsmsg += (*this_stopword);
1774 ++this_stopword;
1775 }
1776 disp.setmacro("stopwordsmsg", "query", stopwordsmsg);
1777 }
1778}
1779
1780// should this change for cross coll search??
1781bool queryaction::save_search_history (cgiargsclass &args, int numdocs,
1782 isapprox isApprox) {
1783 if (args["q"]=="") return true; // null query, dont save
1784 if (args["hs"]=="0") return true; // only save when submit query pressed
1785
1786 // get userid
1787 text_t userid = args["z"];
1788
1789 // the number of docs goes on the front of the query string
1790 text_t query = text_t(numdocs);
1791 if (isApprox==MoreThan) { // there were more docs found
1792 query.push_back('+');
1793 }
1794 query += "c="+args["c"];
1795 query += ";h="+args["h"];
1796 query += ";t="+args["t"];
1797 query += ";b="+args["b"];
1798 query += ";j="+args["j"];
1799 query += ";n="+args["n"];
1800 query += ";s="+args["s"];
1801 query += ";k="+args["k"];
1802 query += ";g="+args["g"];
1803
1804 text_t qstring = args["q"];
1805 //text_t formattedquery =cgi_safe(qstring);
1806 //query += "&amp;q="+formattedquery;
1807 query += ";q="+qstring;
1808 bool display=false;
1809 int hd = args.getintarg("hd");
1810 if (hd > 0) display=true;
1811 if (set_history_info(userid, query, gdbmhome, display)) return true;
1812 else return false;
1813
1814
1815}
1816
Note: See TracBrowser for help on using the repository browser.