More digits in output for total docs
[idzebra-moved-to-github.git] / isamb / benchindex1.c
1 /* $Id: benchindex1.c,v 1.7 2006-12-12 17:33:35 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_dict = 0;
134     zebra_timing_t tim_isamb = 0;
135     zint number_of_int_splits = isamb_get_int_splits(isb);
136     zint number_of_leaf_splits = isamb_get_leaf_splits(isb);
137     zint number_of_dict_splits = dict_get_no_split(dict);
138     
139     b->ar = xmalloc(sizeof(*b->ar) * b->no_entries);
140     for (i = 0; i < b->no_entries; i++, t = t->next)
141     {
142         assert(t);
143         b->ar[i] = t;
144     }
145     assert(!t);
146     
147     qsort(b->ar, b->no_entries, sizeof(*b->ar), cmp_ar);
148     tim_dict = zebra_timing_create();
149 #if 0
150     for (i = 0; i < b->no_entries; i++)
151     {
152         printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n",
153                ar[i]->term, ar[i]->docid, ar[i]->seqno);
154     }
155 #endif
156     dict_info = dict_lookup(dict, "_w");
157     if (dict_info)
158     {
159         assert(*dict_info == sizeof(word_id_seq));
160         memcpy(&word_id_seq, dict_info+1, sizeof(word_id_seq));
161     }
162
163     dict_info = dict_lookup(dict, "_i");
164     if (dict_info)
165     {
166         assert(*dict_info == sizeof(isamc_p));
167         memcpy(&isamc_p, dict_info+1, sizeof(isamc_p));
168     }
169
170     for (i = 0; i < b->no_entries; i++)
171     {
172         if (i > 0 && strcmp(b->ar[i-1]->term, b->ar[i]->term) == 0)
173             b->ar[i]->word_id = b->ar[i-1]->word_id;
174         else
175         {
176             const char *dict_info = dict_lookup(dict, b->ar[i]->term);
177             if (dict_info)
178             {
179                 memcpy(&b->ar[i]->word_id, dict_info+1, sizeof(int));
180             }
181             else
182             {
183                 word_id_seq++;
184                 no_new_words++;
185                 dict_insert(dict, b->ar[i]->term, sizeof(int), &word_id_seq);
186                 b->ar[i]->word_id = word_id_seq;
187             }
188             no_words++;
189         }
190     }
191     dict_insert(dict, "_w", sizeof(word_id_seq), &word_id_seq);
192     
193     zebra_timing_stop(tim_dict);
194     tim_isamb = zebra_timing_create();
195
196     b->current_entry = 0;
197
198     if (b->no_entries)
199     {
200         ISAMC_I isamc_i;
201         
202         isamc_i.clientData = b;
203         isamc_i.read_item = code_read;
204
205         isamb_merge (isb, &isamc_p, &isamc_i);
206
207         assert(isamc_p);
208         dict_insert(dict, "_i", sizeof(isamc_p), &isamc_p);
209     }
210
211     zebra_timing_stop(tim_isamb);
212
213     number_of_int_splits = isamb_get_int_splits(isb) - number_of_int_splits;
214     number_of_leaf_splits = isamb_get_leaf_splits(isb) - number_of_leaf_splits;
215     number_of_dict_splits = dict_get_no_split(dict) - number_of_dict_splits;
216
217     if (b->round == 0)
218     {
219         printf("# run     total dict-real  user   sys isam-real  user   sys "
220                " intsp leafsp     docs postings  words    new d-spl\n");
221     }
222     b->round++;
223     printf("%5d %9.6f %9.6f %5.2f %5.2f %9.6f %5.2f %5.2f "
224            "%6" ZINT_FORMAT0 " %6" ZINT_FORMAT0 
225            " %8d %8d %6d %6d" " %5" ZINT_FORMAT0 "\n",
226            b->round,
227            zebra_timing_get_real(tim_dict) + zebra_timing_get_real(tim_isamb),
228            zebra_timing_get_real(tim_dict),
229            zebra_timing_get_user(tim_dict),
230            zebra_timing_get_sys(tim_dict),
231            zebra_timing_get_real(tim_isamb),
232            zebra_timing_get_user(tim_isamb),
233            zebra_timing_get_sys(tim_isamb),
234            number_of_int_splits,
235            number_of_leaf_splits,
236            no_docs,
237            b->no_entries,
238            no_words,
239            no_new_words,
240            number_of_dict_splits
241         );
242     fflush(stdout);
243
244     xfree(b->ar);
245     b->ar = 0;
246     nmem_reset(b->nmem);
247     b->no_entries = 0;
248     b->terms = 0;
249
250     zebra_timing_destroy(&tim_isamb);
251     zebra_timing_destroy(&tim_dict);
252 }
253
254 void index_block_check_flush(struct index_block *b, ISAMB isb, Dict dict,
255                              int no_docs)
256 {
257     int total = nmem_total(b->nmem);
258     int max = b->current_max;
259     if (total > max)
260     {
261         index_block_flush(b, isb, dict, no_docs);
262     }
263 }
264
265 void index_block_add(struct index_block *b,
266                      const char *term, zint docid, zint seqno)
267 {
268     struct index_term *t = nmem_malloc(b->nmem, sizeof(*t));
269     t->term = nmem_strdup(b->nmem, term);
270     t->docid = docid;
271     t->seqno = seqno;
272     t->next = b->terms;
273     b->terms = t;
274     b->no_entries++;
275 }
276
277 void index_term(struct index_block *b, const char *term,
278                 zint docid, zint *seqno)
279 {
280 #if 0
281     printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n", term,
282            docid, *seqno);
283 #endif
284     index_block_add(b, term, docid, *seqno);
285     (*seqno)++;
286 }
287
288 void index_wrbuf(struct index_block *b, WRBUF wrbuf, zint docid,
289                  int subfield_char)
290 {
291     int nl = 1;
292     const char *cp = wrbuf_buf(wrbuf);
293     char term[4096];
294     size_t sz = 0;
295     zint seqno = 0;
296
297     while (*cp)
298     {
299         if (nl)
300         {
301             int i;
302             if (cp[0] != ' ')
303             {   /* skip field+indicator (e.g. 245 00) */
304                 for (i = 0; i<6 && *cp; i++, cp++)
305                     ;
306             }
307             else
308             {  /* continuation line */
309                 for (i = 0; i<4 && *cp; i++, cp++)
310                     ;
311             }   
312         }
313         nl = 0;
314         if (*cp == '\n')
315         {
316             if (sz)
317             {
318                 index_term(b, term, docid, &seqno);
319                 sz = 0;
320             }
321             nl = 1;
322             cp++;
323         }
324         else if (*cp == subfield_char && cp[1])
325         {
326             if (sz)
327             {
328                 index_term(b, term, docid, &seqno);
329                 sz = 0;
330             }
331             cp += 2;
332         }
333         else if (strchr("$*/-;,.:[]\"&(){} ", *cp))
334         {
335             if (sz)
336             {
337                 index_term(b, term, docid, &seqno);
338                 sz = 0;
339             }
340             cp++;
341         }
342         else
343         {
344             unsigned ch = *(const unsigned char *)cp;
345             if (sz < sizeof(term))
346             {
347                 term[sz] = tolower(ch);
348                 term[sz+1] = '\0';
349                 sz++;
350             }
351             cp++;
352         }            
353     }
354     if (sz)
355         index_term(b, term, docid, &seqno);
356 }
357
358 void index_marc_line_records(ISAMB isb,
359                              Dict dict,
360                              zint *docid_seq,
361                              FILE *inf,
362                              int memory)
363 {
364     WRBUF wrbuf = wrbuf_alloc();
365     int no_docs = 0;
366     int new_rec = 1;
367     char line[4096];
368     struct index_block *b = index_block_new(memory);
369     while(fgets(line, sizeof(line)-1, inf))
370     {
371         if (line[0] == '$')
372         {
373             if (!new_rec)
374                 new_rec = 1;
375             else
376                 new_rec = 0;
377             continue;
378         }
379         if (new_rec)
380         {
381             (*docid_seq)++;
382             no_docs++;
383             index_block_check_flush(b, isb, dict, no_docs);
384             new_rec = 0;
385         }
386
387         if (line[0] == ' ')
388         {
389             /* continuation */
390             wrbuf_puts(wrbuf, line);
391             continue;
392         }
393         else
394         {
395             /* index existing buffer (if any) */
396             if (wrbuf_len(wrbuf))
397             {
398                 index_wrbuf(b, wrbuf, *docid_seq, '*');
399                 wrbuf_rewind(wrbuf);
400             }
401             if (line[0] != ' ' && line[1] != ' ' && line[2] != ' ' &&
402                 line[3] == ' ')
403             {
404                 /* normal field+indicator line */
405                 wrbuf_puts(wrbuf, line);
406             }
407         }
408     }
409     if (wrbuf_len(wrbuf))
410     {
411         index_wrbuf(b, wrbuf, *docid_seq, '*');
412         wrbuf_rewind(wrbuf);
413     }
414     (*docid_seq)++;
415     no_docs++;
416     index_block_flush(b, isb, dict, no_docs);
417     index_block_destroy(&b);
418 }
419
420 void index_marc_from_file(ISAMB isb,
421                           Dict dict,
422                           zint *docid_seq,
423                           FILE *inf,
424                           int memory,
425                           int verbose, int print_offset)
426 {
427     yaz_marc_t mt = yaz_marc_create();
428     WRBUF wrbuf = wrbuf_alloc();
429     struct index_block *b = index_block_new(memory);
430     int no_docs = 0;
431
432     while (1)
433     {
434         size_t r;
435         char buf[100001];
436         int len, rlen;
437
438         r = fread (buf, 1, 5, inf);
439         if (r < 5)
440         {
441             if (r && print_offset && verbose)
442                 printf ("<!-- Extra %ld bytes at end of file -->\n",
443                         (long) r);
444             break;
445         }
446         while (*buf < '0' || *buf > '9')
447         {
448             int i;
449             long off = ftell(inf) - 5;
450             if (verbose || print_offset)
451                 printf("<!-- Skipping bad byte %d (0x%02X) at offset "
452                        "%ld (0x%lx) -->\n", 
453                        *buf & 0xff, *buf & 0xff,
454                        off, off);
455             for (i = 0; i<4; i++)
456                 buf[i] = buf[i+1];
457             r = fread(buf+4, 1, 1, inf);
458             if (r < 1)
459                 break;
460         }
461         if (r < 1)
462         {
463             if (verbose || print_offset)
464                 printf ("<!-- End of file with data -->\n");
465             break;
466         }
467         len = atoi_n(buf, 5);
468         if (len < 25 || len > 100000)
469         {
470             long off = ftell(inf) - 5;
471             printf("Bad Length %ld read at offset %ld (%lx)\n",
472                    (long)len, (long) off, (long) off);
473             break;
474         }
475         rlen = len - 5;
476         r = fread (buf + 5, 1, rlen, inf);
477         if (r < rlen)
478             break;
479         yaz_marc_read_iso2709(mt, buf, len);
480         
481         if (yaz_marc_write_line(mt, wrbuf))
482             break;
483
484         index_wrbuf(b, wrbuf, *docid_seq, '$');
485         wrbuf_rewind(wrbuf);
486         (*docid_seq)++;
487
488         no_docs++;
489         index_block_check_flush(b, isb, dict, no_docs);
490     }
491     index_block_flush(b, isb, dict, no_docs);
492     wrbuf_free(wrbuf, 1);
493     yaz_marc_destroy(mt);
494     index_block_destroy(&b);
495 }
496
497 void exit_usage(void)
498 {
499     fprintf(stderr, "benchindex1 [-t type] [-c d:i] [-m mem] [-i] [inputfile]\n");
500     exit(1);
501 }
502
503 int main(int argc, char **argv)
504 {
505     BFiles bfs;
506     ISAMB isb_postings;
507     ISAMC_M method_postings;
508     Dict dict;
509     int ret;
510     int reset = 0;
511     char *arg;
512     int memory = 5;
513     int isam_cache_size = 40;
514     int dict_cache_size = 50;
515     const char *fname = 0;
516     FILE *inf = stdin;
517     zebra_timing_t tim = 0;
518     zint docid_seq = 1;
519     const char *dict_info;
520     const char *type = "iso2709";
521     int int_count_enable = 1;
522
523     while ((ret = options("im:t:c:N", argv, argc, &arg)) != -2)
524     {
525         switch(ret)
526         {
527         case 'm':
528             memory = atoi(arg);
529             break;
530         case 'i':
531             reset = 1;
532             break;
533         case 't':
534             if (!strcmp(arg, "iso2709"))
535                 type = "iso2709";
536             else if (!strcmp(arg, "line"))
537                 type = "line";
538             else
539             {
540                 fprintf(stderr, "bad type: %s.\n", arg);
541                 exit_usage();
542             }
543             break;
544         case 'c':
545             if (sscanf(arg, "%d:%d", &dict_cache_size, &isam_cache_size) 
546                 != 2)
547             {
548                 fprintf(stderr, "bad cache sizes for -c\n");
549                 exit_usage();
550             }
551             break;
552         case 0:
553             fname = arg;
554             break;
555         case 'N':
556             int_count_enable = 0;
557             break;
558         default:
559             fprintf(stderr, "bad option.\n");
560             exit_usage();
561         }
562     }
563         
564     if (fname)
565     {
566         inf = fopen(fname, "rb");
567         if (!inf)
568         {
569             fprintf(stderr, "Cannot open %s\n", fname);
570             exit(1);
571         }
572     }
573     printf("# benchindex1 %s %s\n", __DATE__, __TIME__);
574     printf("# isam_cache_size = %d\n", isam_cache_size);
575     printf("# dict_cache_size = %d\n", dict_cache_size);
576     printf("# int_count_enable = %d\n", int_count_enable);
577     printf("# memory = %d\n", memory);
578
579     /* setup postings isamb attributes */
580     method_postings.compare_item = key_compare;
581     method_postings.log_item = key_logdump_txt;
582
583     method_postings.codec.start = iscz1_start;
584     method_postings.codec.decode = iscz1_decode;
585     method_postings.codec.encode = iscz1_encode;
586     method_postings.codec.stop = iscz1_stop;
587     method_postings.codec.reset = iscz1_reset;
588
589     method_postings.debug = 0;
590
591     /* create block system */
592     bfs = bfs_create(0, 0);
593     if (!bfs)
594     {
595         yaz_log(YLOG_WARN, "bfs_create failed");
596         exit(1);
597     }
598
599     if (reset)
600         bf_reset(bfs);
601
602     tim = zebra_timing_create();
603     /* create isam handle */
604     isb_postings = isamb_open (bfs, "isamb", isam_cache_size ? 1 : 0,
605                                &method_postings, 0);
606     if (!isb_postings)
607     {
608         yaz_log(YLOG_WARN, "isamb_open failed");
609         exit(2);
610     }
611     isamb_set_cache_size(isb_postings, isam_cache_size);
612     isamb_set_int_count(isb_postings, int_count_enable);
613     dict = dict_open(bfs, "dict", dict_cache_size, 1, 0, 4096);
614
615     dict_info = dict_lookup(dict, "_s");
616     if (dict_info)
617     {
618         assert(*dict_info == sizeof(docid_seq));
619         memcpy(&docid_seq, dict_info+1, sizeof(docid_seq));
620     }
621
622     if (!strcmp(type, "iso2709"))
623         index_marc_from_file(isb_postings, dict, &docid_seq, inf, memory, 
624                              0 /* verbose */ , 0 /* print_offset */);
625     else if (!strcmp(type, "line"))
626         index_marc_line_records(isb_postings, dict, &docid_seq, inf, memory);
627
628     printf("# Total " ZINT_FORMAT " documents\n", docid_seq);
629     dict_insert(dict, "_s", sizeof(docid_seq), &docid_seq);
630
631     dict_close(dict);
632     isamb_close(isb_postings);
633
634     if (fname)
635         fclose(inf);
636     /* exit block system */
637     bfs_destroy(bfs);
638     zebra_timing_stop(tim);
639
640     printf("# Total timings real=%8.6f user=%3.2f system=%3.2f\n",
641             zebra_timing_get_real(tim),
642             zebra_timing_get_user(tim),
643             zebra_timing_get_sys(tim));
644     
645     zebra_timing_destroy(&tim);
646
647     exit(0);
648     return 0;
649 }
650 /*
651  * Local variables:
652  * c-basic-offset: 4
653  * indent-tabs-mode: nil
654  * End:
655  * vim: shiftwidth=4 tabstop=8 expandtab
656  */
657