ferret 0.3.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/CHANGELOG +9 -0
  2. data/Rakefile +51 -25
  3. data/ext/analysis.c +553 -0
  4. data/ext/analysis.h +76 -0
  5. data/ext/array.c +83 -0
  6. data/ext/array.h +19 -0
  7. data/ext/bitvector.c +164 -0
  8. data/ext/bitvector.h +29 -0
  9. data/ext/compound_io.c +335 -0
  10. data/ext/document.c +336 -0
  11. data/ext/document.h +87 -0
  12. data/ext/ferret.c +88 -47
  13. data/ext/ferret.h +43 -109
  14. data/ext/field.c +395 -0
  15. data/ext/filter.c +103 -0
  16. data/ext/fs_store.c +352 -0
  17. data/ext/global.c +219 -0
  18. data/ext/global.h +73 -0
  19. data/ext/hash.c +446 -0
  20. data/ext/hash.h +80 -0
  21. data/ext/hashset.c +141 -0
  22. data/ext/hashset.h +37 -0
  23. data/ext/helper.c +11 -0
  24. data/ext/helper.h +5 -0
  25. data/ext/inc/lang.h +41 -0
  26. data/ext/ind.c +389 -0
  27. data/ext/index.h +884 -0
  28. data/ext/index_io.c +269 -415
  29. data/ext/index_rw.c +2543 -0
  30. data/ext/lang.c +31 -0
  31. data/ext/lang.h +41 -0
  32. data/ext/priorityqueue.c +228 -0
  33. data/ext/priorityqueue.h +44 -0
  34. data/ext/q_boolean.c +1331 -0
  35. data/ext/q_const_score.c +154 -0
  36. data/ext/q_fuzzy.c +287 -0
  37. data/ext/q_match_all.c +142 -0
  38. data/ext/q_multi_phrase.c +343 -0
  39. data/ext/q_parser.c +2180 -0
  40. data/ext/q_phrase.c +657 -0
  41. data/ext/q_prefix.c +75 -0
  42. data/ext/q_range.c +247 -0
  43. data/ext/q_span.c +1566 -0
  44. data/ext/q_term.c +308 -0
  45. data/ext/q_wildcard.c +146 -0
  46. data/ext/r_analysis.c +255 -0
  47. data/ext/r_doc.c +578 -0
  48. data/ext/r_index_io.c +996 -0
  49. data/ext/r_qparser.c +158 -0
  50. data/ext/r_search.c +2321 -0
  51. data/ext/r_store.c +263 -0
  52. data/ext/r_term.c +219 -0
  53. data/ext/ram_store.c +447 -0
  54. data/ext/search.c +524 -0
  55. data/ext/search.h +1065 -0
  56. data/ext/similarity.c +143 -39
  57. data/ext/sort.c +661 -0
  58. data/ext/store.c +35 -0
  59. data/ext/store.h +152 -0
  60. data/ext/term.c +704 -143
  61. data/ext/termdocs.c +599 -0
  62. data/ext/vector.c +594 -0
  63. data/lib/ferret.rb +9 -10
  64. data/lib/ferret/analysis/analyzers.rb +2 -2
  65. data/lib/ferret/analysis/standard_tokenizer.rb +1 -1
  66. data/lib/ferret/analysis/token.rb +14 -14
  67. data/lib/ferret/analysis/token_filters.rb +3 -3
  68. data/lib/ferret/document/field.rb +16 -17
  69. data/lib/ferret/index/document_writer.rb +4 -4
  70. data/lib/ferret/index/index.rb +39 -23
  71. data/lib/ferret/index/index_writer.rb +2 -2
  72. data/lib/ferret/index/multiple_term_doc_pos_enum.rb +1 -8
  73. data/lib/ferret/index/segment_term_vector.rb +4 -4
  74. data/lib/ferret/index/term.rb +5 -1
  75. data/lib/ferret/index/term_vector_offset_info.rb +6 -6
  76. data/lib/ferret/index/term_vectors_io.rb +5 -5
  77. data/lib/ferret/query_parser/query_parser.tab.rb +81 -77
  78. data/lib/ferret/search.rb +1 -1
  79. data/lib/ferret/search/boolean_query.rb +2 -1
  80. data/lib/ferret/search/field_sorted_hit_queue.rb +3 -3
  81. data/lib/ferret/search/fuzzy_query.rb +2 -1
  82. data/lib/ferret/search/index_searcher.rb +3 -0
  83. data/lib/ferret/search/{match_all_docs_query.rb → match_all_query.rb} +7 -7
  84. data/lib/ferret/search/multi_phrase_query.rb +6 -5
  85. data/lib/ferret/search/phrase_query.rb +3 -6
  86. data/lib/ferret/search/prefix_query.rb +4 -4
  87. data/lib/ferret/search/sort.rb +3 -1
  88. data/lib/ferret/search/sort_field.rb +9 -9
  89. data/lib/ferret/search/spans/near_spans_enum.rb +1 -1
  90. data/lib/ferret/search/spans/span_near_query.rb +1 -1
  91. data/lib/ferret/search/spans/span_weight.rb +1 -1
  92. data/lib/ferret/search/spans/spans_enum.rb +7 -7
  93. data/lib/ferret/store/fs_store.rb +10 -6
  94. data/lib/ferret/store/ram_store.rb +3 -3
  95. data/lib/rferret.rb +36 -0
  96. data/test/functional/thread_safety_index_test.rb +2 -2
  97. data/test/test_helper.rb +16 -2
  98. data/test/unit/analysis/c_token.rb +25 -0
  99. data/test/unit/analysis/tc_per_field_analyzer_wrapper.rb +1 -1
  100. data/test/unit/analysis/tc_standard_analyzer.rb +1 -1
  101. data/test/unit/document/{tc_document.rb → c_document.rb} +0 -0
  102. data/test/unit/document/c_field.rb +98 -0
  103. data/test/unit/document/tc_field.rb +0 -66
  104. data/test/unit/index/{tc_index.rb → c_index.rb} +62 -6
  105. data/test/unit/index/{tc_index_reader.rb → c_index_reader.rb} +51 -10
  106. data/test/unit/index/{tc_index_writer.rb → c_index_writer.rb} +0 -4
  107. data/test/unit/index/{tc_term.rb → c_term.rb} +1 -3
  108. data/test/unit/index/{tc_term_vector_offset_info.rb → c_term_voi.rb} +5 -5
  109. data/test/unit/index/tc_segment_term_vector.rb +2 -2
  110. data/test/unit/index/tc_term_vectors_io.rb +4 -4
  111. data/test/unit/query_parser/c_query_parser.rb +138 -0
  112. data/test/unit/search/{tc_filter.rb → c_filter.rb} +24 -24
  113. data/test/unit/search/{tc_fuzzy_query.rb → c_fuzzy_query.rb} +0 -0
  114. data/test/unit/search/{tc_index_searcher.rb → c_index_searcher.rb} +9 -26
  115. data/test/unit/search/{tc_search_and_sort.rb → c_search_and_sort.rb} +15 -15
  116. data/test/unit/search/{tc_sort.rb → c_sort.rb} +2 -1
  117. data/test/unit/search/c_sort_field.rb +27 -0
  118. data/test/unit/search/{tc_spans.rb → c_spans.rb} +0 -0
  119. data/test/unit/search/tc_sort_field.rb +7 -20
  120. data/test/unit/store/c_fs_store.rb +76 -0
  121. data/test/unit/store/c_ram_store.rb +35 -0
  122. data/test/unit/store/m_store.rb +34 -0
  123. data/test/unit/store/m_store_lock.rb +68 -0
  124. data/test/unit/store/tc_fs_store.rb +0 -53
  125. data/test/unit/store/tc_ram_store.rb +0 -20
  126. data/test/unit/store/tm_store.rb +0 -30
  127. data/test/unit/store/tm_store_lock.rb +0 -66
  128. metadata +84 -31
  129. data/ext/Makefile +0 -140
  130. data/ext/ferret_ext.so +0 -0
  131. data/ext/priority_queue.c +0 -232
  132. data/ext/ram_directory.c +0 -321
  133. data/ext/segment_merge_queue.c +0 -37
  134. data/ext/segment_term_enum.c +0 -326
  135. data/ext/string_helper.c +0 -42
  136. data/ext/tags +0 -344
  137. data/ext/term_buffer.c +0 -230
  138. data/ext/term_infos_reader.c +0 -54
  139. data/ext/terminfo.c +0 -160
  140. data/ext/token.c +0 -93
  141. data/ext/util.c +0 -12
data/ext/lang.c ADDED
@@ -0,0 +1,31 @@
1
+ #include <stdarg.h>
2
+ #include <string.h>
3
+ #include <stdio.h>
4
+ #include <errno.h>
5
+ #include "global.h"
6
+
7
+ void ft_raise(char *file, int line_num, VALUE etype, const char *fmt, ...)
8
+ {
9
+ va_list args;
10
+ char buf[MAX_ERROR_LEN];
11
+ char *buf_ptr = buf;
12
+
13
+ if (progname() != NULL) {
14
+ sprintf(buf_ptr, "%s: ", progname());
15
+ buf_ptr += strlen(buf_ptr);
16
+ }
17
+
18
+ sprintf(buf_ptr, "Error occured at <%s>:%d\n", file, line_num);
19
+ buf_ptr += strlen(buf_ptr);
20
+ va_start(args, fmt);
21
+ vsprintf(buf_ptr, fmt, args);
22
+ buf_ptr += strlen(buf_ptr);
23
+ va_end(args);
24
+
25
+ if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') {
26
+ sprintf(buf_ptr, " %s", strerror(errno));
27
+ buf_ptr += strlen(buf_ptr);
28
+ }
29
+ sprintf(buf_ptr, "\n");
30
+ rb_raise(etype, buf); /* conventional value for failed execution */
31
+ }
data/ext/lang.h ADDED
@@ -0,0 +1,41 @@
1
+ #ifndef FRT_LANG_H
2
+ #define FRT_LANG_H
3
+
4
+ #include <ruby.h>
5
+
6
+ #define FERRET_EXT
7
+
8
+ #define MAX_ERROR_LEN 2048
9
+ #define eprintf(...) ft_raise(__FILE__, __LINE__, __VA_ARGS__)
10
+ extern void ft_raise(char *file, int line_num, VALUE etype, const char *fmt, ...);
11
+ extern void weprintf(const char *fmt, ...);
12
+ extern char *progname(void);
13
+ extern void setprogname(const char *str);
14
+
15
+ extern VALUE cQueryParseException;
16
+
17
+ #define ERROR rb_eException
18
+ #define IO_ERROR rb_eIOError
19
+ #define ARG_ERROR rb_eArgError
20
+ #define EOF_ERROR rb_eEOFError
21
+ #define UNSUPPORTED_ERROR rb_eNotImpError
22
+ #define STATE_ERROR rb_eException
23
+ #define PARSE_ERROR cQueryParseException
24
+ #define MEM_ERROR rb_eNoMemError
25
+
26
+ typedef void * mutex_t;
27
+ typedef void * thread_key_t;
28
+ #define MUTEX_INITIALIZER NULL
29
+ #define MUTEX_RECURSIVE_INITIALIZER NULL
30
+ #define mutex_init(a, b)
31
+ #define mutex_lock(a)
32
+ #define mutex_trylock(a)
33
+ #define mutex_unlock(a)
34
+ #define mutex_destroy(a)
35
+ #define thread_key_create(a, b)
36
+ #define thread_key_delete(a)
37
+ #define thread_setspecific(a, b)
38
+ #define thread_getspecific(a) NULL
39
+ #define thread_exit(a)
40
+
41
+ #endif
@@ -0,0 +1,228 @@
1
+ #include <priorityqueue.h>
2
+
3
+ PriorityQueue *pq_create(int max_size, bool (*less_than)(void *p1, void *p2))
4
+ {
5
+ PriorityQueue *pq = ALLOC(PriorityQueue);
6
+ pq->count = 0;
7
+ pq->size = max_size;
8
+ pq->heap = ALLOC_N(void *, (max_size + 1));
9
+ pq->lt = less_than;
10
+ pq->free_elem = &free;
11
+ return pq;
12
+ }
13
+
14
+ void pq_destroy(void *p)
15
+ {
16
+ PriorityQueue *pq = (PriorityQueue *)p;
17
+ free(pq->heap);
18
+ free(p);
19
+ }
20
+
21
+ void pq_up(PriorityQueue *pq)
22
+ {
23
+ int i,j;
24
+ i = pq->count;
25
+ j = i >> 1;
26
+ void **heap = pq->heap;
27
+ void *node = heap[i];
28
+
29
+ while ((j > 0) && pq->lt(node, heap[j])) {
30
+ heap[i] = heap[j];
31
+ i = j;
32
+ j = j >> 1;
33
+ }
34
+ heap[i] = node;
35
+ }
36
+
37
+ void pq_down(PriorityQueue *pq)
38
+ {
39
+ register int i = 1;
40
+ register int j = 2; //i << 1;
41
+ register int k = 3; //j + 1;
42
+ register int count = pq->count;
43
+ void **heap = pq->heap;
44
+ void *node = heap[i]; // save top node
45
+
46
+ if ((k <= count) && (pq->lt(heap[k], heap[j])))
47
+ j = k;
48
+
49
+ while ((j <= count) && pq->lt(heap[j], node)) {
50
+ heap[i] = heap[j]; // shift up child
51
+ i = j;
52
+ j = i << 1;
53
+ k = j + 1;
54
+ if ((k <= count) && pq->lt(heap[k], heap[j]))
55
+ j = k;
56
+ }
57
+ heap[i] = node;
58
+ }
59
+
60
+ void pq_push(PriorityQueue *pq, void *elem)
61
+ {
62
+ pq->count++;
63
+ pq->heap[pq->count] = elem;
64
+ pq_up(pq);
65
+ }
66
+
67
+ void *pq_top(PriorityQueue *pq)
68
+ {
69
+ return pq->heap[1];
70
+ }
71
+
72
+ void *pq_pop(PriorityQueue *pq)
73
+ {
74
+ if (pq->count > 0) {
75
+ void *result = pq->heap[1]; // save first value
76
+ pq->heap[1] = pq->heap[pq->count]; // move last to first
77
+ pq->heap[pq->count] = NULL;
78
+ pq->count--;
79
+ pq_down(pq); // adjust heap
80
+ return result;
81
+ } else {
82
+ return NULL;
83
+ }
84
+ }
85
+
86
+ void pq_clear(PriorityQueue *pq)
87
+ {
88
+ int i;
89
+ for (i = 1; i <= pq->count; i++) {
90
+ pq->free_elem(pq->heap[i]);
91
+ pq->heap[i] = NULL;
92
+ }
93
+ pq->count = 0;
94
+ }
95
+
96
+ int pq_insert(PriorityQueue *pq, void *elem)
97
+ {
98
+ if (pq->count < pq->size) {
99
+ pq_push(pq, elem);
100
+ return true;
101
+ } else if (pq->count > 0 && pq->lt(pq_top(pq), elem)) {
102
+ pq->free_elem(pq->heap[1]);
103
+ pq->heap[1] = elem;
104
+ pq_down(pq);
105
+ return true;
106
+ } else {
107
+ pq->free_elem(elem);
108
+ return false;
109
+ }
110
+ }
111
+
112
+ /*****************************************************************************
113
+ *
114
+ * PriorityQueue2
115
+ *
116
+ *****************************************************************************/
117
+
118
+ PriorityQueue2 *pq2_create(int max_size,
119
+ bool (*less_than)(PriorityQueue2 *pq, void *p1, void *p2),
120
+ void (*destroy)(void *p))
121
+ {
122
+ PriorityQueue2 *pq = ALLOC(PriorityQueue2);
123
+ pq->count = 0;
124
+ pq->size = max_size;
125
+ pq->heap = ALLOC_N(void *, (max_size + 1));
126
+ pq->lt = less_than;
127
+ pq->free_elem = &free;
128
+ pq->destroy = destroy;
129
+ return pq;
130
+ }
131
+
132
+ void pq2_destroy(void *p)
133
+ {
134
+ PriorityQueue2 *pq = (PriorityQueue2 *)p;
135
+ free(pq->heap);
136
+ free(p);
137
+ }
138
+
139
+ void pq2_up(PriorityQueue2 *pq)
140
+ {
141
+ int i,j;
142
+ i = pq->count;
143
+ j = i >> 1;
144
+ void **heap = pq->heap;
145
+ void *node = heap[i];
146
+
147
+ while ((j > 0) && pq->lt(pq, node, heap[j])) {
148
+ heap[i] = heap[j];
149
+ i = j;
150
+ j = j >> 1;
151
+ }
152
+ heap[i] = node;
153
+ }
154
+
155
+ void pq2_down(PriorityQueue2 *pq)
156
+ {
157
+ register int i = 1;
158
+ register int j = 2; //i << 1;
159
+ register int k = 3; //j + 1;
160
+ register int count = pq->count;
161
+ void **heap = pq->heap;
162
+ void *node = heap[i]; // save top node
163
+
164
+ if ((k <= count) && (pq->lt(pq, heap[k], heap[j])))
165
+ j = k;
166
+
167
+ while ((j <= count) && pq->lt(pq, heap[j], node)) {
168
+ heap[i] = heap[j]; // shift up child
169
+ i = j;
170
+ j = i << 1;
171
+ k = j + 1;
172
+ if ((k <= count) && pq->lt(pq, heap[k], heap[j]))
173
+ j = k;
174
+ }
175
+ heap[i] = node;
176
+ }
177
+
178
+ void pq2_push(PriorityQueue2 *pq, void *elem)
179
+ {
180
+ pq->count++;
181
+ pq->heap[pq->count] = elem;
182
+ pq2_up(pq);
183
+ }
184
+
185
+ void *pq2_top(PriorityQueue2 *pq)
186
+ {
187
+ return pq->heap[1];
188
+ }
189
+
190
+ void *pq2_pop(PriorityQueue2 *pq)
191
+ {
192
+ if (pq->count > 0) {
193
+ void *result = pq->heap[1]; // save first value
194
+ pq->heap[1] = pq->heap[pq->count]; // move last to first
195
+ pq->heap[pq->count] = NULL;
196
+ pq->count--;
197
+ pq2_down(pq); // adjust heap
198
+ return result;
199
+ } else {
200
+ return NULL;
201
+ }
202
+ }
203
+
204
+ void pq2_clear(PriorityQueue2 *pq)
205
+ {
206
+ int i;
207
+ for (i = 1; i <= pq->count; i++) {
208
+ pq->free_elem(pq->heap[i]);
209
+ pq->heap[i] = NULL;
210
+ }
211
+ pq->count = 0;
212
+ }
213
+
214
+ int pq2_insert(PriorityQueue2 *pq, void *elem)
215
+ {
216
+ if (pq->count < pq->size) {
217
+ pq2_push(pq, elem);
218
+ return true;
219
+ } else if (pq->count > 0 && pq->lt(pq, pq2_top(pq), elem)) {
220
+ pq->free_elem(pq->heap[1]);
221
+ pq->heap[1] = elem;
222
+ pq2_down(pq);
223
+ return true;
224
+ } else {
225
+ pq->free_elem(elem);
226
+ return false;
227
+ }
228
+ }
@@ -0,0 +1,44 @@
1
+ #ifndef FRT_PRIORITYQUEUE_H
2
+ #define FRT_PRIORITYQUEUE_H
3
+
4
+ #include "global.h"
5
+
6
+ typedef struct PriorityQueue {
7
+ int count;
8
+ int size;
9
+ void **heap;
10
+ bool (*lt)(void *p1, void *p2);
11
+ void (*free_elem)(void *p1);
12
+ } PriorityQueue;
13
+
14
+ PriorityQueue *pq_create(int max_size, bool (*less_than)(void *p1, void *p2));
15
+ void pq_destroy(void *p);
16
+ void pq_push(PriorityQueue *pq, void *elem);
17
+ void *pq_top(PriorityQueue *pq);
18
+ void *pq_pop(PriorityQueue *pq);
19
+ void pq_down(PriorityQueue *pq);
20
+ void pq_clear(PriorityQueue *pq);
21
+ int pq_insert(PriorityQueue *pq, void *elem);
22
+ #define pq_full(pq) ((pq)->count == (pq)->size)
23
+
24
+ typedef struct PriorityQueue2 {
25
+ int count;
26
+ int size;
27
+ void **heap;
28
+ void *data;
29
+ bool (*lt)(struct PriorityQueue2 *pq, void *p1, void *p2);
30
+ void (*free_elem)(void *p);
31
+ void (*destroy)(void *p);
32
+ } PriorityQueue2;
33
+
34
+ PriorityQueue2 *pq2_create(int max_size,
35
+ bool (*less_than)(PriorityQueue2 *pq, void *p1, void *p2),
36
+ void (*destroy)(void *p));
37
+ void pq2_destroy(void *p);
38
+ void pq2_push(PriorityQueue2 *pq, void *elem);
39
+ void *pq2_top(PriorityQueue2 *pq);
40
+ void *pq2_pop(PriorityQueue2 *pq);
41
+ void pq2_down(PriorityQueue2 *pq);
42
+ void pq2_clear(PriorityQueue2 *pq);
43
+ int pq2_insert(PriorityQueue2 *pq, void *elem);
44
+ #endif
data/ext/q_boolean.c ADDED
@@ -0,0 +1,1331 @@
1
+ #include <string.h>
2
+ #include "search.h"
3
+
4
+ /***************************************************************************
5
+ *
6
+ * BooleanWeight
7
+ *
8
+ ***************************************************************************/
9
+
10
+ float bw_sum_of_squared_weights(Weight *self)
11
+ {
12
+ BooleanWeight *bw = (BooleanWeight *)self->data;
13
+ BooleanQuery *bq = (BooleanQuery *)self->query->data;
14
+ Weight *weight;
15
+
16
+ float sum = 0.0;
17
+ int i;
18
+
19
+ for (i = 0; i < bw->w_cnt; i++) {
20
+ if (! bq->clauses[i]->is_prohibited) {
21
+ weight = bw->weights[i];
22
+ sum += weight->sum_of_squared_weights(weight); // sum sub-weights
23
+ }
24
+ }
25
+
26
+ sum *= self->value * self->value; // boost each sub-weight
27
+
28
+ return sum;
29
+ }
30
+
31
+ void bw_normalize(Weight *self, float normalization_factor)
32
+ {
33
+ BooleanWeight *bw = (BooleanWeight *)self->data;
34
+ BooleanQuery *bq = (BooleanQuery *)self->query->data;
35
+ normalization_factor *= self->value; // multiply by query boost
36
+ Weight *weight;
37
+ int i;
38
+
39
+ for (i = 0; i < bw->w_cnt; i++) {
40
+ if (! bq->clauses[i]->is_prohibited) {
41
+ weight = bw->weights[i];
42
+ weight->normalize(weight, normalization_factor); // sum sub-weights
43
+ }
44
+ }
45
+ }
46
+
47
+ Scorer *bw_scorer(Weight *self, IndexReader *ir)
48
+ {
49
+ Scorer *sub_scorer, *bsc = bsc_create(self->similarity);
50
+ BooleanWeight *bw = (BooleanWeight *)self->data;
51
+ BooleanQuery *bq = (BooleanQuery *)self->query->data;
52
+ BooleanClause *clause;
53
+ Weight *weight;
54
+ int i;
55
+
56
+ for (i = 0; i < bw->w_cnt; i++) {
57
+ clause = bq->clauses[i];
58
+ weight = bw->weights[i];
59
+ sub_scorer = weight->scorer(weight, ir);
60
+ if (sub_scorer) {
61
+ bsc_add_scorer(bsc, sub_scorer, clause->occur);
62
+ } else if (clause->is_required) {
63
+ bsc->destroy(bsc);
64
+ return NULL;
65
+ }
66
+ }
67
+
68
+ return bsc;
69
+ }
70
+
71
+ char *bw_to_s(Weight *self)
72
+ {
73
+ char dbuf[32];
74
+ dbl_to_s(dbuf, self->value);
75
+ return epstrdup("BooleanWeight(%s)", strlen(dbuf), dbuf);
76
+ }
77
+
78
+ void bw_destroy(void *p)
79
+ {
80
+ Weight *weight = (Weight *)p;
81
+ BooleanWeight *bw = (BooleanWeight *)weight->data;
82
+ free(bw->weights);
83
+ free(bw);
84
+ free(weight);
85
+ }
86
+
87
+ Explanation *bw_explain(Weight *self, IndexReader *ir, int doc_num)
88
+ {
89
+ BooleanWeight *bw = (BooleanWeight *)self->data;
90
+ BooleanQuery *bq = (BooleanQuery *)self->query->data;
91
+ Explanation *sum_expl = expl_create(0.0, estrdup("sum of:"));
92
+ BooleanClause *clause;
93
+ Weight *weight;
94
+ Explanation *explanation;
95
+ int coord = 0;
96
+ int max_coord = 0;
97
+ float coord_factor = 0.0;
98
+ float sum = 0.0;
99
+ int i;
100
+
101
+ for (i = 0; i < bw->w_cnt; i++) {
102
+ weight = bw->weights[i];
103
+ clause = bq->clauses[i];
104
+ explanation = weight->explain(weight, ir, doc_num);
105
+ if (!clause->is_prohibited) max_coord++;
106
+ if (explanation->value > 0.0) {
107
+ if (!clause->is_prohibited) {
108
+ expl_add_detail(sum_expl, explanation);
109
+ sum += explanation->value;
110
+ coord++;
111
+ } else {
112
+ expl_destoy(explanation);
113
+ expl_destoy(sum_expl);
114
+ return expl_create(0.0, estrdup("match prohibited"));
115
+ }
116
+ } else if (clause->is_required) {
117
+ expl_destoy(explanation);
118
+ expl_destoy(sum_expl);
119
+ return expl_create(0.0, estrdup("match required"));
120
+ } else {
121
+ expl_destoy(explanation);
122
+ }
123
+ }
124
+ sum_expl->value = sum;
125
+
126
+ if (coord == 1) { // only one clause matched
127
+ explanation = sum_expl; // eliminate wrapper
128
+ sum_expl->dcnt = 0;
129
+ sum_expl = sum_expl->details[0];
130
+ expl_destoy(explanation);
131
+ }
132
+
133
+ coord_factor = sim_coord(self->similarity, coord, max_coord);
134
+
135
+ if (coord_factor == 1.0) { // coord is no-op
136
+ return sum_expl; // eliminate wrapper
137
+ } else {
138
+ explanation = expl_create(sum * coord_factor, estrdup("product of:"));
139
+ expl_add_detail(explanation, sum_expl);
140
+ expl_add_detail(explanation, expl_create(coord_factor,
141
+ epstrdup("coord(%d/%d)", 40, coord, max_coord)));
142
+ return explanation;
143
+ }
144
+ }
145
+
146
+ Weight *bw_create(Query *query, Searcher *searcher)
147
+ {
148
+ int i;
149
+ BooleanQuery *bq = (BooleanQuery *)query->data;
150
+ BooleanWeight *bw = ALLOC(BooleanWeight);
151
+ Weight *self = ALLOC(Weight);
152
+ ZEROSET(self, Weight, 1);
153
+ self->data = bw;
154
+ self->get_query = &w_get_query;
155
+ self->get_value = &w_get_value;
156
+ self->normalize = &bw_normalize;
157
+ self->scorer = &bw_scorer;
158
+ self->explain = &bw_explain;
159
+ self->to_s = &bw_to_s;
160
+ self->destroy = &bw_destroy;
161
+ self->sum_of_squared_weights = &bw_sum_of_squared_weights;
162
+
163
+ self->similarity = query->get_similarity(query, searcher);
164
+ self->query = query;
165
+ self->value = query->boost;
166
+
167
+ bw->w_cnt = bq->clause_cnt;
168
+ bw->weights = ALLOC_N(Weight *, bw->w_cnt);
169
+ for (i = 0; i < bw->w_cnt; i++) {
170
+ bw->weights[i] = q_weight(bq->clauses[i]->query, searcher);
171
+ }
172
+
173
+ return self;
174
+ }
175
+
176
+ /***************************************************************************
177
+ *
178
+ * BooleanClause
179
+ *
180
+ ***************************************************************************/
181
+
182
+ void bc_set_occur(BooleanClause *self, unsigned int occur)
183
+ {
184
+ self->occur = occur;
185
+ switch (occur) {
186
+ case BC_SHOULD:
187
+ self->is_prohibited = false;
188
+ self->is_required = false;
189
+ break;
190
+ case BC_MUST:
191
+ self->is_prohibited = false;
192
+ self->is_required = true;
193
+ break;
194
+ case BC_MUST_NOT:
195
+ self->is_prohibited = true;
196
+ self->is_required = false;
197
+ break;
198
+ default:
199
+ eprintf(ARG_ERROR, "Invalid value %d for BooleanClause Type", occur);
200
+ }
201
+ }
202
+
203
+ BooleanClause *bc_create(Query *query, unsigned int occur)
204
+ {
205
+ BooleanClause *self = ALLOC(BooleanClause);
206
+ self->query = query;
207
+ bc_set_occur(self, occur);
208
+ return self;
209
+ }
210
+
211
+ /***************************************************************************
212
+ *
213
+ * BooleanQuery
214
+ *
215
+ ***************************************************************************/
216
+
217
+ Query *bq_rewrite(Query *self, IndexReader *ir)
218
+ {
219
+ BooleanQuery *bq = (BooleanQuery *)self->data;
220
+ BooleanClause *clause;
221
+ Query *query;
222
+ int i;
223
+
224
+ if (bq->clause_cnt == 1) { // optimize 1-clause queries
225
+ clause = bq->clauses[0];
226
+ if (! clause->is_prohibited) { // just return clause
227
+ query = clause->query->rewrite(clause->query, ir); // rewrite first
228
+
229
+ if (self->boost != 1.0) {// incorporate boost
230
+ // original_boost is initialized to 0.0. If it has been set to
231
+ // something else it means this query has already been boosted before
232
+ // so boost from the original value
233
+ if ((query == clause->query) && query->original_boost) { // rewrite was no-op
234
+ query->boost = query->original_boost * self->boost;
235
+ } else {
236
+ query->original_boost = query->boost; // save original boost
237
+ query->boost *= self->boost;
238
+ }
239
+ }
240
+
241
+ return query;
242
+ }
243
+ }
244
+
245
+ for (i = 0; i < bq->clause_cnt; i++) {
246
+ clause = bq->clauses[i];
247
+ clause->rewritten = clause->query->rewrite(clause->query, ir);
248
+ }
249
+ return self; // no clauses rewritten
250
+ }
251
+
252
+ void bq_extract_terms(Query *self, Array *terms)
253
+ {
254
+ BooleanQuery *bq = (BooleanQuery *)self->data;
255
+ BooleanClause *clause;
256
+ int i;
257
+ for (i = 0; i < bq->clause_cnt; i++) {
258
+ clause = bq->clauses[i];
259
+ clause->query->extract_terms(clause->query, terms);
260
+ }
261
+ }
262
+
263
+ char *bq_to_s(Query *self, char *field)
264
+ {
265
+ BooleanQuery *bq = (BooleanQuery *)self->data;
266
+ BooleanClause *clause;
267
+ Query *sub_query;
268
+ char *buffer;
269
+ char *clause_str;
270
+ int bp = 0;
271
+ int size = QUERY_STRING_START_SIZE;
272
+ int needed;
273
+ int clause_len;
274
+
275
+ buffer = ALLOC_N(char, size);
276
+ if (self->boost != 1.0) {
277
+ buffer[0] = '(';
278
+ bp++;
279
+ }
280
+
281
+ int i;
282
+ for (i = 0; i < bq->clause_cnt; i++) {
283
+ clause = bq->clauses[i];
284
+ clause_str = clause->query->to_s(clause->query, field);
285
+ clause_len = strlen(clause_str);
286
+ needed = clause_len + 5;
287
+ while ((size - bp) < needed) {
288
+ size *= 2;
289
+ REALLOC_N(buffer, char, size);
290
+ }
291
+
292
+ if (i > 0) {
293
+ buffer[bp++] = ' ';
294
+ }
295
+ if (clause->is_prohibited) {
296
+ buffer[bp++] = '-';
297
+ } else if (clause->is_required) {
298
+ buffer[bp++] = '+';
299
+ }
300
+
301
+ sub_query = clause->query;
302
+ if (sub_query->type == BOOLEAN_QUERY) { // wrap sub-bools in parens
303
+ buffer[bp++] = '(';
304
+ memcpy(buffer + bp, clause_str, sizeof(char) * clause_len);
305
+ bp += clause_len;
306
+ buffer[bp++] = ')';
307
+ } else {
308
+ memcpy(buffer + bp, clause_str, sizeof(char) * clause_len);
309
+ bp += clause_len;
310
+ }
311
+ free(clause_str);
312
+ }
313
+
314
+ if (self->boost != 1.0) {
315
+ char dbuf[32];
316
+ dbl_to_s(dbuf, self->boost);
317
+ char *boost_str = epstrdup(")^%s", strlen(dbuf), dbuf);
318
+ int boost_len = strlen(boost_str);
319
+ REALLOC_N(buffer, char, bp + boost_len + 1);
320
+ memcpy(buffer + bp, boost_str, sizeof(char) * boost_len);
321
+ bp += boost_len;
322
+ free(boost_str);
323
+ }
324
+ buffer[bp] = 0;
325
+ return buffer;
326
+ }
327
+
328
+ void bq_destroy(void *p)
329
+ {
330
+ Query *self = (Query *)p;
331
+ BooleanQuery *bq = (BooleanQuery *)self->data;
332
+ BooleanClause *clause;
333
+ int i;
334
+ for (i = 0; i < bq->clause_cnt; i++) {
335
+ clause = bq->clauses[i];
336
+ if (self->destroy_all) clause->query->destroy(clause->query);
337
+ free(clause);
338
+ }
339
+ free(bq->clauses);
340
+ if (bq->similarity) {
341
+ bq->similarity->destroy(bq->similarity);
342
+ }
343
+ free(bq);
344
+ q_destroy(self);
345
+ }
346
+
347
+ float bq_coord_disabled(Similarity *sim, int overlap, int max_overlap)
348
+ {
349
+ return 1.0;
350
+ }
351
+
352
+ Similarity *bq_get_similarity(Query *self, Searcher *searcher)
353
+ {
354
+ BooleanQuery *bq = (BooleanQuery *)self->data;
355
+ if (!bq->similarity) {
356
+ Similarity *sim = q_get_similarity(self, searcher);
357
+ bq->similarity = ALLOC(Similarity);
358
+ memcpy(bq->similarity, sim, sizeof(Similarity));
359
+ bq->similarity->coord = &bq_coord_disabled;
360
+ bq->similarity->destroy = &free;
361
+ }
362
+
363
+ return bq->similarity;
364
+ }
365
+
366
+ Query *bq_create(bool coord_disabled)
367
+ {
368
+ Query *self = q_create();
369
+ BooleanQuery *bq = ALLOC(BooleanQuery);
370
+ self->type = BOOLEAN_QUERY;
371
+ self->create_weight = &bw_create;
372
+ self->rewrite = &bq_rewrite;
373
+ self->extract_terms = &bq_extract_terms;
374
+ self->to_s = &bq_to_s;
375
+ self->destroy = &bq_destroy;
376
+ self->data = bq;
377
+ bq->coord_disabled = coord_disabled;
378
+ if (coord_disabled) {
379
+ self->get_similarity = &bq_get_similarity;
380
+ }
381
+ bq->max_clause_cnt = DEFAULT_MAX_CLAUSE_COUNT;
382
+ bq->clause_cnt = 0;
383
+ bq->clause_capa = BOOLEAN_CLAUSES_START_CAPA;
384
+ bq->clauses = ALLOC_N(BooleanClause *, BOOLEAN_CLAUSES_START_CAPA);
385
+ bq->similarity = NULL;
386
+
387
+ return self;
388
+ }
389
+
390
+ void bq_add_query(Query *self, Query *sub_query, unsigned int occur)
391
+ {
392
+ BooleanQuery *bq = (BooleanQuery *)self->data;
393
+ BooleanClause *bc = bc_create(sub_query, occur);
394
+ if (bq->clause_cnt >= bq->clause_capa) {
395
+ bq->clause_capa *= 2;
396
+ REALLOC_N(bq->clauses, BooleanClause *, bq->clause_capa);
397
+ }
398
+ if (bq->clause_cnt > bq->max_clause_cnt) {
399
+ eprintf(STATE_ERROR, "Too many clauses.");
400
+ }
401
+ bq->clauses[bq->clause_cnt] = bc;
402
+ bq->clause_cnt++;
403
+ }
404
+
405
+ /***************************************************************************
406
+ *
407
+ * BooleanScorer
408
+ *
409
+ ***************************************************************************/
410
+
411
+ /***************************************************************************
412
+ * Coordinator
413
+ ***************************************************************************/
414
+
415
+ Coordinator *coord_create(Similarity *similarity)
416
+ {
417
+ Coordinator *self = ALLOC(Coordinator);
418
+ ZEROSET(self, Coordinator, 1);
419
+ self->similarity = similarity;
420
+ return self;
421
+ }
422
+
423
+ Coordinator *coord_init(Coordinator *self)
424
+ {
425
+ int i;
426
+ self->coord_factors = ALLOC_N(float, self->max_coord + 1);
427
+
428
+ for (i = 0; i <= self->max_coord; i++) {
429
+ self->coord_factors[i] = sim_coord(self->similarity, i, self->max_coord);
430
+ }
431
+
432
+ return self;
433
+ }
434
+
435
+ /***************************************************************************
436
+ * DisjunctionSumScorer
437
+ ***************************************************************************/
438
+
439
+ float dssc_score(Scorer *self)
440
+ {
441
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
442
+ return dssc->cum_score;
443
+ }
444
+
445
+ void dssc_init_scorer_queue(DisjunctionSumScorer *dssc)
446
+ {
447
+ int i;
448
+ Scorer *sub_scorer;
449
+ PriorityQueue *pq = dssc->scorer_queue =
450
+ pq_create(dssc->ss_cnt, &scorer_doc_less_than);
451
+
452
+ for (i = 0; i < dssc->ss_cnt; i++) {
453
+ sub_scorer = dssc->sub_scorers[i];
454
+ if (sub_scorer->next(sub_scorer)) {
455
+ pq_insert(pq, sub_scorer);
456
+ }
457
+ }
458
+ }
459
+
460
+ bool dssc_advance_after_current(Scorer *self)
461
+ {
462
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
463
+ PriorityQueue *scorer_queue = dssc->scorer_queue;
464
+ Scorer *top;
465
+ while (true) { // repeat until minimum nr of matches
466
+ top = (Scorer *)pq_top(scorer_queue);
467
+ self->doc = top->doc;
468
+ dssc->cum_score = top->score(top);
469
+ dssc->num_matches = 1;
470
+ while (true) { // Until all subscorers are after self->hit.doc
471
+ if (top->next(top)) {
472
+ pq_down(scorer_queue);
473
+ } else {
474
+ pq_pop(scorer_queue);
475
+ if (scorer_queue->count < (dssc->min_num_matches - dssc->num_matches)) {
476
+ // Not enough subscorers left for a match on this document,
477
+ // and also no more chance of any further match.
478
+ return false;
479
+ }
480
+ if (scorer_queue->count == 0) {
481
+ break; // nothing more to advance, check for last match.
482
+ }
483
+ }
484
+ top = pq_top(scorer_queue);
485
+ if (top->doc != self->doc) {
486
+ break; // All remaining subscorers are after self->hit.doc.
487
+ } else {
488
+ dssc->cum_score += top->score(top);
489
+ dssc->num_matches++;
490
+ }
491
+ }
492
+
493
+ if (dssc->num_matches >= dssc->min_num_matches) {
494
+ return true;
495
+ } else if (scorer_queue->count < dssc->min_num_matches) {
496
+ return false;
497
+ }
498
+ }
499
+ }
500
+
501
+ bool dssc_next(Scorer *self)
502
+ {
503
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
504
+
505
+ if (dssc->scorer_queue == NULL) {
506
+ dssc_init_scorer_queue(dssc);
507
+ }
508
+
509
+ if (dssc->scorer_queue->count < dssc->min_num_matches) {
510
+ return false;
511
+ } else {
512
+ return dssc_advance_after_current(self);
513
+ }
514
+ }
515
+
516
+ bool dssc_skip_to(Scorer *self, int doc_num)
517
+ {
518
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
519
+ PriorityQueue *scorer_queue = dssc->scorer_queue;
520
+ Scorer *top;
521
+
522
+ if (scorer_queue == NULL) {
523
+ dssc_init_scorer_queue(dssc);
524
+ scorer_queue = dssc->scorer_queue;
525
+ }
526
+
527
+ if (scorer_queue->count < dssc->min_num_matches) {
528
+ return false;
529
+ }
530
+ if (doc_num <= self->doc) {
531
+ doc_num = self->doc + 1;
532
+ }
533
+ while (true) {
534
+ top = pq_top(scorer_queue);
535
+ if (top->doc >= doc_num) {
536
+ return dssc_advance_after_current(self);
537
+ } else if (top->skip_to(top, doc_num)) {
538
+ pq_down(scorer_queue);
539
+ } else {
540
+ pq_pop(scorer_queue);
541
+ if (scorer_queue->count < dssc->min_num_matches) {
542
+ return false;
543
+ }
544
+ }
545
+ }
546
+ }
547
+
548
+ Explanation *dssc_explain(Scorer *self, int doc_num)
549
+ {
550
+ int i;
551
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
552
+ Scorer *sub_scorer;
553
+ Explanation *e = expl_create(0.0,
554
+ epstrdup("At least %d of:", 20, dssc->min_num_matches));
555
+ for (i = 0; i < dssc->ss_cnt; i++) {
556
+ sub_scorer = dssc->sub_scorers[i];
557
+ expl_add_detail(e, sub_scorer->explain(sub_scorer, doc_num));
558
+ }
559
+ return e;
560
+ }
561
+
562
+ void dssc_destroy(void *p)
563
+ {
564
+ Scorer *self = (Scorer *)p;
565
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
566
+ int i;
567
+ for (i = 0; i < dssc->ss_cnt; i++) {
568
+ dssc->sub_scorers[i]->destroy(dssc->sub_scorers[i]);
569
+ }
570
+ pq_destroy(dssc->scorer_queue);
571
+ scorer_destroy(self);
572
+ }
573
+
574
+ Scorer *disjunction_sum_scorer_create(Scorer **sub_scorers, int ss_cnt,
575
+ int min_num_matches)
576
+ {
577
+ Scorer *self = scorer_create(NULL);
578
+ DisjunctionSumScorer *dssc = ALLOC(DisjunctionSumScorer);
579
+ self->data = dssc;
580
+ dssc->ss_cnt = ss_cnt;
581
+
582
+ // The document number of the current match.
583
+ self->doc = -1;
584
+ dssc->cum_score = -1.0;
585
+
586
+ // The number of subscorers that provide the current match.
587
+ dssc->num_matches = -1;
588
+ dssc->coordinator = NULL;
589
+
590
+ if (min_num_matches <= 0) {
591
+ eprintf(ARG_ERROR, "Minimum nr of matches must be positive");
592
+ }
593
+ if (ss_cnt <= 1) {
594
+ eprintf(ARG_ERROR, "There must be at least 2 sub_scorers");
595
+ }
596
+
597
+ dssc->min_num_matches = min_num_matches;
598
+ dssc->sub_scorers = sub_scorers;
599
+
600
+ dssc->scorer_queue = NULL;
601
+
602
+ self->score = &dssc_score;
603
+ self->next = &dssc_next;
604
+ self->skip_to = &dssc_skip_to;
605
+ self->explain = &dssc_explain;
606
+ self->destroy = &dssc_destroy;
607
+
608
+ return self;
609
+ }
610
+
611
+ float cdssc_score(Scorer *self)
612
+ {
613
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
614
+ dssc->coordinator->num_matches += dssc->num_matches;
615
+ return dssc->cum_score;
616
+ }
617
+
618
+ Scorer *counting_disjunction_sum_scorer_create(Coordinator *coordinator,
619
+ Scorer **sub_scorers, int ss_cnt, int min_num_matches)
620
+ {
621
+ Scorer *self = disjunction_sum_scorer_create(
622
+ sub_scorers, ss_cnt, min_num_matches);
623
+ DisjunctionSumScorer *dssc = (DisjunctionSumScorer *)self->data;
624
+ dssc->coordinator = coordinator;
625
+ self->score = &cdssc_score;
626
+ return self;
627
+ }
628
+
629
+ /***************************************************************************
630
+ * ConjunctionScorer
631
+ ***************************************************************************/
632
+
633
+ void csc_sort_scorers(ConjunctionScorer *csc)
634
+ {
635
+ qsort(csc->sub_scorers, csc->ss_cnt, sizeof(Scorer *), &scorer_doc_cmp);
636
+ csc->first = 0;
637
+ csc->last = csc->ss_cnt - 1;
638
+ }
639
+
640
+ void csc_init(Scorer *self, bool init_scorers)
641
+ {
642
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
643
+ Scorer *sub_scorer;
644
+ int i;
645
+ // compute coord factor
646
+ csc->coord = sim_coord(self->similarity, csc->ss_cnt, csc->ss_cnt);
647
+
648
+ csc->more = (csc->ss_cnt > 0);
649
+
650
+ if (init_scorers) {
651
+ // move each scorer to its first entry
652
+
653
+ for (i = 0; i < csc->ss_cnt; i++) {
654
+ sub_scorer = csc->sub_scorers[i];
655
+ if (!csc->more) break;
656
+ csc->more = sub_scorer->next(sub_scorer);
657
+ }
658
+ if (csc->more) csc_sort_scorers(csc);
659
+ }
660
+
661
+ csc->first_time = false;
662
+ }
663
+
664
+ void csc_add_scorer(ConjunctionScorer *csc, Scorer *scorer)
665
+ {
666
+ RECAPA(csc, ss_cnt, ss_capa, sub_scorers, Scorer *);
667
+ csc->sub_scorers[csc->ss_cnt++] = scorer;
668
+ }
669
+
670
+ float csc_score(Scorer *self)
671
+ {
672
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
673
+ Scorer *sub_scorer;
674
+ float score = 0.0; // sum scores
675
+ int i;
676
+ for (i = 0; i < csc->ss_cnt; i++) {
677
+ sub_scorer = csc->sub_scorers[i];
678
+ score += sub_scorer->score(sub_scorer);
679
+ }
680
+ score *= csc->coord;
681
+ return score;
682
+ }
683
+
684
+ bool csc_do_next(Scorer *self)
685
+ {
686
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
687
+ Scorer *first = csc->sub_scorers[csc->first];
688
+ Scorer *last = csc->sub_scorers[csc->last];
689
+
690
+ // find doc w/ all clauses
691
+ while (csc->more && (first->doc < last->doc)) {
692
+ csc->more = first->skip_to(first, last->doc); // skip first upto last
693
+ // move first to last
694
+ csc->last = csc->first;
695
+ last = first;
696
+ csc->first = (csc->first + 1) % csc->ss_cnt;
697
+ first = csc->sub_scorers[csc->first];
698
+ }
699
+ self->doc = first->doc;
700
+ return csc->more;
701
+ }
702
+
703
+ bool csc_next(Scorer *self)
704
+ {
705
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
706
+ Scorer *sub_scorer;
707
+ if (csc->first_time) {
708
+ csc_init(self, true);
709
+ } else if (csc->more) {
710
+ sub_scorer = csc->sub_scorers[csc->last];
711
+ csc->more = sub_scorer->next(sub_scorer); // trigger further scanning
712
+ }
713
+ return csc_do_next(self);
714
+ }
715
+
716
+ bool csc_skip_to(Scorer *self, int doc_num)
717
+ {
718
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
719
+ Scorer *sub_scorer;
720
+ int i;
721
+
722
+ if (csc->first_time) {
723
+ csc_init(self, true);
724
+ }
725
+
726
+ for (i = 0; i < csc->ss_cnt; i++) {
727
+ if (!csc->more) break;
728
+ sub_scorer = csc->sub_scorers[i];
729
+ csc->more = sub_scorer->skip_to(sub_scorer, doc_num);
730
+ }
731
+ if (csc->more) csc_sort_scorers(csc); // resort the scorers
732
+
733
+ return csc_do_next(self);
734
+ }
735
+
736
+ void csc_destroy(void *p)
737
+ {
738
+ Scorer *self = (Scorer *)p;
739
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
740
+ int i;
741
+ for (i = 0; i < csc->ss_cnt; i++) {
742
+ csc->sub_scorers[i]->destroy(csc->sub_scorers[i]);
743
+ }
744
+ free(csc->sub_scorers);
745
+ scorer_destroy(self);
746
+ }
747
+
748
+ Scorer *conjunction_scorer_create(Similarity *similarity)
749
+ {
750
+ Scorer *self = scorer_create(similarity);
751
+ ConjunctionScorer *csc = ALLOC(ConjunctionScorer);
752
+ ZEROSET(csc, ConjunctionScorer, 1);
753
+ self->data = csc;
754
+ csc->first_time = true;
755
+ csc->more = true;
756
+ csc->coordinator = NULL;
757
+
758
+ self->score = &csc_score;
759
+ self->next = &csc_next;
760
+ self->skip_to = &csc_skip_to;
761
+ self->destroy = &csc_destroy;
762
+
763
+ return self;
764
+ }
765
+
766
+ float ccsc_score(Scorer *self)
767
+ {
768
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
769
+
770
+ int doc;
771
+ if ((doc = self->doc) > csc->last_scored_doc) {
772
+ csc->last_scored_doc = doc;
773
+ csc->coordinator->num_matches += csc->ss_cnt;
774
+ }
775
+
776
+ return csc_score(self);
777
+ }
778
+
779
+ Scorer *counting_conjunction_sum_scorer_create(Coordinator *coordinator,
780
+ Scorer **sub_scorers, int ss_cnt)
781
+ {
782
+ Scorer *self = conjunction_scorer_create(sim_create_default());
783
+ ConjunctionScorer *csc = (ConjunctionScorer *)self->data;
784
+ csc->coordinator = coordinator;
785
+ csc->last_scored_doc = -1;
786
+ csc->sub_scorers = ALLOC_N(Scorer *, ss_cnt);
787
+ memcpy(csc->sub_scorers, sub_scorers, sizeof(Scorer *) * ss_cnt);
788
+ csc->ss_capa = csc->ss_cnt = ss_cnt;
789
+
790
+ self->score = &ccsc_score;
791
+
792
+ return self;
793
+ }
794
+
795
+ /***************************************************************************
796
+ * SingleMatchScorer
797
+ ***************************************************************************/
798
+
799
+ float smsc_score(Scorer *self)
800
+ {
801
+ SingleMatchScorer *smsc = (SingleMatchScorer *)self->data;
802
+ smsc->coordinator->num_matches++;
803
+ return smsc->scorer->score(smsc->scorer);
804
+ }
805
+
806
+ bool smsc_next(Scorer *self)
807
+ {
808
+ Scorer *scorer = ((SingleMatchScorer *)self->data)->scorer;
809
+ if (scorer->next(scorer)) {
810
+ self->doc = scorer->doc;
811
+ return true;
812
+ }
813
+ return false;
814
+ }
815
+
816
+ bool smsc_skip_to(Scorer *self, int doc_num)
817
+ {
818
+ Scorer *scorer = ((SingleMatchScorer *)self->data)->scorer;
819
+ if (scorer->skip_to(scorer, doc_num)) {
820
+ self->doc = scorer->doc;
821
+ return true;
822
+ }
823
+ return false;
824
+ }
825
+
826
+ Explanation *smsc_explain(Scorer *self, int doc_num)
827
+ {
828
+ Scorer *scorer = ((SingleMatchScorer *)self->data)->scorer;
829
+ return scorer->explain(scorer, doc_num);
830
+ }
831
+
832
+ void smsc_destroy(void *p)
833
+ {
834
+ Scorer *self = (Scorer *)p;
835
+ Scorer *scorer = ((SingleMatchScorer *)self->data)->scorer;
836
+ scorer->destroy(scorer);
837
+ scorer_destroy(self);
838
+ }
839
+
840
+ Scorer *single_match_scorer_create(Coordinator *coordinator, Scorer *scorer)
841
+ {
842
+ Scorer *self = scorer_create(scorer->similarity);
843
+ SingleMatchScorer *smsc = ALLOC(SingleMatchScorer);
844
+ smsc->coordinator = coordinator;
845
+ smsc->scorer = scorer;
846
+ self->data = smsc;
847
+
848
+ self->score = &smsc_score;
849
+ self->next = &smsc_next;
850
+ self->skip_to = &smsc_skip_to;
851
+ self->explain = &smsc_explain;
852
+ self->destroy = &smsc_destroy;
853
+ return self;
854
+ }
855
+
856
+ /***************************************************************************
857
+ * ReqOptSumScorer
858
+ ***************************************************************************/
859
+
860
+ float rossc_score(Scorer *self)
861
+ {
862
+ ReqOptSumScorer *rossc = (ReqOptSumScorer *)self->data;
863
+ Scorer *req_scorer = rossc->req_scorer;
864
+ Scorer *opt_scorer = rossc->opt_scorer;
865
+ int cur_doc = req_scorer->doc;
866
+ float req_score = req_scorer->score(req_scorer);
867
+
868
+ if (rossc->first_time_opt) {
869
+ rossc->first_time_opt = false;
870
+ if (! opt_scorer->skip_to(opt_scorer, cur_doc)) {
871
+ SCORER_NULLIFY(rossc->opt_scorer);
872
+ return req_score;
873
+ }
874
+ } else if (opt_scorer == NULL) {
875
+ return req_score;
876
+ } else if ((opt_scorer->doc < cur_doc) &&
877
+ ! opt_scorer->skip_to(opt_scorer, cur_doc)) {
878
+ SCORER_NULLIFY(rossc->opt_scorer);
879
+ return req_score;
880
+ }
881
+ // assert (@opt_scorer != nil) and (@opt_scorer.doc() >= cur_doc)
882
+ return (opt_scorer->doc == cur_doc)
883
+ ? req_score + opt_scorer->score(opt_scorer)
884
+ : req_score;
885
+ }
886
+
887
+ bool rossc_next(Scorer *self)
888
+ {
889
+ Scorer *req_scorer = ((ReqOptSumScorer *)self->data)->req_scorer;
890
+ if (req_scorer->next(req_scorer)) {
891
+ self->doc = req_scorer->doc;
892
+ return true;
893
+ }
894
+ return false;
895
+ }
896
+
897
+ bool rossc_skip_to(Scorer *self, int doc_num)
898
+ {
899
+ Scorer *req_scorer = ((ReqOptSumScorer *)self->data)->req_scorer;
900
+ if (req_scorer->skip_to(req_scorer, doc_num)) {
901
+ self->doc = req_scorer->doc;
902
+ return true;
903
+ }
904
+ return false;
905
+ }
906
+
907
+ Explanation *rossc_explain(Scorer *self, int doc_num)
908
+ {
909
+ ReqOptSumScorer *rossc = (ReqOptSumScorer *)self->data;
910
+ Scorer *req_scorer = rossc->req_scorer;
911
+ Scorer *opt_scorer = rossc->opt_scorer;
912
+
913
+ Explanation *e = expl_create(self->score(self), estrdup("required, optional:"));
914
+ expl_add_detail(e, req_scorer->explain(req_scorer, doc_num));
915
+ expl_add_detail(e, opt_scorer->explain(opt_scorer, doc_num));
916
+ return e;
917
+ }
918
+
919
+ void rossc_destroy(void *p)
920
+ {
921
+ Scorer *self = (Scorer *)p;
922
+ ReqOptSumScorer *rossc = (ReqOptSumScorer *)self->data;
923
+ if (rossc->req_scorer) rossc->req_scorer->destroy(rossc->req_scorer);
924
+ if (rossc->opt_scorer) rossc->opt_scorer->destroy(rossc->opt_scorer);
925
+ scorer_destroy(self);
926
+ }
927
+
928
+
929
+ Scorer *req_opt_sum_scorer_create(Scorer *req_scorer, Scorer *opt_scorer)
930
+ {
931
+ Scorer *self = scorer_create(NULL);
932
+ ReqOptSumScorer *rossc = ALLOC(ReqOptSumScorer);
933
+ self->data = rossc;
934
+ rossc->req_scorer = req_scorer;
935
+ rossc->opt_scorer = opt_scorer;
936
+ rossc->first_time_opt = true;
937
+
938
+ self->score = &rossc_score;
939
+ self->next = &rossc_next;
940
+ self->skip_to = &rossc_skip_to;
941
+ self->explain = &rossc_explain;
942
+ self->destroy = &rossc_destroy;
943
+
944
+ return self;
945
+ }
946
+
947
+ /***************************************************************************
948
+ * ReqExclScorer
949
+ ***************************************************************************/
950
+
951
+ bool rxsc_to_non_excluded(Scorer *self)
952
+ {
953
+ ReqExclScorer *rxsc = (ReqExclScorer *)self->data;
954
+ Scorer *req_scorer = rxsc->req_scorer;
955
+ Scorer *excl_scorer = rxsc->excl_scorer;
956
+ int excl_doc = excl_scorer->doc, req_doc;
957
+
958
+ do {
959
+ req_doc = req_scorer->doc; // may be excluded
960
+ if (req_doc < excl_doc) {
961
+ // req_scorer advanced to before excl_scorer, ie. not excluded
962
+ self->doc = req_doc;
963
+ return true;
964
+ } else if (req_doc > excl_doc) {
965
+ if (! excl_scorer->skip_to(excl_scorer, req_doc)) {
966
+ SCORER_NULLIFY(rxsc->excl_scorer); // exhausted, no more exclusions
967
+ self->doc = req_doc;
968
+ return true;
969
+ }
970
+ excl_doc = excl_scorer->doc;
971
+ if (excl_doc > req_doc) {
972
+ self->doc = req_doc;
973
+ return true; // not excluded
974
+ }
975
+ }
976
+ } while (req_scorer->next(req_scorer));
977
+ SCORER_NULLIFY(rxsc->req_scorer); // exhausted, nothing left
978
+ return false;
979
+ }
980
+
981
+ bool rxsc_next(Scorer *self)
982
+ {
983
+ ReqExclScorer *rxsc = (ReqExclScorer *)self->data;
984
+ Scorer *req_scorer = rxsc->req_scorer;
985
+ Scorer *excl_scorer = rxsc->excl_scorer;
986
+
987
+ if (rxsc->first_time) {
988
+ if (! excl_scorer->next(excl_scorer)) {
989
+ SCORER_NULLIFY(rxsc->excl_scorer); // exhausted at start
990
+ excl_scorer = NULL;
991
+ }
992
+ rxsc->first_time = false;
993
+ }
994
+ if (req_scorer == NULL) {
995
+ return false;
996
+ }
997
+ if (! req_scorer->next(req_scorer)) {
998
+ SCORER_NULLIFY(rxsc->req_scorer); // exhausted, nothing left
999
+ return false;
1000
+ }
1001
+ if (excl_scorer == NULL) {
1002
+ self->doc = req_scorer->doc;
1003
+ return true; // req_scorer->next() already returned true
1004
+ }
1005
+ return rxsc_to_non_excluded(self);
1006
+ }
1007
+
1008
+ bool rxsc_skip_to(Scorer *self, int doc_num)
1009
+ {
1010
+ ReqExclScorer *rxsc = (ReqExclScorer *)self->data;
1011
+ Scorer *req_scorer = rxsc->req_scorer;
1012
+ Scorer *excl_scorer = rxsc->excl_scorer;
1013
+
1014
+ if (rxsc->first_time) {
1015
+ rxsc->first_time = false;
1016
+ if (! excl_scorer->skip_to(excl_scorer, doc_num)) {
1017
+ SCORER_NULLIFY(rxsc->excl_scorer); // exhausted
1018
+ excl_scorer = NULL;
1019
+ }
1020
+ }
1021
+ if (req_scorer == NULL) {
1022
+ return false;
1023
+ }
1024
+ if (excl_scorer == NULL) {
1025
+ if (req_scorer->skip_to(req_scorer, doc_num)) {
1026
+ self->doc = req_scorer->doc;
1027
+ return true;
1028
+ }
1029
+ return false;
1030
+ }
1031
+ if (! req_scorer->skip_to(req_scorer, doc_num)) {
1032
+ SCORER_NULLIFY(rxsc->req_scorer);
1033
+ return false;
1034
+ }
1035
+ return rxsc_to_non_excluded(self);
1036
+ }
1037
+
1038
+ float rxsc_score(Scorer *self)
1039
+ {
1040
+ Scorer *req_scorer = ((ReqExclScorer *)self->data)->req_scorer;
1041
+ return req_scorer->score(req_scorer);
1042
+ }
1043
+
1044
+ Explanation *rxsc_explain(Scorer *self, int doc_num)
1045
+ {
1046
+ ReqExclScorer *rxsc = (ReqExclScorer *)self->data;
1047
+ Scorer *req_scorer = rxsc->req_scorer;
1048
+ Scorer *excl_scorer = rxsc->excl_scorer;
1049
+
1050
+ Explanation *e;
1051
+ if (excl_scorer->skip_to(excl_scorer, doc_num) && excl_scorer->doc == doc_num) {
1052
+ e = expl_create(0.0, estrdup("excluded:"));
1053
+ } else {
1054
+ e = expl_create(0.0, estrdup("not excluded:"));
1055
+ expl_add_detail(e, req_scorer->explain(req_scorer, doc_num));
1056
+ }
1057
+ return e;
1058
+ }
1059
+
1060
+ void rxsc_destroy(void *p)
1061
+ {
1062
+ Scorer *self = (Scorer *)p;
1063
+ ReqExclScorer *rxsc = (ReqExclScorer *)self->data;
1064
+ if (rxsc->req_scorer) rxsc->req_scorer->destroy(rxsc->req_scorer);
1065
+ if (rxsc->excl_scorer) rxsc->excl_scorer->destroy(rxsc->excl_scorer);
1066
+ scorer_destroy(self);
1067
+ }
1068
+
1069
+ Scorer *req_excl_scorer_create(Scorer *req_scorer, Scorer *excl_scorer)
1070
+ {
1071
+ Scorer *self = scorer_create(NULL);
1072
+ ReqExclScorer *rxsc = ALLOC(ReqExclScorer);
1073
+ self->data = rxsc;
1074
+ rxsc->req_scorer = req_scorer;
1075
+ rxsc->excl_scorer = excl_scorer;
1076
+ rxsc->first_time = true;
1077
+
1078
+ self->score = &rxsc_score;
1079
+ self->next = &rxsc_next;
1080
+ self->skip_to = &rxsc_skip_to;
1081
+ self->explain = &rxsc_explain;
1082
+ self->destroy = &rxsc_destroy;
1083
+
1084
+ return self;
1085
+ }
1086
+
1087
+ /***************************************************************************
1088
+ * NonMatchScorer
1089
+ ***************************************************************************/
1090
+
1091
+ float nmsc_score(Scorer *self)
1092
+ {
1093
+ return 0.0;
1094
+ }
1095
+
1096
+ bool nmsc_next(Scorer *self)
1097
+ {
1098
+ return false;
1099
+ }
1100
+
1101
+ bool nmsc_skip_to(Scorer *self, int doc_num)
1102
+ {
1103
+ return false;
1104
+ }
1105
+
1106
+ Explanation *nmsc_explain(Scorer *self, int doc_num)
1107
+ {
1108
+ return expl_create(0.0, estrdup("No documents matched"));
1109
+ }
1110
+
1111
+ Scorer *non_matching_scorer_create()
1112
+ {
1113
+ Scorer *self = scorer_create(NULL);
1114
+ self->score = &nmsc_score;
1115
+ self->next = &nmsc_next;
1116
+ self->skip_to = &nmsc_skip_to;
1117
+ self->explain = &nmsc_explain;
1118
+
1119
+ return self;
1120
+ }
1121
+
1122
+
1123
+ /***************************************************************************
1124
+ * BooleanScorer
1125
+ ***************************************************************************/
1126
+
1127
+ Scorer *counting_sum_scorer_create3(BooleanScorer *bsc, Scorer *req_scorer,
1128
+ Scorer *opt_scorer)
1129
+ {
1130
+ if (bsc->ps_cnt == 0) { // no prohibited
1131
+ return req_opt_sum_scorer_create(req_scorer, opt_scorer);
1132
+ } else if (bsc->ps_cnt == 1) { // 1 prohibited
1133
+ return req_opt_sum_scorer_create(
1134
+ req_excl_scorer_create(req_scorer, bsc->prohibited_scorers[0]),
1135
+ opt_scorer);
1136
+ } else { // more prohibited
1137
+ return req_opt_sum_scorer_create(
1138
+ req_excl_scorer_create(req_scorer,
1139
+ disjunction_sum_scorer_create(bsc->prohibited_scorers, bsc->ps_cnt, 1)),
1140
+ opt_scorer);
1141
+ }
1142
+ }
1143
+
1144
+ Scorer *counting_sum_scorer_create2(BooleanScorer *bsc, Scorer *req_scorer,
1145
+ Scorer **optional_scorers, int os_cnt)
1146
+ {
1147
+ if (os_cnt == 0) {
1148
+ if (bsc->ps_cnt == 0) {
1149
+ return req_scorer;
1150
+ } else if (bsc->ps_cnt == 1) {
1151
+ return req_excl_scorer_create(req_scorer,
1152
+ bsc->prohibited_scorers[0]);
1153
+ } else { // no optional, more than 1 prohibited
1154
+ return req_excl_scorer_create(req_scorer,
1155
+ disjunction_sum_scorer_create(bsc->prohibited_scorers, bsc->ps_cnt, 1));
1156
+ }
1157
+ } else if (os_cnt == 1) {
1158
+ return counting_sum_scorer_create3(
1159
+ bsc,
1160
+ req_scorer,
1161
+ single_match_scorer_create(bsc->coordinator, optional_scorers[0]));
1162
+ } else { // more optional
1163
+ return counting_sum_scorer_create3(
1164
+ bsc,
1165
+ req_scorer,
1166
+ counting_disjunction_sum_scorer_create(bsc->coordinator,
1167
+ optional_scorers, os_cnt, 1));
1168
+ }
1169
+ }
1170
+
1171
+ Scorer *counting_sum_scorer_create(BooleanScorer *bsc)
1172
+ {
1173
+ if (bsc->rs_cnt == 0) {
1174
+ if (bsc->os_cnt == 0) {
1175
+ int i;
1176
+ // only prohibited_scorers so free them and return non_matching scorer
1177
+ for (i = 0; i < bsc->ps_cnt; i++) {
1178
+ bsc->prohibited_scorers[i]->destroy(bsc->prohibited_scorers[i]);
1179
+ }
1180
+ return non_matching_scorer_create();
1181
+ } else if (bsc->os_cnt == 1) {
1182
+ return counting_sum_scorer_create2( // the only optional scorer is required
1183
+ bsc,
1184
+ single_match_scorer_create(bsc->coordinator, bsc->optional_scorers[0]),
1185
+ NULL, 0); // no optional scorers left
1186
+ } else { // more than 1 @optional_scorers, no required scorers
1187
+ return counting_sum_scorer_create2( // at least one optional scorer is required
1188
+ bsc,
1189
+ counting_disjunction_sum_scorer_create(bsc->coordinator,
1190
+ bsc->optional_scorers, bsc->os_cnt, 1),
1191
+ NULL, 0); // no optional scorers left
1192
+ }
1193
+ } else if (bsc->rs_cnt == 1) { // 1 required
1194
+ return counting_sum_scorer_create2(
1195
+ bsc,
1196
+ single_match_scorer_create(bsc->coordinator, bsc->required_scorers[0]),
1197
+ bsc->optional_scorers, bsc->os_cnt);
1198
+ } else {// more required scorers
1199
+ return counting_sum_scorer_create2(
1200
+ bsc,
1201
+ counting_conjunction_sum_scorer_create(bsc->coordinator,
1202
+ bsc->required_scorers, bsc->rs_cnt),
1203
+ bsc->optional_scorers, bsc->os_cnt);
1204
+ }
1205
+ }
1206
+
1207
+ void bsc_init_counting_sum_scorer(BooleanScorer *bsc)
1208
+ {
1209
+ coord_init(bsc->coordinator);
1210
+ bsc->counting_sum_scorer = counting_sum_scorer_create(bsc);
1211
+ }
1212
+
1213
+ void bsc_add_scorer(Scorer *self, Scorer *scorer, unsigned int occur)
1214
+ {
1215
+ BooleanScorer *bsc = (BooleanScorer *)self->data;
1216
+ if (occur != BC_MUST_NOT) {
1217
+ bsc->coordinator->max_coord++;
1218
+ }
1219
+
1220
+ switch (occur) {
1221
+ case BC_MUST:
1222
+ RECAPA(bsc, rs_cnt, rs_capa, required_scorers, Scorer *);
1223
+ bsc->required_scorers[bsc->rs_cnt++] = scorer;
1224
+ break;
1225
+ case BC_SHOULD:
1226
+ RECAPA(bsc, os_cnt, os_capa, optional_scorers, Scorer *);
1227
+ bsc->optional_scorers[bsc->os_cnt++] = scorer;
1228
+ break;
1229
+ case BC_MUST_NOT:
1230
+ RECAPA(bsc, ps_cnt, ps_capa, prohibited_scorers, Scorer *);
1231
+ bsc->prohibited_scorers[bsc->ps_cnt++] = scorer;
1232
+ break;
1233
+ default:
1234
+ eprintf(ARG_ERROR, "Unknown value for occur <%d>\n", occur);
1235
+ }
1236
+ }
1237
+
1238
+ float bsc_score(Scorer *self)
1239
+ {
1240
+ BooleanScorer *bsc = (BooleanScorer *)self->data;
1241
+ Coordinator *coord = bsc->coordinator;
1242
+ float sum;
1243
+ coord->num_matches = 0;
1244
+ sum = bsc->counting_sum_scorer->score(bsc->counting_sum_scorer);
1245
+ return sum * coord->coord_factors[coord->num_matches];
1246
+ }
1247
+
1248
+ bool bsc_next(Scorer *self)
1249
+ {
1250
+ BooleanScorer *bsc = (BooleanScorer *)self->data;
1251
+
1252
+ if (!bsc->counting_sum_scorer) {
1253
+ bsc_init_counting_sum_scorer(bsc);
1254
+ }
1255
+ if (bsc->counting_sum_scorer->next(bsc->counting_sum_scorer)) {
1256
+ self->doc = bsc->counting_sum_scorer->doc;
1257
+ return true;
1258
+ } else {
1259
+ return false;
1260
+ }
1261
+ }
1262
+
1263
+ bool bsc_skip_to(Scorer *self, int doc_num)
1264
+ {
1265
+ BooleanScorer *bsc = (BooleanScorer *)self->data;
1266
+
1267
+ if (!bsc->counting_sum_scorer) {
1268
+ bsc_init_counting_sum_scorer(bsc);
1269
+ }
1270
+ if (bsc->counting_sum_scorer->skip_to(bsc->counting_sum_scorer, doc_num)) {
1271
+ self->doc = bsc->counting_sum_scorer->doc;
1272
+ return true;
1273
+ } else {
1274
+ return false;
1275
+ }
1276
+ }
1277
+
1278
+ void bsc_destroy(void *p)
1279
+ {
1280
+ Scorer *self = (Scorer *)p;
1281
+ BooleanScorer *bsc = (BooleanScorer *)self->data;
1282
+ Coordinator *coord = bsc->coordinator;
1283
+
1284
+ free(coord->coord_factors);
1285
+ free(coord);
1286
+
1287
+ if (bsc->counting_sum_scorer) {
1288
+ bsc->counting_sum_scorer->destroy(bsc->counting_sum_scorer);
1289
+ } else {
1290
+ int i;
1291
+ for (i = 0; i < bsc->rs_cnt; i++) {
1292
+ bsc->required_scorers[i]->destroy(bsc->required_scorers[i]);
1293
+ }
1294
+
1295
+ for (i = 0; i < bsc->os_cnt; i++) {
1296
+ bsc->optional_scorers[i]->destroy(bsc->optional_scorers[i]);
1297
+ }
1298
+
1299
+ for (i = 0; i < bsc->ps_cnt; i++) {
1300
+ bsc->prohibited_scorers[i]->destroy(bsc->prohibited_scorers[i]);
1301
+ }
1302
+ }
1303
+ free(bsc->required_scorers);
1304
+ free(bsc->optional_scorers);
1305
+ free(bsc->prohibited_scorers);
1306
+ scorer_destroy(self);
1307
+ }
1308
+
1309
+ Explanation *bsc_explain(Scorer *self, int doc_num)
1310
+ {
1311
+ return expl_create(0.0, estrdup("This explanation is not supported"));
1312
+ }
1313
+
1314
+ Scorer *bsc_create(Similarity *similarity)
1315
+ {
1316
+ Scorer *self = scorer_create(similarity);
1317
+ BooleanScorer *bsc = ALLOC(BooleanScorer);
1318
+ ZEROSET(bsc, BooleanScorer, 1);
1319
+ bsc->coordinator = coord_create(similarity);
1320
+ bsc->counting_sum_scorer = NULL;
1321
+ self->data = bsc;
1322
+
1323
+ self->score = &bsc_score;
1324
+ self->next = &bsc_next;
1325
+ self->skip_to = &bsc_skip_to;
1326
+ self->explain = &bsc_explain;
1327
+ self->destroy = &bsc_destroy;
1328
+ return self;
1329
+ }
1330
+
1331
+