543523abbebc901e0a927ef49d20bc6cb05d236d
[idzebra-moved-to-github.git] / isamb / benchindex1.c
1 /* $Id: benchindex1.c,v 1.4 2006-12-11 15:08:55 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <yaz/options.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <yaz/log.h>
28 #include <yaz/nmem.h>
29 #include <yaz/xmalloc.h>
30 #include <yaz/marcdisp.h>
31 #include <it_key.h>
32 #include <idzebra/isamb.h>
33 #include <idzebra/dict.h>
34 #include <assert.h>
35
36 struct index_block {
37     NMEM nmem;
38     int no_entries;
39     size_t current_entry;
40     size_t current_max;
41     struct index_term *terms;
42     struct index_term **ar;
43     int round;
44 };
45
46 struct index_term {
47     const char *term;
48     zint docid;
49     zint seqno;
50     int word_id;
51     struct index_term *next;
52 };
53
54 struct index_block *index_block_new(int memory)
55 {
56     struct index_block *b = xmalloc(sizeof(*b));
57     b->no_entries = 0;
58     b->current_max = memory * 1024 * 1024;
59     b->terms = 0;
60     b->nmem = nmem_create();
61     b->round = 0;
62     return b;
63 }
64
65 void index_block_destroy(struct index_block **bp)
66 {
67     if (*bp)
68     {
69         nmem_destroy((*bp)->nmem);
70         xfree(*bp);
71         *bp = 0;
72     }
73 }
74
75 static int cmp_ar(const void *p1, const void *p2)
76 {
77     struct index_term *t1 = *(struct index_term **) p1;
78     struct index_term *t2 = *(struct index_term **) p2;
79     int d = strcmp(t1->term, t2->term);
80     if (d)
81         return d;
82
83     if (t1->docid > t2->docid)
84         return 1;
85     else if (t1->docid < t2->docid)
86         return -1;
87     if (t1->seqno > t2->seqno)
88         return 1;
89     else if (t1->seqno < t2->seqno)
90         return -1;
91     return 0;
92 }
93
94
95 int code_read(void *vp, char **dst, int *insertMode)
96 {
97     struct index_block *b = (struct index_block *)vp;
98     struct index_term *t;
99     struct it_key key;
100
101     if (b->current_entry >= b->no_entries)
102         return 0;
103     
104     t = b->ar[b->current_entry];
105     b->current_entry++;
106     
107     key.len = 3;
108     key.mem[0] = t->word_id;
109     key.mem[1] = t->docid;
110     key.mem[2] = t->seqno;
111     key.mem[3] = 0;
112
113     memcpy(*dst, &key, sizeof(key));
114
115     (*dst) += sizeof(key);
116     *insertMode = 1;
117 #if 0
118     yaz_log(YLOG_LOG, "returning " ZINT_FORMAT " " ZINT_FORMAT "\n",
119             key.mem[0], key.mem[1]);
120 #endif
121     return 1;
122 }
123
124 void index_block_flush(struct index_block *b, ISAMB isb, Dict dict,
125                        int no_docs)
126 {
127     struct index_term *t = b->terms;
128     int i;
129     int word_id_seq = 0;
130     int no_words = 0, no_new_words = 0;
131     const char *dict_info = 0;
132     ISAM_P isamc_p = 0;
133     zebra_timing_t tim = zebra_timing_create();
134     
135     b->ar = xmalloc(sizeof(*b->ar) * b->no_entries);
136     for (i = 0; i < b->no_entries; i++, t = t->next)
137     {
138         assert(t);
139         b->ar[i] = t;
140     }
141     assert(!t);
142     
143     qsort(b->ar, b->no_entries, sizeof(*b->ar), cmp_ar);
144 #if 0
145     for (i = 0; i < b->no_entries; i++)
146     {
147         printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n",
148                ar[i]->term, ar[i]->docid, ar[i]->seqno);
149     }
150 #endif
151     dict_info = dict_lookup(dict, "_w");
152     if (dict_info)
153     {
154         assert(*dict_info == sizeof(word_id_seq));
155         memcpy(&word_id_seq, dict_info+1, sizeof(word_id_seq));
156     }
157
158     dict_info = dict_lookup(dict, "_i");
159     if (dict_info)
160     {
161         assert(*dict_info == sizeof(isamc_p));
162         memcpy(&isamc_p, dict_info+1, sizeof(isamc_p));
163     }
164
165     for (i = 0; i < b->no_entries; i++)
166     {
167         if (i > 0 && strcmp(b->ar[i-1]->term, b->ar[i]->term) == 0)
168             b->ar[i]->word_id = b->ar[i-1]->word_id;
169         else
170         {
171             const char *dict_info = dict_lookup(dict, b->ar[i]->term);
172             if (dict_info)
173             {
174                 memcpy(&b->ar[i]->word_id, dict_info+1, sizeof(int));
175             }
176             else
177             {
178                 word_id_seq++;
179                 no_new_words++;
180                 dict_insert(dict, b->ar[i]->term, sizeof(int), &word_id_seq);
181                 b->ar[i]->word_id = word_id_seq;
182             }
183             no_words++;
184         }
185     }
186     dict_insert(dict, "_w", sizeof(word_id_seq), &word_id_seq);
187
188     b->current_entry = 0;
189
190     if (b->no_entries)
191     {
192         ISAMC_I isamc_i;
193         
194         isamc_i.clientData = b;
195         isamc_i.read_item = code_read;
196
197         isamb_merge (isb, &isamc_p, &isamc_i);
198
199         assert(isamc_p);
200         dict_insert(dict, "_i", sizeof(isamc_p), &isamc_p);
201     }
202
203     yaz_log(YLOG_LOG, "Flushed %d postings, %d/%d words, %d records",
204             b->no_entries, no_words, no_new_words, no_docs);
205     xfree(b->ar);
206     b->ar = 0;
207     nmem_reset(b->nmem);
208     b->no_entries = 0;
209     b->terms = 0;
210     b->round++;
211
212     zebra_timing_stop(tim);
213
214     printf("%3d %8.6f %5.2f %5.2f\n",
215            b->round,
216            zebra_timing_get_real(tim),
217            zebra_timing_get_user(tim),
218            zebra_timing_get_sys(tim));
219     fflush(stdout);
220     zebra_timing_destroy(&tim);
221 }
222
223 void index_block_check_flush(struct index_block *b, ISAMB isb, Dict dict,
224                              int no_docs)
225 {
226     int total = nmem_total(b->nmem);
227     int max = b->current_max;
228     if (total > max)
229     {
230         yaz_log(YLOG_LOG, "flush to disk total=%d max=%d", total, max);
231         index_block_flush(b, isb, dict, no_docs);
232     }
233 }
234
235 void index_block_add(struct index_block *b,
236                      const char *term, zint docid, zint seqno)
237 {
238     struct index_term *t = nmem_malloc(b->nmem, sizeof(*t));
239     t->term = nmem_strdup(b->nmem, term);
240     t->docid = docid;
241     t->seqno = seqno;
242     t->next = b->terms;
243     b->terms = t;
244     b->no_entries++;
245 }
246
247 void exit_usage(void)
248 {
249     fprintf(stderr, "benchindex1 [-t type] [-m mem] [-i] [inputfile]\n");
250     exit(1);
251 }
252
253 void index_term(struct index_block *b, const char *term,
254                 zint docid, zint *seqno)
255 {
256 #if 0
257     printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n", term,
258            docid, *seqno);
259 #endif
260     index_block_add(b, term, docid, *seqno);
261     (*seqno)++;
262 }
263
264 void index_wrbuf(struct index_block *b, WRBUF wrbuf, zint docid,
265                  int subfield_char)
266 {
267     int nl = 1;
268     const char *cp = wrbuf_buf(wrbuf);
269     char term[4096];
270     size_t sz = 0;
271     zint seqno = 0;
272
273     while (*cp)
274     {
275         if (nl)
276         {
277             int i;
278             if (cp[0] != ' ')
279             {   /* skip field+indicator (e.g. 245 00) */
280                 for (i = 0; i<6 && *cp; i++, cp++)
281                     ;
282             }
283             else
284             {  /* continuation line */
285                 for (i = 0; i<4 && *cp; i++, cp++)
286                     ;
287             }   
288         }
289         nl = 0;
290         if (*cp == '\n')
291         {
292             if (sz)
293             {
294                 index_term(b, term, docid, &seqno);
295                 sz = 0;
296             }
297             nl = 1;
298             cp++;
299         }
300         else if (*cp == subfield_char && cp[1])
301         {
302             if (sz)
303             {
304                 index_term(b, term, docid, &seqno);
305                 sz = 0;
306             }
307             cp += 2;
308         }
309         else if (strchr("$*/-;,.:[]\"&(){} ", *cp))
310         {
311             if (sz)
312             {
313                 index_term(b, term, docid, &seqno);
314                 sz = 0;
315             }
316             cp++;
317         }
318         else
319         {
320             unsigned ch = *(const unsigned char *)cp;
321             if (sz < sizeof(term))
322             {
323                 term[sz] = tolower(ch);
324                 term[sz+1] = '\0';
325                 sz++;
326             }
327             cp++;
328         }            
329     }
330     if (sz)
331         index_term(b, term, docid, &seqno);
332 }
333
334 void index_marc_line_records(ISAMB isb,
335                              Dict dict,
336                              zint *docid_seq,
337                              FILE *inf,
338                              int memory)
339 {
340     WRBUF wrbuf = wrbuf_alloc();
341     int no_docs = 0;
342     int new_rec = 1;
343     char line[4096];
344     struct index_block *b = index_block_new(memory);
345     while(fgets(line, sizeof(line)-1, inf))
346     {
347         if (line[0] == '$')
348         {
349             if (!new_rec)
350                 new_rec = 1;
351             else
352                 new_rec = 0;
353             continue;
354         }
355         if (new_rec)
356         {
357             (*docid_seq)++;
358             no_docs++;
359             index_block_check_flush(b, isb, dict, no_docs);
360             new_rec = 0;
361         }
362
363         if (line[0] == ' ')
364         {
365             /* continuation */
366             wrbuf_puts(wrbuf, line);
367             continue;
368         }
369         else
370         {
371             /* index existing buffer (if any) */
372             if (wrbuf_len(wrbuf))
373             {
374                 index_wrbuf(b, wrbuf, *docid_seq, '*');
375                 wrbuf_rewind(wrbuf);
376             }
377             if (line[0] != ' ' && line[1] != ' ' && line[2] != ' ' &&
378                 line[3] == ' ')
379             {
380                 /* normal field+indicator line */
381                 wrbuf_puts(wrbuf, line);
382             }
383         }
384     }
385     if (wrbuf_len(wrbuf))
386     {
387         index_wrbuf(b, wrbuf, *docid_seq, '*');
388         wrbuf_rewind(wrbuf);
389     }
390     (*docid_seq)++;
391     no_docs++;
392     index_block_check_flush(b, isb, dict, no_docs);
393     index_block_destroy(&b);
394 }
395
396 void index_marc_from_file(ISAMB isb,
397                           Dict dict,
398                           zint *docid_seq,
399                           FILE *inf,
400                           int memory,
401                           int verbose, int print_offset)
402 {
403     yaz_marc_t mt = yaz_marc_create();
404     WRBUF wrbuf = wrbuf_alloc();
405     struct index_block *b = index_block_new(memory);
406     int no_docs = 0;
407
408     while (1)
409     {
410         size_t r;
411         char buf[100001];
412         int len, rlen;
413
414         r = fread (buf, 1, 5, inf);
415         if (r < 5)
416         {
417             if (r && print_offset && verbose)
418                 printf ("<!-- Extra %ld bytes at end of file -->\n",
419                         (long) r);
420             break;
421         }
422         while (*buf < '0' || *buf > '9')
423         {
424             int i;
425             long off = ftell(inf) - 5;
426             if (verbose || print_offset)
427                 printf("<!-- Skipping bad byte %d (0x%02X) at offset "
428                        "%ld (0x%lx) -->\n", 
429                        *buf & 0xff, *buf & 0xff,
430                        off, off);
431             for (i = 0; i<4; i++)
432                 buf[i] = buf[i+1];
433             r = fread(buf+4, 1, 1, inf);
434             if (r < 1)
435                 break;
436         }
437         if (r < 1)
438         {
439             if (verbose || print_offset)
440                 printf ("<!-- End of file with data -->\n");
441             break;
442         }
443         len = atoi_n(buf, 5);
444         if (len < 25 || len > 100000)
445         {
446             long off = ftell(inf) - 5;
447             printf("Bad Length %ld read at offset %ld (%lx)\n",
448                    (long)len, (long) off, (long) off);
449             break;
450         }
451         rlen = len - 5;
452         r = fread (buf + 5, 1, rlen, inf);
453         if (r < rlen)
454             break;
455         yaz_marc_read_iso2709(mt, buf, len);
456         
457         if (yaz_marc_write_line(mt, wrbuf))
458             break;
459
460         index_wrbuf(b, wrbuf, *docid_seq, '$');
461         wrbuf_rewind(wrbuf);
462         (*docid_seq)++;
463
464         no_docs++;
465         index_block_check_flush(b, isb, dict, no_docs);
466     }
467     index_block_flush(b, isb, dict, no_docs);
468     wrbuf_free(wrbuf, 1);
469     yaz_marc_destroy(mt);
470     index_block_destroy(&b);
471 }
472
473 int main(int argc, char **argv)
474 {
475     BFiles bfs;
476     ISAMB isb;
477     ISAMC_M method;
478     Dict dict;
479     int ret;
480     int reset = 0;
481     char *arg;
482     int memory = 5;
483     const char *fname = 0;
484     FILE *inf = stdin;
485     zebra_timing_t tim = 0;
486     zint docid_seq = 1;
487     const char *dict_info;
488     const char *type = "iso2709";
489
490     while ((ret = options("im:t:", argv, argc, &arg)) != -2)
491     {
492         switch(ret)
493         {
494         case 'm':
495             memory = atoi(arg);
496             break;
497         case 'i':
498             reset = 1;
499             break;
500         case 't':
501             if (!strcmp(arg, "iso2709"))
502                 type = "iso2709";
503             else if (!strcmp(arg, "line"))
504                 type = "line";
505             else
506             {
507                 fprintf(stderr, "bad type: %s.\n", arg);
508                 exit_usage();
509             }
510             break;
511         case 0:
512             fname = arg;
513             break;
514         default:
515             fprintf(stderr, "bad option.\n");
516             exit_usage();
517         }
518     }
519         
520     if (fname)
521     {
522         inf = fopen(fname, "rb");
523         if (!inf)
524         {
525             fprintf(stderr, "Cannot open %s\n", fname);
526             exit(1);
527         }
528     }
529     /* setup method (attributes) */
530     method.compare_item = key_compare;
531     method.log_item = key_logdump_txt;
532
533     method.codec.start = iscz1_start;
534     method.codec.decode = iscz1_decode;
535     method.codec.encode = iscz1_encode;
536     method.codec.stop = iscz1_stop;
537     method.codec.reset = iscz1_reset;
538
539     method.debug = 0;
540
541     /* create block system */
542     bfs = bfs_create(0, 0);
543     if (!bfs)
544     {
545         yaz_log(YLOG_WARN, "bfs_create failed");
546         exit(1);
547     }
548
549     if (reset)
550         bf_reset(bfs);
551
552     tim = zebra_timing_create();
553     /* create isam handle */
554     isb = isamb_open (bfs, "isamb", 1, &method, 0);
555     if (!isb)
556     {
557         yaz_log(YLOG_WARN, "isamb_open failed");
558         exit(2);
559     }
560     dict = dict_open(bfs, "dict", 50, 1, 0, 4096);
561
562     dict_info = dict_lookup(dict, "_s");
563     if (dict_info)
564     {
565         assert(*dict_info == sizeof(docid_seq));
566         memcpy(&docid_seq, dict_info+1, sizeof(docid_seq));
567     }
568
569     if (!strcmp(type, "iso2709"))
570         index_marc_from_file(isb, dict, &docid_seq, inf, memory, 
571                              0 /* verbose */ , 0 /* print_offset */);
572     else if (!strcmp(type, "line"))
573         index_marc_line_records(isb, dict, &docid_seq, inf, memory);
574
575     yaz_log(YLOG_LOG, "Total " ZINT_FORMAT " documents", docid_seq);
576     dict_insert(dict, "_s", sizeof(docid_seq), &docid_seq);
577
578     dict_close(dict);
579     isamb_close(isb);
580
581     if (fname)
582         fclose(inf);
583     /* exit block system */
584     bfs_destroy(bfs);
585     zebra_timing_stop(tim);
586
587     yaz_log(YLOG_LOG, "Total %8.6f %5.2f %5.2f\n",
588             zebra_timing_get_real(tim),
589             zebra_timing_get_user(tim),
590             zebra_timing_get_sys(tim));
591     
592     zebra_timing_destroy(&tim);
593
594     exit(0);
595     return 0;
596 }
597 /*
598  * Local variables:
599  * c-basic-offset: 4
600  * indent-tabs-mode: nil
601  * End:
602  * vim: shiftwidth=4 tabstop=8 expandtab
603  */
604