/*
- * Copyright (C) 2018 Nick Downing <nick@ndcode.org>
+ * Copyright (C) 2022 Nick Downing <nick@ndcode.org>
* SPDX-License-Identifier: MIT
- *
+ *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
- *
+ *
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
- *
+ *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* IN THE SOFTWARE.
*/
-#include <node.h>
-#include <node_object_wrap.h>
+#include <napi.h>
#include <stdlib.h>
#include <string.h>
#include <zettair/def.h>
namespace zettair {
-static v8::Local<v8::Object> index_result_to_object(
- v8::Isolate* isolate,
+static Napi::Object result_to_object(
+ Napi::Env env,
struct index_result* result
);
-static v8::Local<v8::Object> index_results_to_object(
- v8::Isolate* isolate,
+static Napi::Object results_to_object(
+ Napi::Env env,
struct index_result* results,
- unsigned int num_results,
+ unsigned int n_results,
double/*unsigned long int*/ total_results
);
-class IndexObject : public node::ObjectWrap {
+class Index : public Napi::ObjectWrap<Index> {
public:
- static void Init(v8::Local<v8::Object> exports);
+ Index(const Napi::CallbackInfo& info);
+ Napi::Value search(const Napi::CallbackInfo& info);
-private:
struct index* idx;
-
- explicit IndexObject(struct index* idx0);
- ~IndexObject();
-
- static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
- static void Search(const v8::FunctionCallbackInfo<v8::Value>& args);
- static v8::Persistent<v8::Function> constructor;
};
-v8::Persistent<v8::Function> IndexObject::constructor;
+Index::Index(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Index>(info) {
+ Napi::Env env = info.Env();
+ Napi::String prefix = info[0].As<Napi::String>();
-IndexObject::IndexObject(struct index* idx0) : idx(idx0) {
-}
+ int lopts = INDEX_LOAD_NOOPT;
+ struct index_load_opt lopt;
+ memset(&lopt, 0, sizeof(struct index_load_opt));
-IndexObject::~IndexObject() {
+ lopts |= INDEX_LOAD_IGNORE_VERSION; /* quick hack */
+ idx = index_load(prefix.Utf8Value().c_str(), MEMORY_DEFAULT, lopts, &lopt);
+ if (idx == NULL)
+ Napi::Error::New(
+ env,
+ "Unable to load index"
+ ).ThrowAsJavaScriptException();
}
-void IndexObject::Init(v8::Local<v8::Object> exports) {
- v8::Isolate* isolate = exports->GetIsolate();
-
- // Prepare constructor template
- v8::Local<v8::FunctionTemplate> tpl =
- v8::FunctionTemplate::New(isolate, New);
- tpl->SetClassName(v8::String::NewFromUtf8(isolate, "Index"));
- tpl->InstanceTemplate()->SetInternalFieldCount(1);
+Napi::Value Index::search(const Napi::CallbackInfo& info){
+ Napi::Env env = info.Env();
+ std::string query = info[0].As<Napi::String>().Utf8Value();
+ uint32_t startdoc = info[1].As<Napi::Number>().Uint32Value();
+ uint32_t len = info[2].As<Napi::Number>().Uint32Value();
+ std::string opt_type =
+ info[3] == env.Undefined() ?
+ std::string() :
+ info[3].As<Napi::String>().Utf8Value();
+ Napi::Array opt_args_tuple =
+ info[4] == env.Undefined() ?
+ Napi::Array::New(env) :
+ info[4].As<Napi::Array>();
+ uint32_t accumulator_limit =
+ info[5] == env.Undefined() ?
+ 0 :
+ info[5].As<Napi::Number>().Uint32Value();
- // Prototype
- NODE_SET_PROTOTYPE_METHOD(tpl, "search", Search);
-
- constructor.Reset(isolate, tpl->GetFunction());
- exports->Set(
- v8::String::NewFromUtf8(isolate, "Index"),
- tpl->GetFunction()
+ struct index_result* results = (struct index_result*)malloc(
+ len * sizeof(struct index_result)
);
-}
-
-void IndexObject::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Isolate* isolate = args.GetIsolate();
-
- if (args.IsConstructCall()) {
- // Invoked as constructor: `new Index(...)`
- v8::String::Utf8Value prefix(args[0]);
- struct index* idx = NULL;;
- int lopts = INDEX_LOAD_NOOPT;
- struct index_load_opt lopt;
-
- lopts |= INDEX_LOAD_IGNORE_VERSION; /* quick hack */
- if (
- (
- idx = index_load(
- args[0]->IsString() ? *prefix : "index",
- MEMORY_DEFAULT,
- lopts,
- &lopt
- )
- ) == NULL
- ) {
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, "Unable to load index")
- );
- return;
- }
- IndexObject* obj = new IndexObject(idx);
- obj->Wrap(args.This());
- args.GetReturnValue().Set(args.This());
- } else {
- // Invoked as plain function `Index(...)`, turn into construct call.
- const int argc = 1;
- v8::Local<v8::Value> argv[argc] = { args[0] };
- v8::Local<v8::Context> context = isolate->GetCurrentContext();
- v8::Local<v8::Function> cons =
- v8::Local<v8::Function>::New(isolate, constructor);
- v8::Local<v8::Object> result =
- cons->NewInstance(context, argc, argv).ToLocalChecked();
- args.GetReturnValue().Set(result);
+ if (results == NULL) {
+ Napi::Error::New(
+ env,
+ "Unable to allocate results"
+ ).ThrowAsJavaScriptException();
+ return env.Null();
}
-}
-
-void IndexObject::Search(const v8::FunctionCallbackInfo<v8::Value>& args) {
- v8::Isolate* isolate = args.GetIsolate();
- v8::String::Utf8Value query(args[0]);
- v8::String::Utf8Value optType(args[3]);
- v8::Local<v8::Array> optArgsTuple;
- if (args[4]->IsArray())
- optArgsTuple = args[4].As<v8::Array>();
- unsigned long startdoc = args[1]->IntegerValue();
- unsigned long len = args[2]->IntegerValue();
- IndexObject* Index = node::ObjectWrap::Unwrap<IndexObject>(args.Holder());
- struct index_result* result;
- unsigned int results;
+ unsigned int n_results;
double/*unsigned long int*/ total_results;
int est;
- unsigned int accumulator_limit = args[5]->Uint32Value();
int opts = INDEX_SEARCH_NOOPT;
struct index_search_opt opt;
+ memset(&opt, 0, sizeof(struct index_search_opt));
opt.u.okapi_k3.k1 = 1.2;
opt.u.okapi_k3.k3 = 1e10;
opt.u.okapi_k3.b = 0.75;
- if (
- (result = (struct index_result*)malloc(sizeof(*result) * len)) == NULL
- ) {
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, "Unable to allocate results")
- );
- return;
- }
- if (args[3]->IsString()) {
- if (strcmp(*optType, "COSINE") == 0) {
+ if (info[3] != env.Undefined()) {
+ if (opt_type == "COSINE")
opts = INDEX_SEARCH_COSINE_RANK;
- } else if (strcmp(*optType, "OKAPI") == 0) {
+ else if (opt_type == "OKAPI")
opts = INDEX_SEARCH_OKAPI_RANK;
- } else if (strcmp(*optType, "OKAPI_K3") == 0) {
- if (optArgsTuple.IsEmpty()) {
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, "Must supply args to search type")
- );
- free(result);
- return;
+ else if (opt_type == "OKAPI_K3") {
+ if (opt_args_tuple.Length() < 3U) {
+ Napi::Error::New(
+ env,
+ "Must supply args to search type"
+ ).ThrowAsJavaScriptException();
+ free(results);
+ return env.Null();
}
opts = INDEX_SEARCH_OKAPI_RANK;
- opt.u.okapi_k3.k1 = optArgsTuple->Get(0)->IntegerValue();
- opt.u.okapi_k3.k3 = optArgsTuple->Get(1)->IntegerValue();
- opt.u.okapi_k3.b = optArgsTuple->Get(2)->IntegerValue();
- } else if (strcmp(*optType, "HAWKAPI") == 0) {
- if (optArgsTuple.IsEmpty()) {
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, "Must supply args to search type")
- );
- free(result);
- return;
+ opt.u.okapi_k3.k1 = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+ opt.u.okapi_k3.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
+ opt.u.okapi_k3.b = opt_args_tuple.Get(2U).As<Napi::Number>().FloatValue();
+ }
+ else if (opt_type == "HAWKAPI") {
+ if (opt_args_tuple.Length() < 2U) {
+ Napi::Error::New(
+ env,
+ "Must supply args to search type"
+ ).ThrowAsJavaScriptException();
+ free(results);
+ return env.Null();
}
opts = INDEX_SEARCH_HAWKAPI_RANK;
- opt.u.hawkapi.alpha = optArgsTuple->Get(0)->IntegerValue();
- opt.u.hawkapi.k3 = optArgsTuple->Get(1)->IntegerValue();
- } else if (strcmp(*optType, "DIRICHLET") == 0) {
+ opt.u.hawkapi.alpha = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+ opt.u.hawkapi.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
+ }
+ else if (opt_type == "DIRICHLET") {
opts = INDEX_SEARCH_DIRICHLET_RANK;
- if (optArgsTuple.IsEmpty() || optArgsTuple->Length() == 0)
- opt.u.dirichlet.mu = 2500.0;
- else
- opt.u.dirichlet.mu = optArgsTuple->Get(0)->NumberValue();
- } else {
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, "Unknown search type")
- );
- free(result);
- return;
+ opt.u.dirichlet.mu =
+ opt_args_tuple.Length() < 1U ?
+ 2500.0 :
+ opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
+ }
+ else {
+ Napi::Error::New(
+ env,
+ "Unknown search type"
+ ).ThrowAsJavaScriptException();
+ free(results);
+ return env.Null();
}
}
if (accumulator_limit != 0) {
opts |= INDEX_SEARCH_SUMMARY_TYPE;
opt.summary_type = INDEX_SUMMARISE_TAG;
#endif
- if (!index_search(Index->idx, *query, startdoc, len,
- result, &results, &total_results, &est, opts, &opt)) {
+ if (
+ !index_search(
+ idx,
+ query.c_str(),
+ startdoc,
+ len,
+ results,
+ &n_results,
+ &total_results,
+ &est,
+ opts,
+ &opt
+ )
+ ) {
char err_buf[1024];
- snprintf(err_buf, 1024, "Unable to perform search for query '%s'; "
- "system error is '%s'\n", *query, strerror(errno));
- isolate->ThrowException(
- v8::String::NewFromUtf8(isolate, err_buf)
+ snprintf(
+ err_buf,
+ sizeof(err_buf),
+ "Unable to perform search for query '%s'; system error is '%s'\n",
+ query.c_str(),
+ strerror(errno)
);
- free(result);
- return;
+ Napi::Error::New(env, err_buf).ThrowAsJavaScriptException();
+ free(results);
+ return env.Null();
}
- args.GetReturnValue().Set(
- index_results_to_object(isolate, result, results, total_results)
- );
- free(result);
- return;
+ Napi::Object object =
+ results_to_object(env, results, n_results, total_results);
+ free(results);
+ return object;
}
-static v8::Local<v8::Object> index_result_to_object(
- v8::Isolate* isolate,
+static Napi::Object result_to_object(
+ Napi::Env env,
struct index_result* result
) {
- v8::EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Object> result_object = v8::Object::New(isolate);
- result_object->Set(
- v8::String::NewFromUtf8(isolate, "docno"),
- v8::Integer::New(isolate, result->docno)
+ Napi::Object object = Napi::Object::New(env);
+ object.Set(
+ Napi::String::New(env, "docno"),
+ Napi::Number::New(env, result->docno)
);
- result_object->Set(
- v8::String::NewFromUtf8(isolate, "score"),
- v8::Number::New(isolate, result->score)
+ object.Set(
+ Napi::String::New(env, "score"),
+ Napi::Number::New(env, result->score)
);
- result_object->Set(
- v8::String::NewFromUtf8(isolate, "summary"),
- v8::String::NewFromUtf8(isolate, result->summary)
+ object.Set(
+ Napi::String::New(env, "summary"),
+ Napi::String::New(env, result->summary)
);
- result_object->Set(
- v8::String::NewFromUtf8(isolate, "title"),
- v8::String::NewFromUtf8(isolate, result->title)
+ object.Set(
+ Napi::String::New(env, "title"),
+ Napi::String::New(env, result->title)
);
- result_object->Set(
- v8::String::NewFromUtf8(isolate, "auxiliary"),
- v8::String::NewFromUtf8(isolate, result->auxilliary)
+ object.Set(
+ Napi::String::New(env, "auxiliary"),
+ Napi::String::New(env, result->auxilliary)
);
- return handle_scope.Escape(result_object);
+ return object;
}
-static v8::Local<v8::Object> index_results_to_object(
- v8::Isolate* isolate,
+static Napi::Object results_to_object(
+ Napi::Env env,
struct index_result* results,
- unsigned int num_results,
+ unsigned int n_results,
double/*unsigned long int*/ total_results
) {
- v8::EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Object> results_object = v8::Object::New(isolate);
- v8::Local<v8::Array> results_array = v8::Array::New(isolate, num_results);
- for (unsigned int i = 0; i < num_results; ++i)
- results_array->Set(i, index_result_to_object(isolate, results + i));
- results_object->Set(
- v8::String::NewFromUtf8(isolate, "results"),
- results_array
+ Napi::Object object = Napi::Object::New(env);
+ Napi::Array array = Napi::Array::New(env, n_results);
+ for (unsigned int i = 0; i < n_results; ++i)
+ array.Set(i, result_to_object(env, results + i));
+ object.Set(
+ Napi::String::New(env, "results"),
+ array
);
- results_object->Set(
- v8::String::NewFromUtf8(isolate, "total_results"),
- v8::Number::New(isolate, total_results)
+ object.Set(
+ Napi::String::New(env, "total_results"),
+ Napi::Number::New(env, total_results)
);
- return handle_scope.Escape(results_object);
+ return object;
}
-void InitAll(v8::Local<v8::Object> exports) {
- IndexObject::Init(exports);
+Napi::Object Init(Napi::Env env, Napi::Object exports) {
+ exports.Set(
+ "Index",
+ Napi::ObjectWrap<Index>::DefineClass(
+ env,
+ "Index",
+ {
+ Napi::ObjectWrap<Index>::InstanceMethod<&Index::search>(
+ "search",
+ static_cast<napi_property_attributes>(
+ napi_writable | napi_configurable
+ )
+ )
+ }
+ )
+ );
+ return exports;
}
-NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
+NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
}