Make MARC indexer with different ISAM strategy.
[idzebra-moved-to-github.git] / isamb / benchindex1.c
1 /* $Id: benchindex1.c,v 1.1 2006-12-10 21:00:56 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 #if HAVE_SYS_TIMES_H
25 #include <sys/times.h>
26 #endif
27 #if HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #endif
30
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <yaz/log.h>
35 #include <yaz/nmem.h>
36 #include <yaz/xmalloc.h>
37 #include <yaz/marcdisp.h>
38 #include <it_key.h>
39 #include <idzebra/isamb.h>
40 #include <idzebra/dict.h>
41 #include <assert.h>
42
43 struct index_block {
44     NMEM nmem;
45     size_t no_entries;
46     size_t current_entry;
47     size_t current_max;
48     struct index_term *terms;
49     struct index_term **ar;
50     int round;
51 };
52
53 struct index_term {
54     const char *term;
55     zint docid;
56     zint seqno;
57     int word_id;
58     struct index_term *next;
59 };
60
61 struct index_block *index_block_new(int memory)
62 {
63     struct index_block *b = xmalloc(sizeof(*b));
64     b->no_entries = 0;
65     b->current_max = memory * 1024 * 1024;
66     b->terms = 0;
67     b->nmem = nmem_create();
68     b->round = 0;
69     return b;
70 }
71
72 void index_block_destroy(struct index_block **bp)
73 {
74     if (*bp)
75     {
76         nmem_destroy((*bp)->nmem);
77         xfree(*bp);
78         *bp = 0;
79     }
80 }
81
82 static int cmp_ar(const void *p1, const void *p2)
83 {
84     struct index_term *t1 = *(struct index_term **) p1;
85     struct index_term *t2 = *(struct index_term **) p2;
86     int d = strcmp(t1->term, t2->term);
87     if (d)
88         return d;
89
90     if (t1->docid > t2->docid)
91         return 1;
92     else if (t1->docid < t2->docid)
93         return -1;
94     if (t1->seqno > t2->seqno)
95         return 1;
96     else if (t1->seqno < t2->seqno)
97         return -1;
98     return 0;
99 }
100
101
102 int code_read(void *vp, char **dst, int *insertMode)
103 {
104     struct index_block *b = (struct index_block *)vp;
105     struct index_term *t;
106     struct it_key key;
107
108     if (b->current_entry >= b->no_entries)
109         return 0;
110     
111     t = b->ar[b->current_entry];
112     b->current_entry++;
113     
114     key.len = 3;
115     key.mem[0] = t->word_id;
116     key.mem[1] = t->docid;
117     key.mem[2] = t->seqno;
118     key.mem[3] = 0;
119
120     memcpy(*dst, &key, sizeof(key));
121
122     (*dst) += sizeof(key);
123     *insertMode = 1;
124 #if 0
125     yaz_log(YLOG_LOG, "returning " ZINT_FORMAT " " ZINT_FORMAT "\n",
126             key.mem[0], key.mem[1]);
127 #endif
128     return 1;
129 }
130
131 void index_block_flush(struct index_block *b, ISAMB isb, Dict dict,
132                        int no_docs)
133 {
134     struct index_term *t = b->terms;
135     int i;
136     int word_id_seq = 0;
137     int no_words = 0, no_new_words = 0;
138     const char *dict_info = 0;
139     ISAM_P isamc_p = 0;
140
141 #if HAVE_SYS_TIMES_H
142 #if HAVE_SYS_TIME_H
143         struct tms tms1, tms2;
144         struct timeval start_time, end_time;
145         double usec;
146         times(&tms1);
147         gettimeofday(&start_time, 0);
148 #endif
149 #endif
150     
151     b->ar = xmalloc(sizeof(*b->ar) * b->no_entries);
152     for (i = 0; i < b->no_entries; i++, t = t->next)
153     {
154         assert(t);
155         b->ar[i] = t;
156     }
157     assert(!t);
158     
159     qsort(b->ar, b->no_entries, sizeof(*b->ar), cmp_ar);
160 #if 0
161     for (i = 0; i < b->no_entries; i++)
162     {
163         printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n",
164                ar[i]->term, ar[i]->docid, ar[i]->seqno);
165     }
166 #endif
167     dict_info = dict_lookup(dict, "_w");
168     if (dict_info)
169     {
170         assert(*dict_info == sizeof(word_id_seq));
171         memcpy(&word_id_seq, dict_info+1, sizeof(word_id_seq));
172     }
173
174     dict_info = dict_lookup(dict, "_i");
175     if (dict_info)
176     {
177         assert(*dict_info == sizeof(isamc_p));
178         memcpy(&isamc_p, dict_info+1, sizeof(isamc_p));
179     }
180
181     for (i = 0; i < b->no_entries; i++)
182     {
183         if (i > 0 && strcmp(b->ar[i-1]->term, b->ar[i]->term) == 0)
184             b->ar[i]->word_id = b->ar[i-1]->word_id;
185         else
186         {
187             const char *dict_info = dict_lookup(dict, b->ar[i]->term);
188             if (dict_info)
189             {
190                 memcpy(&b->ar[i]->word_id, dict_info+1, sizeof(int));
191             }
192             else
193             {
194                 word_id_seq++;
195                 no_new_words++;
196                 dict_insert(dict, b->ar[i]->term, sizeof(int), &word_id_seq);
197                 b->ar[i]->word_id = word_id_seq;
198             }
199             no_words++;
200         }
201     }
202     dict_insert(dict, "_w", sizeof(word_id_seq), &word_id_seq);
203
204     b->current_entry = 0;
205
206     if (b->no_entries)
207     {
208         ISAMC_I isamc_i;
209         
210         isamc_i.clientData = b;
211         isamc_i.read_item = code_read;
212
213         isamb_merge (isb, &isamc_p, &isamc_i);
214
215         assert(isamc_p);
216         dict_insert(dict, "_i", sizeof(isamc_p), &isamc_p);
217     }
218
219     yaz_log(YLOG_LOG, "Flushed %d postings, %d/%d words, %d records",
220             b->no_entries, no_words, no_new_words, no_docs);
221     xfree(b->ar);
222     b->ar = 0;
223     nmem_reset(b->nmem);
224     b->no_entries = 0;
225     b->terms = 0;
226
227 #if HAVE_SYS_TIMES_H
228 #if HAVE_SYS_TIME_H      
229     b->round++;
230     gettimeofday(&end_time, 0);
231     times(&tms2);
232     
233     usec = (end_time.tv_sec - start_time.tv_sec) * 1000000.0 +
234         end_time.tv_usec - start_time.tv_usec;
235     
236     printf("%3d %8.6f %5.2f %5.2f\n",
237            b->round,
238            usec / 1000000,
239            (double) (tms2.tms_utime - tms1.tms_utime)/100,
240            (double) (tms2.tms_stime - tms1.tms_stime)/100);
241 #endif
242 #endif
243
244 }
245
246 void index_block_check_flush(struct index_block *b, ISAMB isb, Dict dict,
247                              int no_docs)
248 {
249     int total = nmem_total(b->nmem);
250     int max = b->current_max;
251     if (total > max)
252     {
253         yaz_log(YLOG_LOG, "flush to disk total=%d max=%d", total, max);
254         index_block_flush(b, isb, dict, no_docs);
255     }
256 }
257
258 void index_block_add(struct index_block *b,
259                      const char *term, zint docid, zint seqno)
260 {
261     struct index_term *t = nmem_malloc(b->nmem, sizeof(*t));
262     t->term = nmem_strdup(b->nmem, term);
263     t->docid = docid;
264     t->seqno = seqno;
265     t->next = b->terms;
266     b->terms = t;
267     b->no_entries++;
268 }
269
270 void exit_usage(void)
271 {
272     fprintf(stderr, "benchindex1 [-z sz]\n");
273     exit(1);
274 }
275
276 void index_term(struct index_block *b, const char *term,
277                 zint docid, zint *seqno)
278 {
279 #if 0
280     printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n", term,
281            docid, *seqno);
282 #endif
283     index_block_add(b, term, docid, *seqno);
284     (*seqno)++;
285 }
286
287 void index_wrbuf(struct index_block *b, WRBUF wrbuf, zint docid)
288 {
289     int nl = 1;
290     const char *cp = wrbuf_buf(wrbuf);
291     char term[4096];
292     size_t sz = 0;
293     zint seqno = 0;
294
295     while (*cp)
296     {
297         if (nl)
298         {
299             int i;
300             for (i = 0; i<6 && *cp; i++, cp++)
301                 ;
302         }
303         nl = 0;
304         if (*cp == '\n')
305         {
306             if (sz)
307             {
308                 index_term(b, term, docid, &seqno);
309                 sz = 0;
310             }
311             nl = 1;
312             cp++;
313         }
314         else if (*cp == '$' && cp[1])
315         {
316             if (sz)
317             {
318                 index_term(b, term, docid, &seqno);
319                 sz = 0;
320             }
321             cp += 2;
322         }
323         else if (strchr("$/-;,.:[]\"&(){} ", *cp))
324         {
325             if (sz)
326             {
327                 index_term(b, term, docid, &seqno);
328                 sz = 0;
329             }
330             cp++;
331         }
332         else
333         {
334             unsigned ch = *(const unsigned char *)cp;
335             if (sz < sizeof(term))
336             {
337                 term[sz] = tolower(ch);
338                 term[sz+1] = '\0';
339                 sz++;
340             }
341             cp++;
342         }            
343     }
344     if (sz)
345         index_term(b, term, docid, &seqno);
346 }
347
348 void index_marc_from_file(ISAMB isb,
349                           Dict dict,
350                           FILE *inf,
351                           int memory,
352                           int verbose, int print_offset)
353 {
354     yaz_marc_t mt = yaz_marc_create();
355     WRBUF wrbuf = wrbuf_alloc();
356     struct index_block *b = index_block_new(memory);
357     const char *dict_info = 0;
358     zint docid_seq = 1;
359     int no_docs = 0;
360
361     dict_info = dict_lookup(dict, "_s");
362     if (dict_info)
363     {
364         assert(*dict_info == sizeof(docid_seq));
365         memcpy(&docid_seq, dict_info+1, sizeof(docid_seq));
366     }
367
368     while (1)
369     {
370         size_t r;
371         char buf[100001];
372         int len, rlen;
373
374         r = fread (buf, 1, 5, inf);
375         if (r < 5)
376         {
377             if (r && print_offset && verbose)
378                 printf ("<!-- Extra %ld bytes at end of file -->\n",
379                         (long) r);
380             break;
381         }
382         while (*buf < '0' || *buf > '9')
383         {
384             int i;
385             long off = ftell(inf) - 5;
386             if (verbose || print_offset)
387                 printf("<!-- Skipping bad byte %d (0x%02X) at offset "
388                        "%ld (0x%lx) -->\n", 
389                        *buf & 0xff, *buf & 0xff,
390                        off, off);
391             for (i = 0; i<4; i++)
392                 buf[i] = buf[i+1];
393             r = fread(buf+4, 1, 1, inf);
394             if (r < 1)
395                 break;
396         }
397         if (r < 1)
398         {
399             if (verbose || print_offset)
400                 printf ("<!-- End of file with data -->\n");
401             break;
402         }
403         len = atoi_n(buf, 5);
404         if (len < 25 || len > 100000)
405         {
406             long off = ftell(inf) - 5;
407             printf("Bad Length %ld read at offset %ld (%lx)\n",
408                    (long)len, (long) off, (long) off);
409             break;
410         }
411         rlen = len - 5;
412         r = fread (buf + 5, 1, rlen, inf);
413         if (r < rlen)
414             break;
415         yaz_marc_read_iso2709(mt, buf, len);
416         
417         if (yaz_marc_write_line(mt, wrbuf))
418             break;
419
420         index_wrbuf(b, wrbuf, docid_seq);
421         wrbuf_rewind(wrbuf);
422         docid_seq++;
423
424         no_docs++;
425         index_block_check_flush(b, isb, dict, no_docs);
426     }
427     index_block_flush(b, isb, dict, no_docs);
428     wrbuf_free(wrbuf, 1);
429     yaz_marc_destroy(mt);
430     index_block_destroy(&b);
431     yaz_log(YLOG_LOG, "Total " ZINT_FORMAT " documents", docid_seq);
432     dict_insert(dict, "_s", sizeof(docid_seq), &docid_seq);
433 }
434
435 int main(int argc, char **argv)
436 {
437     BFiles bfs;
438     ISAMB isb;
439     ISAMC_M method;
440     Dict dict;
441     int ret;
442     int reset = 0;
443     char *arg;
444     int memory = 5;
445     const char *fname = 0;
446     FILE *inf = stdin;
447
448     while ((ret = options("im:", argv, argc, &arg)) != -2)
449     {
450         switch(ret)
451         {
452         case 'm':
453             memory = atoi(arg);
454             break;
455         case 'i':
456             reset = 1;
457             break;
458         case 0:
459             fname = arg;
460             break;
461         default:
462             fprintf(stderr, "bad option.\n");
463             exit_usage();
464         }
465     }
466         
467     if (fname)
468     {
469         inf = fopen(fname, "rb");
470         if (!inf)
471         {
472             fprintf(stderr, "Cannot open %s\n", fname);
473             exit(1);
474         }
475     }
476     /* setup method (attributes) */
477     method.compare_item = key_compare;
478     method.log_item = key_logdump_txt;
479
480     method.codec.start = iscz1_start;
481     method.codec.decode = iscz1_decode;
482     method.codec.encode = iscz1_encode;
483     method.codec.stop = iscz1_stop;
484     method.codec.reset = iscz1_reset;
485
486     method.debug = 0;
487
488     /* create block system */
489     bfs = bfs_create(0, 0);
490     if (!bfs)
491     {
492         yaz_log(YLOG_WARN, "bfs_create failed");
493         exit(1);
494     }
495
496     if (reset)
497         bf_reset(bfs);
498
499     /* create isam handle */
500     isb = isamb_open (bfs, "isamb", 1, &method, 0);
501     if (!isb)
502     {
503         yaz_log(YLOG_WARN, "isamb_open failed");
504         exit(2);
505     }
506     dict = dict_open(bfs, "dict", 50, 1, 0, 4096);
507
508     index_marc_from_file(isb, dict, inf, memory, 
509                          0 /* verbose */ , 0 /* print_offset */);
510
511     dict_close(dict);
512     isamb_close(isb);
513
514     if (fname)
515         fclose(inf);
516     /* exit block system */
517     bfs_destroy(bfs);
518     exit(0);
519     return 0;
520 }
521 /*
522  * Local variables:
523  * c-basic-offset: 4
524  * indent-tabs-mode: nil
525  * End:
526  * vim: shiftwidth=4 tabstop=8 expandtab
527  */
528