44dec1bcec93d6cd65e16438178e77126c04c690
[idzebra-moved-to-github.git] / index / livcode.c
1 /*
2   
3 The University of Liverpool
4
5 Modifications to Zebra 1.1 / YAZ 1.7 to enable ranking
6 by attribute weight.
7
8 Copyright (c) 2001-2002 The University of Liverpool.  All
9 rights reserved.
10
11 Licensed under the Academic Free License version 1.1.
12 http://opensource.org/licenses/academic.php
13
14 $Id: livcode.c,v 1.1 2003-03-26 16:41:48 adam Exp $
15
16 */
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #ifdef WIN32
21 #include <process.h>
22 #else
23 #include <unistd.h>
24 #endif
25 #include <assert.h>
26
27 #include "index.h"
28 #include "zserver.h"
29
30 /*
31 ** These functions/routines 
32 ** 1. reads in and builds a linked list of rank attr/rank score pairs
33 ** 2. expand a simple query into a paired list of complex/simple nodes.
34 */
35
36 typedef struct rstype
37 {
38     struct rstype *next_rsnode ;
39     int rank ;
40     int score ;
41     char *rankstr ;
42 } rsnode, *refrsnode ;
43
44 refrsnode start_rsnode = NULL ;
45
46 /*
47 ** Function/Routine prototypes
48 */
49 static int        search_for_score( char *rankstr ) ;
50 static char      *search_for_rankstr( int rank ) ;
51 static int        search_for_rank( int rank ) ;
52 static refrsnode  set_rsnode( int rank, int score ) ;
53 static int        read_zrank_file(ZebraHandle zh) ;
54
55 static void       convert_simple2complex(ZebraHandle zh, Z_RPNStructure *rpnstruct ) ;
56 static void       walk_complex_query(ZebraHandle zh, Z_RPNStructure *rpnstruct ) ;
57 static Z_Complex *expand_query(ZebraHandle zh, Z_Operand *thisop ) ;
58 static Z_Complex *set_1complex_1operand( Z_Complex *comp,Z_Operand *simp ) ;
59 static Z_Complex *set_2operands( Z_Operand *sim1,Z_Operand *sim2 ) ;
60 static Z_Operand *set_operand( Z_Operand *thisop, int newattr ) ;
61 static int        check_operand_attrs( Z_Operand *thisop ) ;
62
63 /*
64 ** search_for_score()
65 ** given a rank-string traverse down the linked list ;
66 ** return its score if found otherwise return -1.
67 */
68 int search_for_score( char *rankstr )
69 {
70     refrsnode node = start_rsnode ;
71     int rank ;
72   
73     if ( sscanf( rankstr,"%d",&rank ) )
74     {
75         while ( node )
76         {
77             if ( node->rank == rank ) return node->score ;
78             node = node->next_rsnode ;
79         }
80     }
81     return -1 ;
82 }
83
84 /*
85 ** search_for_rankstr()
86 ** given a rank traverse down the linked list ; 
87 ** return its string if found otherwise return NULL.
88 */
89 char *search_for_rankstr( int rank )
90 {
91     refrsnode node = start_rsnode ;
92   
93     while ( node )
94     {
95         if ( node->rank == rank ) return node->rankstr ;
96         node = node->next_rsnode ;
97     }
98     return "rank" ;
99 }
100
101 /*
102 ** search_for_rank()
103 ** given a rank traverse down the linked list ;
104 ** return 1 if found otherwise return 0.
105 */
106 int search_for_rank( int rank )
107 {
108     refrsnode node = start_rsnode ;
109
110     while ( node )
111     {
112         if ( node->rank == rank ) return 1 ;
113         node = node->next_rsnode ;
114     }
115     return 0 ;
116 }
117
118 /*
119 ** set_rsnode()
120 ** given a rank and a score, build the rest of the rsnode structure.
121 */
122 refrsnode set_rsnode( int rank, int score )
123 {
124 #define BUFFMAX 128
125     refrsnode node = (refrsnode)malloc( sizeof(rsnode) ) ;
126     char buff[BUFFMAX] ;
127
128     node->next_rsnode = NULL  ;
129     node->rank        = rank  ;
130     node->score       = score ;
131
132     sprintf( buff,"%d",rank ) ;
133     node->rankstr     = (char *)malloc( strlen(buff)+1 ) ;
134     strcpy( node->rankstr, buff ) ;
135
136     return node ;
137 }
138
139 /*
140 ** read_zrank_file(zh)
141 ** read in the rankfile and build the rank/score linked list ; 
142 ** return 0 : can't open the zebra config. file
143 ** return 0 : can't find the rankfile entry in the zebra config. file
144 ** return 0 : can't open the rankfile itself
145 ** return the number of distinct ranks read in.
146 */
147 int read_zrank_file(ZebraHandle zh)
148 {
149 #define LINEMAX 256
150     char line[ LINEMAX ] ;
151     char rname[ LINEMAX ] ;
152     char *lineptr ;
153     FILE *ifd ;
154     int rank = 0 ;
155     int score = 0 ;
156     int numranks = 0 ;
157  
158     /*
159     ** open the zebra configuration file and look for the "rankfile:"
160     ** entry which contains the path/name of the rankfile
161     */
162     
163     const char *rankfile = res_get_def(zh->res, "rankfile", 0);
164     const char *profilePath = res_get_def(zh->res, "profilePath",
165                                           DEFAULT_PROFILE_PATH);
166
167     if (!rankfile)
168     {
169         yaz_log(LOG_LOG, "rankfile entry not found in config file" ) ;
170         return 0 ;
171     }
172     ifd = yaz_path_fopen(profilePath, rankfile, "r" ) ;
173     if ( ifd )
174     {
175         while ( (lineptr = fgets( line,LINEMAX,ifd )) )
176         {
177             if ( sscanf( lineptr,"rankfile: %s", rname ) == 1 )
178                 rankfile  = rname ;
179         }
180
181         /*
182         ** open the rankfile and read the rank/score pairs
183         ** ignore 1016 
184         ** ignore duplicate ranks 
185         ** ignore ranks without +ve scores
186         */
187         if ( rankfile )
188         { 
189             if ( !(ifd = fopen( rankfile, "r" )) )
190             {
191                 logf( LOG_LOG, "unable to open rankfile %s",rankfile ) ;
192                 return 0;
193             }
194      
195             while ( (lineptr = fgets( line,LINEMAX,ifd )) )
196             {
197                 sscanf( lineptr,"%d : %d", &rank,&score ) ;
198                 if ( ( score > 0 ) && ( rank != 1016 ) )
199                 {
200                     refrsnode new_rsnode ;
201     
202                     if ( search_for_rank( rank ) == 0 )
203                     {
204                         new_rsnode              = set_rsnode( rank,score ) ;
205                         new_rsnode->next_rsnode = start_rsnode ;
206                         start_rsnode            = new_rsnode ;
207                         numranks++ ;
208                     }
209                 }
210             }
211         }
212         else 
213         {
214             yaz_log(LOG_WARN|LOG_ERRNO, "unable to open config file (%s)",
215                     rankfile);
216         }
217     }
218     return numranks ;
219 }
220
221 /*
222 ** set_operand() 
223 ** build an operand "node" - hav to make a complete copy of thisop and 
224 ** then insert newattr in the appropriate place
225 ** 
226 */
227 Z_Operand *set_operand( Z_Operand *thisop, int newattr )
228 {
229     Z_Operand            *operand ;
230     Z_AttributesPlusTerm *attributesplusterm ;
231     Z_AttributeList      *attributelist ;
232     Z_AttributeElement   *attributeelement ;
233     Z_AttributeElement   *attrptr ;
234     Z_AttributeElement   **attrptrptr ;
235     Z_Term               *term ;
236     Odr_oct              *general ;
237     int i ;
238
239     operand            = (Z_Operand *) 
240                          malloc( sizeof( Z_Operand ) ) ;
241     attributesplusterm = (Z_AttributesPlusTerm *)
242                          malloc( sizeof( Z_AttributesPlusTerm ) ) ;
243     attributelist      = (Z_AttributeList *)
244                          malloc( sizeof( Z_AttributeList ) ) ;
245     attributeelement   = (Z_AttributeElement *)
246                          malloc( sizeof( Z_AttributeElement ) ) ;
247     term               = (Z_Term *)
248                          malloc( sizeof( Z_Term ) ) ;
249     general            = (Odr_oct *)
250                          malloc( sizeof( Odr_oct ) ) ;
251
252     operand->which                 = Z_Operand_APT ;
253     operand->u.attributesPlusTerm  = attributesplusterm ;
254
255     attributesplusterm->attributes = attributelist ;
256     attributesplusterm->term       = term ;
257
258     attributelist->num_attributes  = thisop->u.attributesPlusTerm->
259                                      attributes->num_attributes ;
260
261     attrptr = (Z_AttributeElement *) malloc( sizeof(Z_AttributeElement) *
262                                              attributelist->num_attributes ) ;
263     attrptrptr = (Z_AttributeElement **) malloc( sizeof(Z_AttributeElement) *
264                                            attributelist->num_attributes ) ; 
265
266     attributelist->attributes = attrptrptr ;
267
268     for ( i = 0 ; i < attributelist->num_attributes ; i++ )
269     {
270         *attrptr = *thisop->u.attributesPlusTerm->attributes->attributes[i] ;
271
272          attrptr->attributeType  = (int *)malloc( sizeof(int *) ) ;
273         *attrptr->attributeType  = *thisop->u.attributesPlusTerm->attributes->
274                                     attributes[i]->attributeType;
275
276          attrptr->value.numeric  = (int *)malloc( sizeof(int *) ) ;
277         *attrptr->value.numeric  = *thisop->u.attributesPlusTerm->attributes->
278                                     attributes[i]->value.numeric;
279
280         if ( (*attrptr->attributeType == 1) && 
281              (*attrptr->value.numeric == 1016) )
282         {
283             *attrptr->value.numeric = newattr ;
284         }
285         *attrptrptr++ = attrptr++ ;
286     }
287
288     term->which     = Z_Term_general ;
289     term->u.general = general ;
290
291     general->len  = thisop->u.attributesPlusTerm->term->u.general->len ;
292     general->size = thisop->u.attributesPlusTerm->term->u.general->size ;
293     general->buf  = malloc( general->size ) ;
294     strcpy( general->buf,
295             thisop->u.attributesPlusTerm->term->u.general->buf ) ;
296
297     return operand ;
298 }
299
300 /*
301 ** set_2operands()
302 ** build a complex "node" with two (simple) operand "nodes" as branches
303 */
304 Z_Complex *set_2operands( Z_Operand *sim1,Z_Operand *sim2 )
305 {
306     Z_Complex *top        ;
307     Z_RPNStructure *s1    ;
308     Z_RPNStructure *s2    ;
309     Z_Operator *roperator ;
310
311     top       = (Z_Complex *)     malloc( sizeof( Z_Complex      ) ) ;
312     s1        = (Z_RPNStructure *)malloc( sizeof( Z_RPNStructure ) ) ;
313     s2        = (Z_RPNStructure *)malloc( sizeof( Z_RPNStructure ) ) ;
314     roperator = (Z_Operator *)    malloc( sizeof( Z_Operator     ) ) ;
315
316     top->roperator          = roperator     ;
317     top->roperator->which   = Z_Operator_or ;
318     top->roperator->u.op_or = odr_nullval() ;
319
320     top->s1                 = s1                    ;
321     top->s1->which          = Z_RPNStructure_simple ;
322     top->s1->u.simple       = sim1                  ;
323
324     top->s2                 = s2                    ;
325     top->s2->which          = Z_RPNStructure_simple ;
326     top->s2->u.simple       = sim2                  ;
327
328     return top ;
329 }
330
331 /*
332 ** set_1complex_1operand()
333 ** build a complex "node" with a complex "node" branch and an 
334 ** operand "node" branch
335 */
336 Z_Complex *set_1complex_1operand( Z_Complex *comp,Z_Operand *simp )
337 {
338     Z_Complex *top        ;
339     Z_RPNStructure *s1    ;
340     Z_RPNStructure *s2    ;
341     Z_Operator *roperator ;
342
343     top       = (Z_Complex *)     malloc( sizeof( Z_Complex      ) ) ;
344     s1        = (Z_RPNStructure *)malloc( sizeof( Z_RPNStructure ) ) ;
345     s2        = (Z_RPNStructure *)malloc( sizeof( Z_RPNStructure ) ) ;
346     roperator = (Z_Operator *)    malloc( sizeof( Z_Operator     ) ) ;
347
348     top->roperator          = roperator     ;
349     top->roperator->which   = Z_Operator_or ;
350     top->roperator->u.op_or = odr_nullval() ;
351
352     top->s1                 = s1                     ;
353     top->s1->which          = Z_RPNStructure_complex ;
354     top->s1->u.complex      = comp                   ;
355
356     top->s2                 = s2                     ;
357     top->s2->which          = Z_RPNStructure_simple  ;
358     top->s2->u.simple       = simp                   ;
359
360     return top ;
361 }
362
363 /*
364 ** expand_query()
365 ** expand a simple query into a number of complex queries
366 */
367 Z_Complex *expand_query(ZebraHandle zh, Z_Operand *thisop )
368 {
369     Z_Complex *top ;
370     int numattrs = 0 ;
371
372     /*
373     ** start_rsnode will be set if we have already read the rankfile 
374     ** so don't bother again but we need to know the number of attributes
375     ** in the linked list so traverse it again to find out how many.
376     */
377     if ( start_rsnode )
378     {
379         refrsnode node = start_rsnode ;
380         while ( node )
381         {
382             numattrs++ ;
383             node = node->next_rsnode ;
384         }
385     } 
386
387     /*
388     ** only expand the query if there are 2 or more attributes 
389     */
390     if ( numattrs >= 2 )
391     {
392         refrsnode node = start_rsnode ;
393         int attr1 ;
394         int attr2 ;
395
396         attr1 = node->rank ; node = node->next_rsnode ;
397         attr2 = node->rank ; node = node->next_rsnode ;
398
399         /*
400         ** this is the special case and has to be done first because the 
401         ** last complex node in the linear list has two simple nodes whereas 
402         ** all the others have a complex and a simple.
403         */
404         top = set_2operands( set_operand( thisop,attr1 ),
405                              set_operand( thisop,attr2 ) ) ;
406
407         /*
408         ** do the rest as complex/simple pairs
409         */
410         while ( node )
411         {
412             attr1 = node->rank ; node = node->next_rsnode ;
413             top = set_1complex_1operand( top,set_operand( thisop,attr1 ) ) ;
414         }
415         /*
416         ** finally add the 1016 rank attribute at the top of the tree
417         */
418         top = set_1complex_1operand( top,set_operand( thisop,1016 ) ) ;
419
420         return top ;
421     }
422     else return NULL ;
423 }
424
425 /*
426 ** check_operand_attrs()
427 ** loop through the attributes of a particular operand 
428 ** return 1 if (type==1 && value==1016) && (type==2 && value==102) 
429 ** otherwise return 0
430 */
431 int check_operand_attrs( Z_Operand *thisop ) 
432 {
433     Z_AttributeElement *attrptr ;
434     int cond1 = 0 ;
435     int cond2 = 0 ;
436     int numattrs ;
437     int i ;
438
439     numattrs = thisop->u.attributesPlusTerm->attributes->num_attributes ;
440
441     for ( i = 0 ; i < numattrs ; i++ )
442     {
443         attrptr = thisop->u.attributesPlusTerm->attributes->attributes[i] ;
444
445         if ( (*attrptr->attributeType == 1) && 
446              (*attrptr->value.numeric == 1016) )
447             cond1 = 1 ;
448
449         if ( (*attrptr->attributeType == 2) && 
450              (*attrptr->value.numeric == 102) )
451             cond2 = 1 ;
452     }
453
454     return (cond1 & cond2) ;
455 }
456
457 /*
458 ** convert_simple2complex()
459 ** 
460 */
461 void convert_simple2complex(ZebraHandle zh, Z_RPNStructure *rpnstruct )
462 {
463     Z_Complex *complex = NULL ;                                         
464     Z_Operand *operand = rpnstruct->u.simple ;                          
465
466     if ( check_operand_attrs( operand ) )
467     {
468         complex = expand_query(zh, operand ) ;
469
470         if ( complex )
471         {
472             /*
473             ** Everything is complete so replace the original
474             ** operand with the newly built complex structure
475             ** This is it ... no going back!!
476             */
477             rpnstruct->which     = Z_RPNStructure_complex ;
478             rpnstruct->u.complex = complex ;
479         }
480     }                                                                       
481 }
482
483 /*
484 ** walk_complex_query()
485 ** recursively traverse the tree expanding any simple queries we find
486 */
487 void walk_complex_query(ZebraHandle zh, Z_RPNStructure *rpnstruct )
488 {
489     if ( rpnstruct->which == Z_RPNStructure_simple )
490     {
491         convert_simple2complex(zh, rpnstruct ) ;
492     }
493     else
494     {
495         walk_complex_query(zh, rpnstruct->u.complex->s1 ) ;
496         walk_complex_query(zh, rpnstruct->u.complex->s2 ) ;
497     }
498 }
499
500 void zebra_livcode_transform(ZebraHandle zh, Z_RPNQuery *query)
501 {
502     /*
503     ** Got a search request,
504     ** 1. if it is a simple query, see if it suitable for expansion
505     **    i.e. the attributes are of the form ...
506     **    (type==1 && value==1016) && (type==2 && value==102)
507     ** or
508     ** 2. if it is complex, traverse the complex query tree and expand
509     **    any simples simples as above
510     */
511 #if LIV_CODE
512     Z_RPNStructure *rpnstruct = query->RPNStructure ;
513     
514     if ( rpnstruct->which == Z_RPNStructure_simple )
515     {
516         convert_simple2complex(zh, rpnstruct ) ;
517     }
518     else if ( rpnstruct->which == Z_RPNStructure_complex )
519     {
520         walk_complex_query(zh, rpnstruct ) ;
521     }
522 #endif
523 }
524
525
526 struct rank_class_info {
527     int dummy;
528 };
529
530 struct rank_term_info {
531     int local_occur;
532     int global_occur;
533     int global_inv;
534     int rank_flag;
535 };
536
537 struct rank_set_info {
538     int last_pos;
539     int no_entries;
540     int no_rank_entries;
541     struct rank_term_info *entries;
542 };
543
544 static int log2_int (unsigned g)
545 {
546     int n = 0;
547     while ((g = g>>1))
548         n++;
549     return n;
550 }
551
552 /*
553  * create: Creates/Initialises this rank handler. This routine is 
554  *  called exactly once. The routine returns the class_handle.
555  */
556 static void *create (ZebraHandle zh)
557 {
558     struct rank_class_info *ci = (struct rank_class_info *)
559         xmalloc (sizeof(*ci));
560
561     logf (LOG_DEBUG, "livrank create");
562
563     read_zrank_file(zh) ;
564
565     return ci;
566 }
567
568 /*
569  * destroy: Destroys this rank handler. This routine is called
570  *  when the handler is no longer needed - i.e. when the server
571  *  dies. The class_handle was previously returned by create.
572  */
573 static void destroy (struct zebra_register *reg, void *class_handle)
574 {
575     struct rank_class_info *ci = (struct rank_class_info *) class_handle;
576
577     logf (LOG_DEBUG, "livrank destroy");
578     xfree (ci);
579 }
580
581
582 /*
583  * begin: Prepares beginning of "real" ranking. Called once for
584  *  each result set. The returned handle is a "set handle" and
585  *  will be used in each of the handlers below.
586  */
587 static void *begin (struct zebra_register *reg, void *class_handle, RSET rset)
588 {
589     struct rank_set_info *si = (struct rank_set_info *) xmalloc (sizeof(*si));
590     int i;
591
592     logf (LOG_DEBUG, "livrank begin");
593     si->no_entries = rset->no_rset_terms;
594     si->no_rank_entries = 0;
595     si->entries = (struct rank_term_info *)
596         xmalloc (sizeof(*si->entries)*si->no_entries);
597     for (i = 0; i < si->no_entries; i++)
598     {
599         const char *flags = rset->rset_terms[i]->flags;
600         int g = rset->rset_terms[i]->nn;
601         const char *cp = strstr(flags, ",u=");
602
603         si->entries[i].rank_flag = 1;
604         if (cp)
605         {
606             char *t = search_for_rankstr(atoi(cp+3));
607             if (t)
608                 si->entries[i].rank_flag = search_for_score(t) ;
609         }
610         if ( si->entries[i].rank_flag )
611             (si->no_rank_entries)++;
612
613         si->entries[i].local_occur = 0;
614         si->entries[i].global_occur = g;
615         si->entries[i].global_inv = 32 - log2_int (g);
616         logf (LOG_DEBUG, "-------- %d ------", 32 - log2_int (g));
617     }
618     return si;
619 }
620
621 /*
622  * end: Terminates ranking process. Called after a result set
623  *  has been ranked.
624  */
625 static void end (struct zebra_register *reg, void *set_handle)
626 {
627     struct rank_set_info *si = (struct rank_set_info *) set_handle;
628     logf (LOG_DEBUG, "livrank end");
629     xfree (si->entries);
630     xfree (si);
631 }
632
633 /*
634  * add: Called for each word occurence in a result set. This routine
635  *  should be as fast as possible. This routine should "incrementally"
636  *  update the score.
637  */
638 static void add (void *set_handle, int seqno, int term_index)
639 {
640     struct rank_set_info *si = (struct rank_set_info *) set_handle;
641     logf (LOG_DEBUG, "rank-1 add seqno=%d term_index=%d", seqno, term_index);
642     si->last_pos = seqno;
643     si->entries[term_index].local_occur++;
644 }
645
646 /*
647  * calc: Called for each document in a result. This handler should 
648  *  produce a score based on previous call(s) to the add handler. The
649  *  score should be between 0 and 1000. If score cannot be obtained
650  *  -1 should be returned.
651  */
652 static int calc (void *set_handle, int sysno)
653 {
654     int i, lo, divisor, score = 0;
655     struct rank_set_info *si = (struct rank_set_info *) set_handle;
656
657     logf (LOG_DEBUG, "livrank calc sysno=%d", sysno);
658
659     if (!si->no_rank_entries)
660         return -1;
661     for (i = 0; i < si->no_entries; i++)
662     {
663         score += si->entries[i].local_occur * si->entries[i].rank_flag ;
664     }
665     for (i = 0; i < si->no_entries; i++)
666         if (si->entries[i].rank_flag && (lo = si->entries[i].local_occur))
667             score += (8+log2_int (lo)) * si->entries[i].global_inv;
668     score *= 34;
669     divisor = si->no_rank_entries * (8+log2_int (si->last_pos/si->no_entries));
670     score = score / divisor;
671     if (score > 1000)
672         score = 1000;
673     for (i = 0; i < si->no_entries; i++)
674         si->entries[i].local_occur = 0;
675     return score;
676 }
677
678 /*
679  * Pseudo-meta code with sequence of calls as they occur in a
680  * server. Handlers are prefixed by --:
681  *
682  *     server init
683  *     -- create
684  *     foreach search
685  *        rank result set
686  *        -- begin
687  *        foreach record
688  *           foreach word
689  *              -- add
690  *           -- calc
691  *        -- end
692  *     -- destroy
693  *     server close
694  */
695
696 static struct rank_control rank_control = {
697     "livrank",
698     create,
699     destroy,
700     begin,
701     end,
702     calc,
703     add,
704 };
705  
706 struct rank_control *rankliv_class = &rank_control;