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

Last change on this file since 543 was 543, checked in by rjmcnab, 25 years ago

removed some compiler warnings

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