Make zetjs use INDEX_SUMMARISE_TAG
[node_zettair.git] / zetjs.cpp
1 #include <node.h>
2 #include <node_object_wrap.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <zettair/def.h>
6 #include <zettair/index.h>
7
8 namespace zetjs {
9
10 static v8::Local<v8::Object> index_result_to_object(
11   v8::Isolate* isolate,
12   struct index_result* result
13 );
14 static v8::Local<v8::Object> index_results_to_object(
15   v8::Isolate* isolate,
16   struct index_result* results,
17   unsigned int num_results,
18   double/*unsigned long int*/ total_results
19 );
20
21 class IndexObject : public node::ObjectWrap {
22 public:
23   static void Init(v8::Local<v8::Object> exports);
24
25 private:
26   struct index* idx;
27
28   explicit IndexObject(struct index* idx0);
29   ~IndexObject();
30
31   static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
32   static void Search(const v8::FunctionCallbackInfo<v8::Value>& args);
33   static v8::Persistent<v8::Function> constructor;
34 };
35
36 v8::Persistent<v8::Function> IndexObject::constructor;
37
38 IndexObject::IndexObject(struct index* idx0) : idx(idx0) {
39 }
40
41 IndexObject::~IndexObject() {
42 }
43
44 void IndexObject::Init(v8::Local<v8::Object> exports) {
45   v8::Isolate* isolate = exports->GetIsolate();
46
47   // Prepare constructor template
48   v8::Local<v8::FunctionTemplate> tpl =
49     v8::FunctionTemplate::New(isolate, New);
50   tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Index"));
51   tpl->InstanceTemplate()->SetInternalFieldCount(1);
52
53   // Prototype
54   NODE_SET_PROTOTYPE_METHOD(tpl, "search", Search);
55
56   constructor.Reset(isolate, tpl->GetFunction());
57   exports->Set(
58     v8::String::NewFromUtf8(isolate, "Index"),
59     tpl->GetFunction()
60   );
61 }
62
63 void IndexObject::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
64   v8::Isolate* isolate = args.GetIsolate();
65
66   if (args.IsConstructCall()) {
67     // Invoked as constructor: `new Index(...)`
68     v8::String::Utf8Value prefix(args[0]);
69     struct index* idx = NULL;;
70     int lopts = INDEX_LOAD_NOOPT;
71     struct index_load_opt lopt;
72
73     lopts |= INDEX_LOAD_IGNORE_VERSION;  /* quick hack */
74     if (
75       (
76         idx = index_load(
77           args[0]->IsString() ? *prefix : "index",
78           MEMORY_DEFAULT,
79           lopts,
80           &lopt
81         )
82       ) == NULL
83     ) {
84       isolate->ThrowException(
85         v8::String::NewFromUtf8(isolate, "Unable to load index")
86       );
87       return;
88     }
89     IndexObject* obj = new IndexObject(idx);
90     obj->Wrap(args.This());
91     args.GetReturnValue().Set(args.This());
92   } else {
93     // Invoked as plain function `Index(...)`, turn into construct call.
94     const int argc = 1;
95     v8::Local<v8::Value> argv[argc] = { args[0] };
96     v8::Local<v8::Context> context = isolate->GetCurrentContext();
97     v8::Local<v8::Function> cons =
98       v8::Local<v8::Function>::New(isolate, constructor);
99     v8::Local<v8::Object> result =
100       cons->NewInstance(context, argc, argv).ToLocalChecked();
101     args.GetReturnValue().Set(result);
102   }
103 }
104
105 void IndexObject::Search(const v8::FunctionCallbackInfo<v8::Value>& args) {
106   v8::Isolate* isolate = args.GetIsolate();
107
108   v8::String::Utf8Value query(args[0]);
109   v8::String::Utf8Value optType(args[3]);
110   v8::Local<v8::Array> optArgsTuple;
111   if (args[4]->IsArray())
112     optArgsTuple = args[4].As<v8::Array>();
113   unsigned long startdoc = args[1]->IntegerValue();
114   unsigned long len = args[2]->IntegerValue();
115   IndexObject* Index = node::ObjectWrap::Unwrap<IndexObject>(args.Holder());
116   struct index_result* result;
117   unsigned int results;
118   double/*unsigned long int*/ total_results;
119   int est;
120   unsigned int accumulator_limit = args[5]->Uint32Value();
121   int opts = INDEX_SEARCH_NOOPT;
122   struct index_search_opt opt;
123
124   opt.u.okapi_k3.k1 = 1.2;
125   opt.u.okapi_k3.k3 = 1e10;
126   opt.u.okapi_k3.b = 0.75;
127
128   if (
129     (result = (struct index_result*)malloc(sizeof(*result) * len)) == NULL
130   ) {
131     isolate->ThrowException(
132       v8::String::NewFromUtf8(isolate, "Unable to allocate results")
133     );
134     return;
135   }
136   if (args[3]->IsString()) {
137     if (strcmp(*optType, "COSINE") == 0) {
138       opts = INDEX_SEARCH_COSINE_RANK;
139     } else if (strcmp(*optType, "OKAPI") == 0) {
140       opts = INDEX_SEARCH_OKAPI_RANK;
141     } else if (strcmp(*optType, "OKAPI_K3") == 0) {
142       if (optArgsTuple.IsEmpty()) {
143         isolate->ThrowException(
144           v8::String::NewFromUtf8(isolate, "Must supply args to search type")
145         );
146         free(result);
147         return;
148       }
149       opts = INDEX_SEARCH_OKAPI_RANK;
150       opt.u.okapi_k3.k1 = optArgsTuple->Get(0)->IntegerValue();
151       opt.u.okapi_k3.k3 = optArgsTuple->Get(1)->IntegerValue();
152       opt.u.okapi_k3.b = optArgsTuple->Get(2)->IntegerValue();
153     } else if (strcmp(*optType, "HAWKAPI") == 0) {
154       if (optArgsTuple.IsEmpty()) {
155         isolate->ThrowException(
156           v8::String::NewFromUtf8(isolate, "Must supply args to search type")
157         );
158         free(result);
159         return;
160       }
161       opts = INDEX_SEARCH_HAWKAPI_RANK;
162       opt.u.hawkapi.alpha = optArgsTuple->Get(0)->IntegerValue();
163       opt.u.hawkapi.k3 = optArgsTuple->Get(1)->IntegerValue();
164     } else if (strcmp(*optType, "DIRICHLET") == 0) {
165       opts = INDEX_SEARCH_DIRICHLET_RANK;
166       if (optArgsTuple.IsEmpty() || optArgsTuple->Length() == 0)
167         opt.u.dirichlet.mu = 2500.0;
168       else
169         opt.u.dirichlet.mu = optArgsTuple->Get(0)->NumberValue();
170     } else {
171       isolate->ThrowException(
172         v8::String::NewFromUtf8(isolate, "Unknown search type")
173       );
174       free(result);
175       return;
176     }
177   }
178   if (accumulator_limit != 0) {
179     opts |= INDEX_SEARCH_ACCUMULATOR_LIMIT;
180     opt.accumulator_limit = accumulator_limit;
181   }
182 #if 1 /* Nick */
183  opts |= INDEX_SEARCH_SUMMARY_TYPE;
184  opt.summary_type = INDEX_SUMMARISE_TAG;
185 #endif
186   if (!index_search(Index->idx, *query, startdoc, len,
187       result, &results, &total_results, &est, opts, &opt)) {
188     char err_buf[1024];
189     snprintf(err_buf, 1024, "Unable to perform search for query '%s'; "
190        "system error is '%s'\n", *query, strerror(errno));
191     isolate->ThrowException(
192       v8::String::NewFromUtf8(isolate, err_buf)
193     );
194     free(result);
195     return;
196   }
197   args.GetReturnValue().Set(
198     index_results_to_object(isolate, result, results, total_results)
199   );
200   free(result);
201   return;
202 }
203
204 static v8::Local<v8::Object> index_result_to_object(
205   v8::Isolate* isolate,
206   struct index_result* result
207 ) {
208   v8::EscapableHandleScope handle_scope(isolate);
209   v8::Local<v8::Object> result_object = v8::Object::New(isolate);
210   result_object->Set(
211     v8::String::NewFromUtf8(isolate, "docno"),
212     v8::Integer::New(isolate, result->docno)
213   );
214   result_object->Set(
215     v8::String::NewFromUtf8(isolate, "score"),
216     v8::Number::New(isolate, result->score)
217   );
218   result_object->Set(
219     v8::String::NewFromUtf8(isolate, "summary"),
220     v8::String::NewFromUtf8(isolate, result->summary)
221   );
222   result_object->Set(
223     v8::String::NewFromUtf8(isolate, "title"),
224     v8::String::NewFromUtf8(isolate, result->title)
225   );
226   result_object->Set(
227     v8::String::NewFromUtf8(isolate, "auxiliary"),
228     v8::String::NewFromUtf8(isolate, result->auxilliary)
229   );
230   return handle_scope.Escape(result_object);
231 }
232
233 static v8::Local<v8::Object> index_results_to_object(
234   v8::Isolate* isolate,
235   struct index_result* results,
236   unsigned int num_results,
237   double/*unsigned long int*/ total_results
238 ) {
239   v8::EscapableHandleScope handle_scope(isolate);
240   v8::Local<v8::Object> results_object = v8::Object::New(isolate);
241   v8::Local<v8::Array> results_array = v8::Array::New(isolate, num_results);
242   for (unsigned int i = 0; i < num_results; ++i)
243     results_array->Set(i, index_result_to_object(isolate, results + i));
244   results_object->Set(
245     v8::String::NewFromUtf8(isolate, "results"),
246     results_array
247   );
248   results_object->Set(
249     v8::String::NewFromUtf8(isolate, "total_results"),
250     v8::Number::New(isolate, total_results)
251   );
252   return handle_scope.Escape(results_object);
253 }
254
255 void InitAll(v8::Local<v8::Object> exports) {
256   IndexObject::Init(exports);
257 }
258
259 NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
260
261 }