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

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

added GPL notice

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 20.8 KB
Line 
1/**********************************************************************
2 *
3 * queryaction.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * $Id: queryaction.cpp 533 1999-09-07 04:57:01Z sjboddie $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.21 1999/09/07 04:56:57 sjboddie
31 added GPL notice
32
33 Revision 1.20 1999/08/25 04:47:55 sjboddie
34 added advanced search option - other minor changes
35
36 Revision 1.19 1999/08/13 04:17:24 sjboddie
37 small change to do with new collection-level metadata
38
39 Revision 1.18 1999/08/10 22:46:33 sjboddie
40 changed format option result to QueryResults and added QueryLinks option
41
42 Revision 1.17 1999/07/30 02:24:42 sjboddie
43 added collectinfo argument to some functions
44
45 Revision 1.16 1999/07/19 00:16:58 sjboddie
46 no longer display documents that don't match all phrases in query string
47
48 Revision 1.15 1999/07/16 08:33:36 rjmcnab
49 Changed the logic for getting the results string slightly
50
51 Revision 1.14 1999/07/16 03:41:29 sjboddie
52 changed isApprox
53
54 Revision 1.13 1999/07/16 00:19:01 sjboddie
55 some changes to the way quoted queries are handled
56
57 Revision 1.12 1999/07/09 02:17:55 rjmcnab
58 Setting macros needed for a second query.
59
60 Revision 1.11 1999/07/07 06:13:10 rjmcnab
61 Added ability to combine two independant queries.
62
63 Revision 1.10 1999/07/07 05:49:35 sjboddie
64 had another crack at the format string code - created a new formattools
65 module. It can now handle {If} and {Or} statements although there's a
66 bug preventing nested if's and or's.
67
68 Revision 1.9 1999/07/01 22:48:46 sjboddie
69 had a go at getting a query result format string working
70
71 Revision 1.8 1999/06/27 22:02:11 sjboddie
72 author is added to queryresults if there is one
73
74 Revision 1.7 1999/06/26 01:10:18 rjmcnab
75 Made h, i, and n arguments saved in the compressed arguments.
76
77 Revision 1.6 1999/06/24 05:12:25 sjboddie
78 lots of small changes
79
80 Revision 1.5 1999/06/16 04:03:48 sjboddie
81 Now sets "cl" arg to "search" when going to a document from a search
82 results page. This allows the close book icon (in hierarchy toc) to
83 take you back to the results page if that's where you came from.
84 If you got to the document page somehow other than from a
85 classification or a search (i.e. if "cl" isn't set) then the close
86 book icon is disabled
87
88 Revision 1.4 1999/06/16 02:08:38 sjboddie
89 got queryaction working
90
91 Revision 1.3 1999/03/25 03:06:45 sjboddie
92
93 altered receptionist slightly so it now passes *collectproto to
94 define_internal_macros and define_external_macros - need it
95 for browseaction
96
97 Revision 1.2 1999/03/03 20:26:50 rjmcnab
98
99 Modified stuff.
100
101 Revision 1.1 1999/02/28 22:45:21 rjmcnab
102
103 Initial revision.
104
105 */
106
107
108#include "queryaction.h"
109#include "querytools.h"
110#include "formattools.h"
111
112queryaction::queryaction () {
113
114 num_phrases = 0;
115
116 // this action uses cgi variable "a"
117 cgiarginfo arg_ainfo;
118 arg_ainfo.shortname = "a";
119 arg_ainfo.longname = "action";
120 arg_ainfo.multiplechar = true;
121 arg_ainfo.defaultstatus = cgiarginfo::weak;
122 arg_ainfo.argdefault = "q";
123 arg_ainfo.savedarginfo = cgiarginfo::must;
124 argsinfo.addarginfo (NULL, arg_ainfo);
125
126 // "b" - 0 = simple, 1 = advanced
127 arg_ainfo.shortname = "b";
128 arg_ainfo.longname = "query mode";
129 arg_ainfo.multiplechar = false;
130 arg_ainfo.defaultstatus = cgiarginfo::weak;
131 arg_ainfo.argdefault = "0";
132 arg_ainfo.savedarginfo = cgiarginfo::must;
133 argsinfo.addarginfo (NULL, arg_ainfo);
134
135 // "h"
136 arg_ainfo.shortname = "h";
137 arg_ainfo.longname = "main index";
138 arg_ainfo.multiplechar = true;
139 arg_ainfo.defaultstatus = cgiarginfo::weak;
140 arg_ainfo.argdefault = "";
141 arg_ainfo.savedarginfo = cgiarginfo::must;
142 argsinfo.addarginfo (NULL, arg_ainfo);
143
144 // "h2"
145 arg_ainfo.shortname = "h2";
146 arg_ainfo.longname = "main index for second query";
147 arg_ainfo.multiplechar = true;
148 arg_ainfo.defaultstatus = cgiarginfo::weak;
149 arg_ainfo.argdefault = "";
150 arg_ainfo.savedarginfo = cgiarginfo::must;
151 argsinfo.addarginfo (NULL, arg_ainfo);
152
153 // "j"
154 arg_ainfo.shortname = "j";
155 arg_ainfo.longname = "sub collection index";
156 arg_ainfo.multiplechar = true;
157 arg_ainfo.defaultstatus = cgiarginfo::weak;
158 arg_ainfo.argdefault = "";
159 arg_ainfo.savedarginfo = cgiarginfo::must;
160 argsinfo.addarginfo (NULL, arg_ainfo);
161
162 // "j2"
163 arg_ainfo.shortname = "j2";
164 arg_ainfo.longname = "sub collection index for second query";
165 arg_ainfo.multiplechar = true;
166 arg_ainfo.defaultstatus = cgiarginfo::weak;
167 arg_ainfo.argdefault = "";
168 arg_ainfo.savedarginfo = cgiarginfo::must;
169 argsinfo.addarginfo (NULL, arg_ainfo);
170
171 // "n"
172 arg_ainfo.shortname = "n";
173 arg_ainfo.longname = "language index";
174 arg_ainfo.multiplechar = true;
175 arg_ainfo.defaultstatus = cgiarginfo::weak;
176 arg_ainfo.argdefault = "";
177 arg_ainfo.savedarginfo = cgiarginfo::must;
178 argsinfo.addarginfo (NULL, arg_ainfo);
179
180 // "n2"
181 arg_ainfo.shortname = "n2";
182 arg_ainfo.longname = "language index for second query";
183 arg_ainfo.multiplechar = true;
184 arg_ainfo.defaultstatus = cgiarginfo::weak;
185 arg_ainfo.argdefault = "";
186 arg_ainfo.savedarginfo = cgiarginfo::must;
187 argsinfo.addarginfo (NULL, arg_ainfo);
188
189 // "q"
190 arg_ainfo.shortname = "q";
191 arg_ainfo.longname = "query string";
192 arg_ainfo.multiplechar = true;
193 arg_ainfo.defaultstatus = cgiarginfo::weak;
194 arg_ainfo.argdefault = "";
195 arg_ainfo.savedarginfo = cgiarginfo::must;
196 argsinfo.addarginfo (NULL, arg_ainfo);
197
198 // "q2"
199 arg_ainfo.shortname = "q2";
200 arg_ainfo.longname = "query string for second query";
201 arg_ainfo.multiplechar = true;
202 arg_ainfo.defaultstatus = cgiarginfo::weak;
203 arg_ainfo.argdefault = "";
204 arg_ainfo.savedarginfo = cgiarginfo::must;
205 argsinfo.addarginfo (NULL, arg_ainfo);
206
207 // "cq2" ""=don't combine, "and", "or", "not"
208 arg_ainfo.shortname = "cq2";
209 arg_ainfo.longname = "combine queries";
210 arg_ainfo.multiplechar = true;
211 arg_ainfo.defaultstatus = cgiarginfo::weak;
212 arg_ainfo.argdefault = "";
213 arg_ainfo.savedarginfo = cgiarginfo::must;
214 argsinfo.addarginfo (NULL, arg_ainfo);
215
216 // "t" - 1 = ranked 0 = boolean
217 arg_ainfo.shortname = "t";
218 arg_ainfo.longname = "search type";
219 arg_ainfo.multiplechar = false;
220 arg_ainfo.defaultstatus = cgiarginfo::weak;
221 arg_ainfo.argdefault = "1";
222 arg_ainfo.savedarginfo = cgiarginfo::must;
223 argsinfo.addarginfo (NULL, arg_ainfo);
224
225 // "k"
226 arg_ainfo.shortname = "k";
227 arg_ainfo.longname = "casefolding";
228 arg_ainfo.multiplechar = false;
229 arg_ainfo.defaultstatus = cgiarginfo::weak;
230 arg_ainfo.argdefault = "1";
231 arg_ainfo.savedarginfo = cgiarginfo::must;
232 argsinfo.addarginfo (NULL, arg_ainfo);
233
234 // "s"
235 arg_ainfo.shortname = "s";
236 arg_ainfo.longname = "stemming";
237 arg_ainfo.multiplechar = false;
238 arg_ainfo.defaultstatus = cgiarginfo::weak;
239 arg_ainfo.argdefault ="0";
240 arg_ainfo.savedarginfo = cgiarginfo::must;
241 argsinfo.addarginfo (NULL, arg_ainfo);
242
243 // "m"
244 arg_ainfo.shortname = "m";
245 arg_ainfo.longname = "maximum number of documents";
246 arg_ainfo.multiplechar = true;
247 arg_ainfo.defaultstatus = cgiarginfo::weak;
248 arg_ainfo.argdefault = "50";
249 arg_ainfo.savedarginfo = cgiarginfo::must;
250 argsinfo.addarginfo (NULL, arg_ainfo);
251
252 // "o"
253 arg_ainfo.shortname = "o";
254 arg_ainfo.longname = "hits per page";
255 arg_ainfo.multiplechar = true;
256 arg_ainfo.defaultstatus = cgiarginfo::weak;
257 arg_ainfo.argdefault = "20";
258 arg_ainfo.savedarginfo = cgiarginfo::must;
259 argsinfo.addarginfo (NULL, arg_ainfo);
260
261 // "r"
262 arg_ainfo.shortname = "r";
263 arg_ainfo.longname = "start results from";
264 arg_ainfo.multiplechar = true;
265 arg_ainfo.defaultstatus = cgiarginfo::weak;
266 arg_ainfo.argdefault = "1";
267 arg_ainfo.savedarginfo = cgiarginfo::must;
268 argsinfo.addarginfo (NULL, arg_ainfo);
269}
270
271void queryaction::configure (const text_t &key, const text_tarray &cfgline) {
272 action::configure (key, cfgline);
273}
274
275bool queryaction::init (ostream &logout) {
276 return action::init (logout);
277}
278
279bool queryaction::check_cgiargs (cgiargsinfoclass &argsinfo, cgiargsclass &args,
280 ostream &logout) {
281
282 // check t argument
283 int arg_t = args.getintarg("t");
284 if (arg_t != 0 && arg_t != 1) {
285 logout << "Warning: \"t\" argument out of range (" << arg_t << ")\n";
286 cgiarginfo *tinfo = argsinfo.getarginfo ("t");
287 if (tinfo != NULL) args["t"] = tinfo->argdefault;
288 }
289
290 // check k argument
291 int arg_k = args.getintarg("k");
292 if (arg_k != 0 && arg_k != 1) {
293 logout << "Warning: \"k\" argument out of range (" << arg_k << ")\n";
294 cgiarginfo *kinfo = argsinfo.getarginfo ("k");
295 if (kinfo != NULL) args["k"] = kinfo->argdefault;
296 }
297
298 // check s argument
299 int arg_s = args.getintarg("s");
300 if (arg_s != 0 && arg_s != 1) {
301 logout << "Warning: \"s\" argument out of range (" << arg_s << ")\n";
302 cgiarginfo *sinfo = argsinfo.getarginfo ("s");
303 if (sinfo != NULL) args["s"] = sinfo->argdefault;
304 }
305
306 // check m argument
307 int arg_m = args.getintarg("m");
308 if (arg_m < 0) {
309 logout << "Warning: \"m\" argument less than 0 (" << arg_m << ")\n";
310 cgiarginfo *minfo = argsinfo.getarginfo ("m");
311 if (minfo != NULL) args["m"] = minfo->argdefault;
312 }
313
314 // check o argument
315 int arg_o = args.getintarg("o");
316 if (arg_o < 0) {
317 logout << "Warning: \"o\" argument less than 0 (" << arg_o << ")\n";
318 cgiarginfo *oinfo = argsinfo.getarginfo ("o");
319 if (oinfo != NULL) args["o"] = oinfo->argdefault;
320 }
321
322 // check r argument
323 int arg_r = args.getintarg("r");
324 if (arg_r < 1) {
325 logout << "Warning: \"r\" argument less than 1 (" << arg_r << ")\n";
326 cgiarginfo *rinfo = argsinfo.getarginfo ("r");
327 if (rinfo != NULL) args["r"] = rinfo->argdefault;
328 }
329
330 return true;
331}
332
333void queryaction::get_cgihead_info (cgiargsclass &/*args*/, response_t &response,
334 text_t &response_data, ostream &/*logout*/) {
335 response = content;
336 response_data = "text/html";
337}
338
339void queryaction::define_internal_macros (const ColInfoResponse_t &/*collectinfo*/, displayclass &disp,
340 cgiargsclass &args, recptproto */*collectproto*/,
341 ostream &/*logout*/) {
342
343 // define_internal_macros sets the following macros:
344
345 // _quotedquery_ the part of the query string that was quoted for post-processing
346
347
348
349 // The following macros are set later (in define_query_macros) as they can't be set until
350 // the query has been done.
351
352 // _freqmsg_ the term frequency string
353
354 // _resultline_ the "x documents matched the query" string
355
356 // _prevfirst_ these are used when setting up the links to previous/next
357 // _prevlast_ pages of results (_thisfirst_ and _thislast_ are used to set
358 // _nextfirst_ the 'results x-x for query: xxxx' string in the title bar)
359 // _nextlast_
360 // _thisfirst_
361 // _thislast_
362
363
364 // get the quoted bits of the query string and set _quotedquery_
365 text_tarray phrases;
366 get_phrases (args["q"], phrases);
367 text_tarray::const_iterator phere = phrases.begin();
368 text_tarray::const_iterator pend = phrases.end();
369 bool first = true;
370 text_t quotedquery;
371 while (phere != pend) {
372 if (!first)
373 if ((phere +1) == pend) quotedquery += " and ";
374 else quotedquery += ", ";
375
376 quotedquery += "\"" + *phere + "\"";
377 first = false;
378 phere ++;
379 }
380 if (args.getintarg("s")) quotedquery += "_textstemon_";
381 disp.setmacro ("quotedquery", "query", quotedquery);
382
383 // we'll also set num_phrases here so we don't have to parse the
384 // querystring again later (we need to know this before outputting
385 // results so we don't include results for documents not containing
386 // all requested phrases).
387 num_phrases = phrases.size();
388
389}
390
391// sets the selection box macros _hselection_, _jselection_, and _nselection_.
392void queryaction::set_option_macro (const text_t &macroname, text_t current_value,
393 const FilterOption_t &option, displayclass &disp) {
394
395 if (option.validValues.size() < 2) return;
396
397 text_t macrovalue = "<select name=\"" + macroname + "\">\n";
398
399 if (current_value.empty()) current_value = option.defaultValue;
400
401 text_tarray::const_iterator thisvalue = option.validValues.begin();
402 text_tarray::const_iterator endvalue = option.validValues.end();
403
404 while (thisvalue != endvalue) {
405 macrovalue += "<option value=\"" + *thisvalue + "\"";
406 if (*thisvalue == current_value)
407 macrovalue += " selected";
408 macrovalue += ">_" + *thisvalue + "_\n";
409 thisvalue ++;
410 }
411 macrovalue += "</select>\n";
412 disp.setmacro (macroname + "selection", "Global", macrovalue);
413}
414
415void queryaction::define_external_macros (const ColInfoResponse_t &/*collectinfo*/, displayclass &disp,
416 cgiargsclass &args, recptproto *collectproto,
417 ostream &logout) {
418
419 // define_external_macros sets the following macros:
420
421 // some or all of these may not be required to be set
422 // _hselection_, _h2selection_ the selection box for the main part of the index
423 // _jselection_, _j2selection_ the selection box for the subcollection part of the index
424 // _nselection_, _n2selection_ the selection box for the language part of the index
425 // _cq2selection the selection box for combining two queries
426
427
428 // can't do anything if collectproto is null (i.e. no collection was specified)
429 if (collectproto == NULL) return;
430
431 comerror_t err;
432 InfoFilterOptionsResponse_t response;
433 InfoFilterOptionsRequest_t request;
434 request.filterName = "QueryFilter";
435
436 collectproto->get_filteroptions (args["c"], request, response, err, logout);
437 if (err == noError) {
438
439 FilterOption_tmap::const_iterator it;
440 FilterOption_tmap::const_iterator end = response.filterOptions.end();
441
442 // _hselection_ and _h2selection_ (Index)
443 it = response.filterOptions.find ("Index");
444 if (it != end) set_option_macro ("h", args["h"], (*it).second, disp);
445 if (it != end) set_option_macro ("h2", args["h2"], (*it).second, disp);
446
447 // _jselection_ and _j2selection_ (Subcollection)
448 it = response.filterOptions.find ("Subcollection");
449 if (it != end) set_option_macro ("j", args["j"], (*it).second, disp);
450 if (it != end) set_option_macro ("j2", args["j2"], (*it).second, disp);
451
452 // _nselection_ and _n2selection_ (Language)
453 it = response.filterOptions.find ("Language");
454 if (it != end) set_option_macro ("n", args["n"], (*it).second, disp);
455 if (it != end) set_option_macro ("n2", args["n2"], (*it).second, disp);
456
457 // _cq2selection_ (CombineQuery)
458 it = response.filterOptions.find ("CombineQuery");
459 if (it != end) set_option_macro ("cq2", args["cq2"], (*it).second, disp);
460 }
461}
462
463bool queryaction::do_action (cgiargsclass &args, const ColInfoResponse_t &collectinfo,
464 recptproto *collectproto, displayclass &disp,
465 outconvertclass &outconvert, ostream &textout,
466 ostream &logout) {
467
468 if (formatstring.empty()) {
469 text_tmap::const_iterator result = collectinfo.format.find("QueryResults");
470 if (result != collectinfo.format.end())
471 formatstring = (*result).second;
472 }
473
474 // see if there's a QueryLinks format option
475 text_t querylinkmeta;
476 bool havequerylink = false;
477 text_tmap::const_iterator it = collectinfo.format.find("QueryLinks");
478 if (it != collectinfo.format.end()) {
479 querylinkmeta = (*it).second;
480 havequerylink = true;
481 }
482
483 // if we still don't have a format string use the default
484 if (formatstring.empty())
485 formatstring = "<td valign=top nowrap>[link]_icontext_[/link]</td><td>[Title]</td>";
486
487 if (collectproto == NULL) {
488 logout << "queryaction::do_action called with NULL collectproto\n";
489 textout << outconvert << disp << "_query:header_\n"
490 << "Error: Attempt to do query without setting collection\n"
491 << "_query:footer_\n";
492 } else {
493
494 FilterRequest_t request;
495 FilterResponse_t response;
496 format_t *formatlistptr = new format_t();
497
498 parse_formatstring (formatstring, formatlistptr, request.fields, request.getParents);
499
500 if (havequerylink)
501 request.fields.push_back (querylinkmeta);
502
503 int metasize = request.fields.size();
504
505 // do the query
506 request.filterResultOptions = FROID | FRmetadata | FRtermFreq;
507 if (!do_query (request, args, collectproto, response, logout))
508 return false;
509
510 // set macros
511 define_query_macros (args, disp, response);
512
513 // output the header
514 textout << outconvert << disp << "_query:header_\n"
515 << "_query:content_";
516
517 // output the results
518 textout << "<table cellspacing=4>\n";
519 ResultDocInfo_tarray::const_iterator this_doc = response.docInfo.begin();
520 ResultDocInfo_tarray::const_iterator end_doc = response.docInfo.end();
521
522 while (this_doc != end_doc) {
523 // don't include docs that didn't match phrases (if there were any)
524 // those that did match will have been sorted to the top
525 if ((*this_doc).num_phrase_match < num_phrases) break;
526 textout << "<tr>\n";
527 if (havequerylink) {
528 const text_t &qlmeta = (*this_doc).metadata[metasize-1].values.back();
529 if (qlmeta.empty())
530 textout << outconvert << disp
531 << get_formatted_string (*this_doc, formatlistptr, "", "_iconblanktext_") << "\n";
532 else
533 textout << outconvert << disp
534 << get_formatted_string (*this_doc, formatlistptr) << "\n";
535 } else {
536 textout << outconvert << disp
537 << get_formatted_string (*this_doc, formatlistptr) << "\n";
538 }
539 textout << "</tr>\n";
540
541 this_doc ++;
542 }
543 textout << "</table>\n";
544
545 delete (formatlistptr);
546
547 // output the footer
548 textout << outconvert << disp << "_query:footer_";
549 }
550
551 return true;
552}
553
554// define_query_macros sets the macros that couldn't be set until the
555// query had been done. Those macros are _freqmsg_, _quotedquery_,
556// _resultline_, _nextfirst_, _nextlast_, _prevfirst_, _prevlast_,
557// _thisfirst_, and _thislast_
558void queryaction::define_query_macros (cgiargsclass &args, displayclass &disp,
559 const FilterResponse_t &response) {
560 // set up _freqmsg_ and _quotedquery_ macros
561 text_t freqmsg = "_textfreqmsg1_";
562 TermInfo_tarray::const_iterator this_term = response.termInfo.begin();
563 TermInfo_tarray::const_iterator end_term = response.termInfo.end();
564 while (this_term != end_term) {
565 freqmsg += (*this_term).term + ": " + (*this_term).freq;
566 if ((this_term + 1) != end_term)
567 freqmsg += ", ";
568 this_term ++;
569 }
570
571 disp.setmacro ("freqmsg", "query", freqmsg);
572
573
574 // set up _resultline_ macro
575 text_t resline;
576 int maxdocs = args.getintarg("m");
577 int numdocs = response.numDocs;
578 isapprox isApprox = response.isApprox;
579
580 // if there were phrases (post-processing) we're not going to include
581 // those documents that didn't match
582 if (num_phrases > 0) {
583 numdocs = 0;
584 isApprox = Exact;
585 ResultDocInfo_tarray::const_iterator this_doc = response.docInfo.begin();
586 ResultDocInfo_tarray::const_iterator end_doc = response.docInfo.end();
587 while (this_doc != end_doc) {
588 if ((*this_doc).num_phrase_match == num_phrases) numdocs ++;
589 else break; // we can bail here as matching docs are sorted to top
590 this_doc++;
591 }
592 }
593
594 // if (isApprox == MoreThan && numdocs > maxdocs) numdocs = maxdocs;
595 if (numdocs > maxdocs) {numdocs = maxdocs; isApprox = MoreThan;}
596
597 if (isApprox == Approximate) resline = "_textapprox_";
598 else if (isApprox == MoreThan) resline = "_textmorethan_";
599
600 if (numdocs == 0) resline = "_textnodocs_";
601 else if (numdocs == 1) resline += "_text1doc_";
602 else resline += text_t(numdocs) + " _textlotsdocs_";
603
604 disp.setmacro("resultline", "query", resline);
605
606
607 int firstdoc = args.getintarg("r");
608 int hitsperpage = args.getintarg("o");
609
610 // set up _thisfirst_ and _thislast_ macros
611 disp.setmacro ("thisfirst", "query", firstdoc);
612 int thislast = firstdoc + (hitsperpage - 1);
613 if (thislast > numdocs) thislast = numdocs;
614 disp.setmacro ("thislast", "query", thislast);
615
616 // set up _prevfirst_ and _prevlast_ macros
617 if (firstdoc > 1) {
618 disp.setmacro ("prevlast", "query", firstdoc - 1);
619 int prevfirst = firstdoc - hitsperpage;
620 if (prevfirst < 1) prevfirst = 1;
621 disp.setmacro ("prevfirst", "query", prevfirst);
622 }
623
624 // set up _nextfirst_ and _nextlast_ macros
625 if (thislast < numdocs) {
626 disp.setmacro ("nextfirst", "query", thislast + 1);
627 int nextlast = thislast + hitsperpage;
628 if (nextlast > numdocs) nextlast = numdocs;
629 disp.setmacro ("nextlast", "query", nextlast);
630 }
631}
632
633
634
Note: See TracBrowser for help on using the repository browser.