2 * Copyright (C) 2022 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
27 #include <zettair/def.h>
28 #include <zettair/index.h>
32 static Napi::Object result_to_object(
34 struct index_result* result
36 static Napi::Object results_to_object(
38 struct index_result* results,
39 unsigned int n_results,
40 double/*unsigned long int*/ total_results
43 class Index : public Napi::ObjectWrap<Index> {
45 Index(const Napi::CallbackInfo& info);
46 Napi::Value search(const Napi::CallbackInfo& info);
51 Index::Index(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Index>(info) {
52 Napi::Env env = info.Env();
53 Napi::String prefix = info[0].As<Napi::String>();
55 int lopts = INDEX_LOAD_NOOPT;
56 struct index_load_opt lopt;
57 memset(&lopt, 0, sizeof(struct index_load_opt));
59 lopts |= INDEX_LOAD_IGNORE_VERSION; /* quick hack */
60 idx = index_load(prefix.Utf8Value().c_str(), MEMORY_DEFAULT, lopts, &lopt);
64 "Unable to load index"
65 ).ThrowAsJavaScriptException();
68 Napi::Value Index::search(const Napi::CallbackInfo& info){
69 Napi::Env env = info.Env();
70 std::string query = info[0].As<Napi::String>().Utf8Value();
71 uint32_t startdoc = info[1].As<Napi::Number>().Uint32Value();
72 uint32_t len = info[2].As<Napi::Number>().Uint32Value();
73 std::string opt_type =
74 info[3] == env.Undefined() ?
76 info[3].As<Napi::String>().Utf8Value();
77 Napi::Array opt_args_tuple =
78 info[4] == env.Undefined() ?
79 Napi::Array::New(env) :
80 info[4].As<Napi::Array>();
81 uint32_t accumulator_limit =
82 info[5] == env.Undefined() ?
84 info[5].As<Napi::Number>().Uint32Value();
86 struct index_result* results = (struct index_result*)malloc(
87 len * sizeof(struct index_result)
89 if (results == NULL) {
92 "Unable to allocate results"
93 ).ThrowAsJavaScriptException();
97 unsigned int n_results;
98 double/*unsigned long int*/ total_results;
100 int opts = INDEX_SEARCH_NOOPT;
101 struct index_search_opt opt;
102 memset(&opt, 0, sizeof(struct index_search_opt));
104 opt.u.okapi_k3.k1 = 1.2;
105 opt.u.okapi_k3.k3 = 1e10;
106 opt.u.okapi_k3.b = 0.75;
108 if (info[3] != env.Undefined()) {
109 if (opt_type == "COSINE")
110 opts = INDEX_SEARCH_COSINE_RANK;
111 else if (opt_type == "OKAPI")
112 opts = INDEX_SEARCH_OKAPI_RANK;
113 else if (opt_type == "OKAPI_K3") {
114 if (opt_args_tuple.Length() < 3U) {
117 "Must supply args to search type"
118 ).ThrowAsJavaScriptException();
122 opts = INDEX_SEARCH_OKAPI_RANK;
123 opt.u.okapi_k3.k1 = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
124 opt.u.okapi_k3.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
125 opt.u.okapi_k3.b = opt_args_tuple.Get(2U).As<Napi::Number>().FloatValue();
127 else if (opt_type == "HAWKAPI") {
128 if (opt_args_tuple.Length() < 2U) {
131 "Must supply args to search type"
132 ).ThrowAsJavaScriptException();
136 opts = INDEX_SEARCH_HAWKAPI_RANK;
137 opt.u.hawkapi.alpha = opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
138 opt.u.hawkapi.k3 = opt_args_tuple.Get(1U).As<Napi::Number>().FloatValue();
140 else if (opt_type == "DIRICHLET") {
141 opts = INDEX_SEARCH_DIRICHLET_RANK;
143 opt_args_tuple.Length() < 1U ?
145 opt_args_tuple.Get(0U).As<Napi::Number>().FloatValue();
150 "Unknown search type"
151 ).ThrowAsJavaScriptException();
156 if (accumulator_limit != 0) {
157 opts |= INDEX_SEARCH_ACCUMULATOR_LIMIT;
158 opt.accumulator_limit = accumulator_limit;
161 opts |= INDEX_SEARCH_SUMMARY_TYPE;
162 opt.summary_type = INDEX_SUMMARISE_TAG;
182 "Unable to perform search for query '%s'; system error is '%s'\n",
186 Napi::Error::New(env, err_buf).ThrowAsJavaScriptException();
190 Napi::Object object =
191 results_to_object(env, results, n_results, total_results);
196 static Napi::Object result_to_object(
198 struct index_result* result
200 Napi::Object object = Napi::Object::New(env);
202 Napi::String::New(env, "docno"),
203 Napi::Number::New(env, result->docno)
206 Napi::String::New(env, "score"),
207 Napi::Number::New(env, result->score)
210 Napi::String::New(env, "summary"),
211 Napi::String::New(env, result->summary)
214 Napi::String::New(env, "title"),
215 Napi::String::New(env, result->title)
218 Napi::String::New(env, "auxiliary"),
219 Napi::String::New(env, result->auxilliary)
224 static Napi::Object results_to_object(
226 struct index_result* results,
227 unsigned int n_results,
228 double/*unsigned long int*/ total_results
230 Napi::Object object = Napi::Object::New(env);
231 Napi::Array array = Napi::Array::New(env, n_results);
232 for (unsigned int i = 0; i < n_results; ++i)
233 array.Set(i, result_to_object(env, results + i));
235 Napi::String::New(env, "results"),
239 Napi::String::New(env, "total_results"),
240 Napi::Number::New(env, total_results)
245 Napi::Object Init(Napi::Env env, Napi::Object exports) {
248 Napi::ObjectWrap<Index>::DefineClass(
252 Napi::ObjectWrap<Index>::InstanceMethod<&Index::search>(
254 static_cast<napi_property_attributes>(
255 napi_writable | napi_configurable
264 NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)