Improved ranking.
[idzebra-moved-to-github.git] / index / rank1.c
1 /*
2  * Copyright (C) 1998, Index Data I/S
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: rank1.c,v $
7  * Revision 1.2  1998-03-05 13:03:29  adam
8  * Improved ranking.
9  *
10  * Revision 1.1  1998/03/05 08:45:12  adam
11  * New result set model and modular ranking system. Moved towards
12  * descent server API. System information stored as "SGML" records.
13  *
14  */
15
16 #include <stdio.h>
17 #include <assert.h>
18 #ifdef WINDOWS
19 #include <io.h>
20 #else
21 #include <unistd.h>
22 #endif
23
24 #include "zserver.h"
25
26 struct rank_class_info {
27     int dummy;
28 };
29
30 struct rank_term_info {
31     int local_occur;
32     int global_occur;
33     int global_inv;
34     int rank_flag;
35 };
36
37 struct rank_set_info {
38     int last_pos;
39     int no_entries;
40     int no_rank_entries;
41     struct rank_term_info *entries;
42 };
43
44 static int log2_int (unsigned g)
45 {
46     int n = 0;
47     while ((g = g>>1))
48         n++;
49     return n;
50 }
51
52 /*
53  * create: Creates/Initialises this rank handler. This routine is 
54  *  called exactly once. The routine returns the class_handle.
55  */
56 static void *create (ZebraHandle zh)
57 {
58     struct rank_class_info *ci = xmalloc (sizeof(*ci));
59
60     logf (LOG_DEBUG, "rank-1 create");
61     return ci;
62 }
63
64 /*
65  * destroy: Destroys this rank handler. This routine is called
66  *  when the handler is no longer needed - i.e. when the server
67  *  dies. The class_handle was previously returned by create.
68  */
69 static void destroy (ZebraHandle zh, void *class_handle)
70 {
71     struct rank_class_info *ci = class_handle;
72
73     logf (LOG_DEBUG, "rank-1 destroy");
74     xfree (ci);
75 }
76
77
78 /*
79  * begin: Prepares beginning of "real" ranking. Called once for
80  *  each result set. The returned handle is a "set handle" and
81  *  will be used in each of the handlers below.
82  */
83 static void *begin (ZebraHandle zh, void *class_handle, RSET rset)
84 {
85     struct rank_set_info *si = xmalloc (sizeof(*si));
86     int i;
87
88     logf (LOG_DEBUG, "rank-1 begin");
89     si->no_entries = rset->no_rset_terms;
90     si->no_rank_entries = 0;
91     si->entries = xmalloc (sizeof(*si->entries)*si->no_entries);
92     for (i = 0; i < si->no_entries; i++)
93     {
94         int g = rset->rset_terms[i]->nn;
95         if (!strcmp (rset->rset_terms[i]->flags, "rank"))
96         {
97             si->entries[i].rank_flag = 1;
98             (si->no_rank_entries)++;
99         }
100         else
101             si->entries[i].rank_flag = 0;
102         si->entries[i].local_occur = 0;
103         si->entries[i].global_occur = g;
104         si->entries[i].global_inv = 32 - log2_int (g);
105         logf (LOG_DEBUG, "-------- %d ------", 32 - log2_int (g));
106     }
107     return si;
108 }
109
110 /*
111  * end: Terminates ranking process. Called after a result set
112  *  has been ranked.
113  */
114 static void end (ZebraHandle zh, void *set_handle)
115 {
116     struct rank_set_info *si = set_handle;
117     logf (LOG_DEBUG, "rank-1 end");
118     xfree (si);
119 }
120
121 /*
122  * add: Called for each word occurence in a result set. This routine
123  *  should be as fast as possible. This routine should "incrementally"
124  *  update the score.
125  */
126 static void add (void *set_handle, int seqno, int term_index)
127 {
128     struct rank_set_info *si = set_handle;
129     logf (LOG_DEBUG, "rank-1 add seqno=%d term_index=%d", seqno, term_index);
130     si->last_pos = seqno;
131     si->entries[term_index].local_occur++;
132 }
133
134 /*
135  * calc: Called for each document in a result. This handler should 
136  *  produce a score based on previous call(s) to the add handler. The
137  *  score should be between 0 and 1000. If score cannot be obtained
138  *  -1 should be returned.
139  */
140 static int calc (void *set_handle, int sysno)
141 {
142     int i, lo, divisor, score = 0;
143     struct rank_set_info *si = set_handle;
144
145     logf (LOG_DEBUG, "rank-1 calc sysno=%d", sysno);
146
147     if (!si->no_rank_entries)
148         return -1;
149     for (i = 0; i < si->no_entries; i++)
150         if (si->entries[i].rank_flag && (lo = si->entries[i].local_occur))
151             score += (8+log2_int (lo)) * si->entries[i].global_inv;
152     score *= 34;
153     divisor = si->no_rank_entries * (8+log2_int (si->last_pos/si->no_entries));
154     score = score / divisor;
155     if (score > 1000)
156         score = 1000;
157     for (i = 0; i < si->no_entries; i++)
158         si->entries[i].local_occur = 0;
159     return score;
160 }
161
162 /*
163  * Pseudo-meta code with sequence of calls as they occur in a
164  * server. Handlers are prefixed by --:
165  *
166  *     server init
167  *     -- create
168  *     foreach search
169  *        rank result set
170  *        -- begin
171  *        foreach record
172  *           foreach word
173  *              -- add
174  *           -- calc
175  *        -- end
176  *     -- destroy
177  *     server close
178  */
179
180 static struct rank_control rank_control = {
181     "rank-1",
182     create,
183     destroy,
184     begin,
185     end,
186     calc,
187     add,
188 };
189  
190 struct rank_control *rank1_class = &rank_control;