source: branches/z3950-branch/gsdl/src/recpt/historydb.cpp@ 1342

Last change on this file since 1342 was 1342, checked in by johnmcp, 24 years ago

Relatively stable z39.50 implementation now, merged with the mgpp source.
(Still needs a decent interface and query language though...)

  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1/**********************************************************************
2 *
3 * historydb.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 "historydb.h"
27#include "fileutil.h"
28#include "cgiutils.h"
29#include "recptproto.h"
30#include "OIDtools.h"
31
32
33// returns true on success (in which case historyinfo will contain
34// the information for this history)
35bool get_history_info (const text_t &userid, text_tarray &historyinfo,
36 const text_t &gsdlhome, ostream &logout) {
37
38 text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
39
40 bool result = false;
41 // open the history database
42 gdbmclass historydb;
43
44 if (historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
45 // get history list
46 text_t historyresult;
47
48 historydb.getkeydata(userid, historyresult);
49
50 if (historyresult != "") { // there are entries, process them
51
52 splitchar(historyresult.begin(), historyresult.end(), '\n', historyinfo);
53 result = true;
54 }
55 historydb.closedatabase();
56
57 } else {
58 outconvertclass text_t2ascii;
59 logout << text_t2ascii << "couldn't open history database " << historyfile << "\n";
60 }
61 return result;
62}
63
64
65// returns true on success
66bool set_history_info (const text_t &userid, const text_t &history, const text_t &gsdlhome) {
67
68 text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
69
70 bool result = false;
71 // open the history database
72 gdbmclass historydb;
73
74 text_t querynum;
75 text_t historyresult;
76
77
78 if ( !historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
79 // not created yet
80 historyresult="";
81 querynum="1";
82 }
83 else {
84
85 // get history list
86 if (! historydb.getkeydata(userid, historyresult)) {
87 historyresult="";
88 querynum="1";
89 }
90 historydb.closedatabase();
91 }
92
93 // get next query num
94 if (historyresult!="") {
95 text_tarray entries;
96 splitchar(historyresult.begin(), historyresult.end(), '\n', entries);
97 get_query_num(entries.back(), querynum);
98 querynum.setint(querynum.getint()+1);
99 }
100
101 // open for writing
102 if (!historydb.opendatabase(historyfile, GDBM_WRCREAT, 1000, true)) return false;
103
104 // add on new line
105 historyresult += querynum;
106 historyresult += ":";
107 historyresult += history;
108 historyresult += "\n";
109
110 if (historydb.setinfo(userid, historyresult))
111 result=true;
112
113 historydb.closedatabase();
114 return result;
115}
116
117// deletes all a users history
118bool delete_all_history_info (const text_t &userid, const text_t &gsdlhome) {
119
120 text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
121
122 // open the history database
123 gdbmclass historydb;
124
125 if ( !historydb.opendatabase(historyfile, GDBM_WRITER, 1000, true)) return false;
126
127 historydb.deletekey(userid);
128 historydb.closedatabase();
129 return true;
130
131}
132
133//deletes only the selected records
134bool delete_history_info (const text_t &userid, const text_t &deletemode,
135 const text_t &selection, const text_t &gsdlhome) {
136
137 text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
138 bool result = false;
139 // open the history database
140 gdbmclass historydb;
141
142 if ( !historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) return false;
143
144 // get history list
145 text_t historyresult;
146
147 if (! historydb.getkeydata(userid, historyresult)) return false;
148
149 historydb.closedatabase();
150
151 text_t newhistory; // for the new list of search history
152 // get list of queries
153 text_tarray queries;
154 splitchar(historyresult.begin(), historyresult.end(), '\n', queries);
155 text_tarray::iterator begin=queries.begin();
156 text_tarray::iterator end=queries.end();
157
158 // get list of numbers to delete
159 text_tarray numbers;
160 splitchar(selection.begin(), selection.end(), ',', numbers);
161 text_tarray::iterator numbegin=numbers.begin();
162 text_tarray::iterator numend=numbers.end();
163
164 bool tdefault, tset;
165 if (deletemode=="delete") {
166 tdefault=true;
167 tset=false;
168 }
169 else {// deletemode == save
170 tdefault=false;
171 tset=true;
172 }
173
174 // create a HistRecord map of all the entries
175 HistRecordmap records;
176 while (begin != end) {
177 HistRecord rec;
178 rec.entry=*begin;
179 rec.save=tdefault;
180 text_t num;
181 get_query_num(*begin, num);
182 records[num] = rec;
183 begin++;
184 }
185
186
187 // now set the save field for all records that are selected
188 while(numbegin != numend) {
189
190 if (is_number(*numbegin)) {
191 if (records.count(*numbegin)==1) {
192 records[*numbegin].save=tset;
193 } // else invalid number
194 }
195 else{
196 int start, stop;
197 if (get_selection_params(*numbegin, start, stop)) {
198 for (int i=start; i<=stop; i++) {
199 text_t num = i;
200 if (records.count(num)==1) {
201 records[num].save=tset;
202 }
203 }}
204 else { // dodgy format
205 // error message
206 }
207
208 }
209 numbegin++;
210 } // while
211
212 HistRecordmap::iterator recbegin = records.begin();
213 HistRecordmap::iterator recend = records.end();
214
215 //create new history record - go through all Records
216 // and add to new history those with save set to true
217 while (recbegin != recend) {
218 if ((*recbegin).second.save) {
219 expand_query((*recbegin).second, records);
220 newhistory += (*recbegin).second.entry + "\n";
221 }
222 recbegin++;
223 }
224
225 // open for writing
226 if (!historydb.opendatabase(historyfile, GDBM_WRITER, 1000, true)) return false;
227
228 if (historydb.setinfo(userid, newhistory)) result = true;
229
230 historydb.closedatabase();
231 return result;
232}
233
234bool get_selection_params (text_t &data, int &start, int &stop) {
235
236 text_tarray results;
237
238 splitchar(data.begin(), data.end(), '-', results);
239 if (results.size()==2 && is_number(results[0]) && is_number(results[1])) {
240 start=results[0].getint();
241 stop=results[1].getint();
242 return true;
243 }
244
245 // error message
246 return false;
247}
248
249// expand query takes a HistRecord, and expands out any hashes in
250// the query string for queries that aren't to be saved
251bool expand_query(HistRecord &record, HistRecordmap &records) {
252
253 text_tarray args;
254 splitchar(record.entry.begin(), record.entry.end(), '&', args);
255
256 text_tarray::iterator begin=args.begin();
257 text_tarray::iterator end=args.end();
258
259 text_t query;
260 // find the q argument
261 while(begin != end) {
262 if ((*begin)[0] == 'q'&& (*begin)[1] == '=') {
263 query=*begin;
264 args.erase(begin);
265 break;
266 } else begin++;
267 }
268 if (query !="") {
269
270 //remove the q=
271 text_t::iterator front = query.begin();
272 query.erase(front);
273 front=query.begin();
274
275 query.erase(front);
276 decode_cgi_arg(query);
277 // now have just the query string
278 // check any refs in it to see if they are still valid
279 combine_query(query, records);
280 text_t formattedquery = cgi_safe(query);
281 text_t newquery = "q=";
282 newquery += formattedquery;
283
284 args.push_back(newquery);
285 joinchar(args, '&', record.entry);
286
287 return true;
288 }
289 return false;
290}
291
292// replaces a reference to a previous query with the search
293// string from that query
294bool combine_query(text_t &userid, text_t &query, const text_t &gsdlhome)
295{
296 text_t::iterator begin = query.begin();
297 text_t::iterator end = query.end();
298 text_t::iterator it = findchar(begin, end, '#');
299 if (it==end) return true; // no need to swap anything
300
301
302 text_t queryresult = "";
303 text_t historyfile = filename_cat(gsdlhome, "etc", "history.db");
304 text_t historyresult;
305 // open the history database
306 gdbmclass historydb;
307 bool exists = false;
308 if (historydb.opendatabase(historyfile, GDBM_READER, 1000, true)) {
309 // get history list
310 if (historydb.getkeydata(userid, historyresult)) {
311 exists=true;
312
313 historydb.closedatabase();
314 }
315 }
316 if (!exists) {// cant open history file, or user entry doesn't exist. replace #x with blanks
317 while(it !=end) {
318 queryresult += substr(begin, it);
319 it++;
320 while(*it>='0'&&*it<='9') it++;
321 begin=it;
322 it=findchar(begin, end, '#');
323 }
324 queryresult += substr(begin, end);
325 query = queryresult;
326 return false;
327 }
328 bool result=true;
329
330 // if have got to here, have a previous history in historyresult
331 text_tarray historyinfo;
332 splitchar(historyresult.begin(), historyresult.end(), '\n', historyinfo);
333 text_tarray::iterator histbegin = historyinfo.begin();
334 text_tarray::iterator histend = historyinfo.end();
335
336 text_tmap historymap;
337 // put all entries into a map, so can pull out the appropriate one easily
338 while (histbegin != histend) {
339 text_t num;
340 get_query_num(*histbegin, num);
341 historymap[num]=*histbegin;
342 histbegin++;
343 }
344 while(it!=end) { // while there still is a hash present
345 text_t querynum;
346 text_t newquery;
347
348 while (begin!=end) {
349 if (*begin==text_t('#')) {
350 begin++;
351 while(begin !=end && *begin>='0'&&*begin<='9') {
352 querynum.push_back(*begin);
353 begin++;
354 }
355 text_t oldquery = historymap[querynum];
356 if (oldquery !="") { // valid entry
357 parse_saved_args(oldquery, "q", newquery);
358 decode_cgi_arg(newquery);
359 queryresult += newquery;
360 }
361 else {
362 result=false;
363 }
364 querynum.clear();
365 // else do nothing (replace #x with nothing)
366 } // if
367 else {
368 queryresult.push_back(*begin);
369 begin++;
370 }
371 } // while begin!=end
372
373 // have got to end of query string,
374 // now go back and check for internal #
375 query = queryresult;
376 begin = query.begin();
377 end = query.end();
378 it = findchar(begin, end, '#');
379 queryresult.clear();
380
381 } // while it !=end
382
383
384 return result;
385
386} // combine query
387
388
389// replaces a reference to a previous query with the search
390// string from that query, uses the Histrecordmap rather than the GDBM file
391bool combine_query( text_t &query, HistRecordmap &records)
392{
393 text_t::iterator begin = query.begin();
394 text_t::iterator end = query.end();
395 text_t::iterator it = findchar(begin, end, '#');
396 if (it==end) return true; // no need to swap anything
397
398 text_t queryresult = "";
399 bool changed=true;
400 while(it!=end && changed) { // while there still is a hash present
401 // and query has changed since last time round
402 // we are leaving some #X in
403 text_t querynum;
404 text_t newquery;
405 changed=false;
406 while (begin!=end) { // go through the query looking for hash
407 if (*begin==text_t('#')) {
408 begin++;
409 while(begin !=end && *begin>='0'&&*begin<='9') {
410 querynum.push_back(*begin);
411 begin++;
412 }
413 if(records.count(querynum)>0) { // valid entry
414 if (!records[querynum].save){ // referenced record to be deleted
415 // get the q arg out of referenced query
416 parse_saved_args(records[querynum].entry, "q", newquery);
417 decode_cgi_arg(newquery);
418 queryresult += newquery;
419 changed=true;
420 }
421 else { // leave the #x in
422 queryresult.push_back('#');
423 queryresult += querynum;
424 }
425 }
426 querynum.clear();
427 newquery.clear();
428 // else do nothing (replace #x with nothing)
429 } // if its a hash
430 else {
431 queryresult.push_back(*begin);
432 begin++;
433 }
434 } // while begin!=end
435
436 // have got to end of query string,
437 // now go back and check for internal #
438 query = queryresult;
439 begin = query.begin();
440 end = query.end();
441 it = findchar(begin, end, '#');
442 queryresult.clear();
443
444 } // while it !=end
445
446 return true;
447} // combine query
448
449// retrieves the value of one of the arguments
450void parse_saved_args(text_t &args, text_t key, text_t &value)
451{
452 text_t::iterator here = args.begin();
453 text_t::iterator end = args.end();
454 text_t::iterator it;
455 while (here != end) {
456 if(*here==key[0]) {
457 it=findchar(here, end, '=');
458 if (it==end) {
459 value=""; return;
460 }
461 text_t entry = substr(here, it);
462 if (entry==key) {
463 here=it+1;
464 it=findchar(here, end, '&');
465 value = substr(here, it);
466 return;
467 }
468 }
469 here++;
470 }// while
471}
472
473// retrieves the value of all of the arguments
474void parse_saved_args(text_t &args, infodbclass &info)
475{
476 text_t::iterator here = args.begin();
477 text_t::iterator end = args.end();
478 text_t::iterator it;
479 text_tarray values;
480
481 splitchar(here, end, '&', values);
482
483 text_tarray::iterator start = values.begin();
484 text_tarray::iterator stop = values.end();
485
486 text_t key;
487 text_t value;
488 while(start!=stop) {
489
490 here=(*start).begin();
491 end=(*start).end();
492 it=findchar(here, end, '=');
493 if (it!=end) {
494 key = substr(here, it);
495 value = substr(it+1, end);
496
497 info[key]=value;
498 }
499 start++;
500 }
501}
502
503void get_query_num(text_t &query, text_t &querynum)
504{
505 text_t::iterator begin = query.begin();
506
507 while (*begin >='0'&& *begin <='9') { // get the digits
508 querynum.push_back(*begin);
509 begin++;
510 }
511 if (*begin != ':') {
512 // something has gone wrong
513 }
514}
515
516void split_saved_query(text_t &query, text_t &querynum, text_t &numdocs, text_t &cgiargs)
517{
518 text_t::iterator begin = query.begin();
519 text_t::iterator end = query.end();
520
521 while (*begin >='0'&& *begin <='9') { // get the digits
522 querynum.push_back(*begin);
523 begin++;
524 }
525 if (*begin != ':') {
526 // something has gone wrong
527 }
528 else begin++;
529 while (*begin >='0'&& *begin <='9') { // get the digits
530 numdocs.push_back(*begin);
531 begin++;
532 }
533 if (*begin == '+') { // get the + if there
534 numdocs.push_back(*begin);
535 begin++;
536 }
537
538 cgiargs += (substr(begin, end)); // put rest of query into cgiargs
539
540}
541
542void format_user_info (text_t &cgiargs, text_t &userinfo, recptprotolistclass *protos, ostream &logout)
543{
544 logout << "in format user info";
545 text_tset metadata;
546
547 infodbclass argsinfo;
548 parse_saved_args(cgiargs, argsinfo);
549
550 text_t collect = argsinfo["c"];
551 if (collect=="") {
552 userinfo="";
553 return;
554 }
555 recptproto *collectproto = protos->getrecptproto(collect,logout);
556 if (collectproto == NULL) {
557 userinfo="";
558 return;
559 }
560 metadata.insert(argsinfo["h"]);
561 FilterResponse_t response;
562
563 get_info("collection", collect, metadata, false, collectproto, response, logout);
564
565 text_t index = response.docInfo[0].metadata[argsinfo["h"]].values[0];
566
567 text_t mode;
568 if (argsinfo["b"]=="0") { // simple mode
569 if (argsinfo["t"]=="0") {
570 mode = "all words";
571 }
572 else { // t=1
573 mode = "some words";
574 }
575
576 }
577 else { // advanced mode
578 if (argsinfo["t"]=="0") {
579 mode = "boolean";
580 }
581 else {
582 mode = "ranked";
583 }
584 }
585
586 text_t options;
587 if (argsinfo["k"]=="0") {
588 options = "case must match";
589 }
590 else {
591 options = "ignore case";
592 }
593 if (argsinfo["s"]=="0") {
594 options += ", whole word must match";
595 }
596 else {
597 options += ", ignore word endings";
598 }
599
600 userinfo.clear();
601 userinfo = "collection ("+argsinfo["c"] + ") ";
602 userinfo += "search (" + index + ") ";
603 userinfo += "mode (" + mode +") ";
604 userinfo += "options (" + options + ")";
605
606 logout << "in format user info - end";
607}
Note: See TracBrowser for help on using the repository browser.