2 * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
3 * SPDX-License-Identifier: MIT
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 #include <node_object_wrap.h>
28 #include <zettair/def.h>
29 #include <zettair/index.h>
33 static v8::Local<v8::Object> index_result_to_object(
35 struct index_result* result
37 static v8::Local<v8::Object> index_results_to_object(
39 struct index_result* results,
40 unsigned int num_results,
41 double/*unsigned long int*/ total_results
44 class IndexObject : public node::ObjectWrap {
46 static void Init(v8::Local<v8::Object> exports);
51 explicit IndexObject(struct index* idx0);
54 static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
55 static void Search(const v8::FunctionCallbackInfo<v8::Value>& args);
56 static v8::Persistent<v8::Function> constructor;
59 v8::Persistent<v8::Function> IndexObject::constructor;
61 IndexObject::IndexObject(struct index* idx0) : idx(idx0) {
64 IndexObject::~IndexObject() {
67 void IndexObject::Init(v8::Local<v8::Object> exports) {
68 v8::Isolate* isolate = exports->GetIsolate();
70 // Prepare constructor template
71 v8::Local<v8::FunctionTemplate> tpl =
72 v8::FunctionTemplate::New(isolate, New);
73 tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Index"));
74 tpl->InstanceTemplate()->SetInternalFieldCount(1);
77 NODE_SET_PROTOTYPE_METHOD(tpl, "search", Search);
79 constructor.Reset(isolate, tpl->GetFunction());
81 v8::String::NewFromUtf8(isolate, "Index"),
86 void IndexObject::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
87 v8::Isolate* isolate = args.GetIsolate();
89 if (args.IsConstructCall()) {
90 // Invoked as constructor: `new Index(...)`
91 v8::String::Utf8Value prefix(args[0]);
92 struct index* idx = NULL;;
93 int lopts = INDEX_LOAD_NOOPT;
94 struct index_load_opt lopt;
96 lopts |= INDEX_LOAD_IGNORE_VERSION; /* quick hack */
100 args[0]->IsString() ? *prefix : "index",
107 isolate->ThrowException(
108 v8::String::NewFromUtf8(isolate, "Unable to load index")
112 IndexObject* obj = new IndexObject(idx);
113 obj->Wrap(args.This());
114 args.GetReturnValue().Set(args.This());
116 // Invoked as plain function `Index(...)`, turn into construct call.
118 v8::Local<v8::Value> argv[argc] = { args[0] };
119 v8::Local<v8::Context> context = isolate->GetCurrentContext();
120 v8::Local<v8::Function> cons =
121 v8::Local<v8::Function>::New(isolate, constructor);
122 v8::Local<v8::Object> result =
123 cons->NewInstance(context, argc, argv).ToLocalChecked();
124 args.GetReturnValue().Set(result);
128 void IndexObject::Search(const v8::FunctionCallbackInfo<v8::Value>& args) {
129 v8::Isolate* isolate = args.GetIsolate();
131 v8::String::Utf8Value query(args[0]);
132 v8::String::Utf8Value optType(args[3]);
133 v8::Local<v8::Array> optArgsTuple;
134 if (args[4]->IsArray())
135 optArgsTuple = args[4].As<v8::Array>();
136 unsigned long startdoc = args[1]->IntegerValue();
137 unsigned long len = args[2]->IntegerValue();
138 IndexObject* Index = node::ObjectWrap::Unwrap<IndexObject>(args.Holder());
139 struct index_result* result;
140 unsigned int results;
141 double/*unsigned long int*/ total_results;
143 unsigned int accumulator_limit = args[5]->Uint32Value();
144 int opts = INDEX_SEARCH_NOOPT;
145 struct index_search_opt opt;
147 opt.u.okapi_k3.k1 = 1.2;
148 opt.u.okapi_k3.k3 = 1e10;
149 opt.u.okapi_k3.b = 0.75;
152 (result = (struct index_result*)malloc(sizeof(*result) * len)) == NULL
154 isolate->ThrowException(
155 v8::String::NewFromUtf8(isolate, "Unable to allocate results")
159 if (args[3]->IsString()) {
160 if (strcmp(*optType, "COSINE") == 0) {
161 opts = INDEX_SEARCH_COSINE_RANK;
162 } else if (strcmp(*optType, "OKAPI") == 0) {
163 opts = INDEX_SEARCH_OKAPI_RANK;
164 } else if (strcmp(*optType, "OKAPI_K3") == 0) {
165 if (optArgsTuple.IsEmpty()) {
166 isolate->ThrowException(
167 v8::String::NewFromUtf8(isolate, "Must supply args to search type")
172 opts = INDEX_SEARCH_OKAPI_RANK;
173 opt.u.okapi_k3.k1 = optArgsTuple->Get(0)->IntegerValue();
174 opt.u.okapi_k3.k3 = optArgsTuple->Get(1)->IntegerValue();
175 opt.u.okapi_k3.b = optArgsTuple->Get(2)->IntegerValue();
176 } else if (strcmp(*optType, "HAWKAPI") == 0) {
177 if (optArgsTuple.IsEmpty()) {
178 isolate->ThrowException(
179 v8::String::NewFromUtf8(isolate, "Must supply args to search type")
184 opts = INDEX_SEARCH_HAWKAPI_RANK;
185 opt.u.hawkapi.alpha = optArgsTuple->Get(0)->IntegerValue();
186 opt.u.hawkapi.k3 = optArgsTuple->Get(1)->IntegerValue();
187 } else if (strcmp(*optType, "DIRICHLET") == 0) {
188 opts = INDEX_SEARCH_DIRICHLET_RANK;
189 if (optArgsTuple.IsEmpty() || optArgsTuple->Length() == 0)
190 opt.u.dirichlet.mu = 2500.0;
192 opt.u.dirichlet.mu = optArgsTuple->Get(0)->NumberValue();
194 isolate->ThrowException(
195 v8::String::NewFromUtf8(isolate, "Unknown search type")
201 if (accumulator_limit != 0) {
202 opts |= INDEX_SEARCH_ACCUMULATOR_LIMIT;
203 opt.accumulator_limit = accumulator_limit;
206 opts |= INDEX_SEARCH_SUMMARY_TYPE;
207 opt.summary_type = INDEX_SUMMARISE_TAG;
209 if (!index_search(Index->idx, *query, startdoc, len,
210 result, &results, &total_results, &est, opts, &opt)) {
212 snprintf(err_buf, 1024, "Unable to perform search for query '%s'; "
213 "system error is '%s'\n", *query, strerror(errno));
214 isolate->ThrowException(
215 v8::String::NewFromUtf8(isolate, err_buf)
220 args.GetReturnValue().Set(
221 index_results_to_object(isolate, result, results, total_results)
227 static v8::Local<v8::Object> index_result_to_object(
228 v8::Isolate* isolate,
229 struct index_result* result
231 v8::EscapableHandleScope handle_scope(isolate);
232 v8::Local<v8::Object> result_object = v8::Object::New(isolate);
234 v8::String::NewFromUtf8(isolate, "docno"),
235 v8::Integer::New(isolate, result->docno)
238 v8::String::NewFromUtf8(isolate, "score"),
239 v8::Number::New(isolate, result->score)
242 v8::String::NewFromUtf8(isolate, "summary"),
243 v8::String::NewFromUtf8(isolate, result->summary)
246 v8::String::NewFromUtf8(isolate, "title"),
247 v8::String::NewFromUtf8(isolate, result->title)
250 v8::String::NewFromUtf8(isolate, "auxiliary"),
251 v8::String::NewFromUtf8(isolate, result->auxilliary)
253 return handle_scope.Escape(result_object);
256 static v8::Local<v8::Object> index_results_to_object(
257 v8::Isolate* isolate,
258 struct index_result* results,
259 unsigned int num_results,
260 double/*unsigned long int*/ total_results
262 v8::EscapableHandleScope handle_scope(isolate);
263 v8::Local<v8::Object> results_object = v8::Object::New(isolate);
264 v8::Local<v8::Array> results_array = v8::Array::New(isolate, num_results);
265 for (unsigned int i = 0; i < num_results; ++i)
266 results_array->Set(i, index_result_to_object(isolate, results + i));
268 v8::String::NewFromUtf8(isolate, "results"),
272 v8::String::NewFromUtf8(isolate, "total_results"),
273 v8::Number::New(isolate, total_results)
275 return handle_scope.Escape(results_object);
278 void InitAll(v8::Local<v8::Object> exports) {
279 IndexObject::Init(exports);
282 NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)