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