@@ -2,6 +2,9 @@ import BaseProvider from "./base-provider.js"
2
2
import * as _ from "../utils/lodash.js"
3
3
import * as errors from "../errors/index.js"
4
4
import { listOfCapabilities } from "../utils/index.js"
5
+ import jskos from "jskos-tools"
6
+ import FlexSearch from "flexsearch"
7
+ import axios from "axios"
5
8
6
9
// from https://stackoverflow.com/a/22021709
7
10
function unicodeToChar ( text ) {
@@ -33,8 +36,8 @@ export default class SkohubProvider extends BaseProvider {
33
36
this . has . concepts = true
34
37
this . has . narrower = true
35
38
this . has . ancestors = true
36
- this . has . suggest = false
37
- this . has . search = false
39
+ this . has . suggest = true
40
+ this . has . search = true
38
41
// Explicitly set other capabilities to false
39
42
listOfCapabilities . filter ( c => ! this . has [ c ] ) . forEach ( c => {
40
43
this . has [ c ] = false
@@ -43,6 +46,7 @@ export default class SkohubProvider extends BaseProvider {
43
46
44
47
_setup ( ) {
45
48
this . _jskos . schemes = this . schemes || [ ]
49
+ this . _index = { }
46
50
this . _cache = { }
47
51
}
48
52
@@ -184,6 +188,82 @@ export default class SkohubProvider extends BaseProvider {
184
188
return concept . narrower
185
189
}
186
190
191
+ /**
192
+ * Returns concept search results.
193
+ *
194
+ * @param {Object } config
195
+ * @param {string } config.search search string
196
+ * @param {Object } [config.scheme] concept scheme to search in
197
+ * @param {number } [config.limit=100] maximum number of search results
198
+ * @returns {Array } array of JSKOS concept objects
199
+ */
200
+ async search ( { search, scheme, limit = 100 } ) {
201
+ if ( ! scheme || ! scheme . uri ) {
202
+ throw new errors . InvalidOrMissingParameterError ( { parameter : "scheme" } )
203
+ }
204
+ if ( ! search ) {
205
+ throw new errors . InvalidOrMissingParameterError ( { parameter : "search" } )
206
+ }
207
+ // 1. Load index file if necessary
208
+ let index
209
+ if ( ! this . _index [ scheme . uri ] ) {
210
+ this . _index [ scheme . uri ] = { }
211
+ }
212
+ // Iterate over languages and use the first one that has an index
213
+ for ( const lang of this . languages ) {
214
+ if ( this . _index [ scheme . uri ] [ lang ] ) {
215
+ index = this . _index [ scheme . uri ] [ lang ]
216
+ break
217
+ }
218
+ try {
219
+ const { data } = await axios . get ( `${ scheme . uri } .${ lang } .index` )
220
+ index = FlexSearch . create ( )
221
+ index . import ( data )
222
+ this . _index [ scheme . uri ] [ lang ] = index
223
+ break
224
+ } catch ( error ) {
225
+ // Ignore error
226
+ }
227
+ }
228
+ if ( ! index ) {
229
+ throw new errors . InvalidRequestError ( { message : "Could not find search index for any of the available languages " + this . languages . join ( "," ) } )
230
+ }
231
+ // 2. Use Flexsearch to get result URIs from index
232
+ const result = index . search ( search )
233
+ // 3. Load concept data for results
234
+ const concepts = await this . getConcepts ( { concepts : result . map ( uri => ( { uri, inScheme : [ scheme ] } ) ) } )
235
+ return concepts . slice ( 0 , limit )
236
+ }
237
+
238
+ /**
239
+ * Returns suggestion result in OpenSearch Suggest Format.
240
+ *
241
+ * @param {Object } config
242
+ * @param {string } config.search search string
243
+ * @param {Object } [config.scheme] concept scheme to search in
244
+ * @param {number } [config.limit=100] maximum number of search results
245
+ * @returns {Array } result in OpenSearch Suggest Format
246
+ */
247
+ async suggest ( config ) {
248
+ config . _raw = true
249
+ const concepts = await this . search ( config )
250
+ const result = [ config . search , [ ] , [ ] , [ ] ]
251
+ for ( let concept of concepts ) {
252
+ const notation = jskos . notation ( concept )
253
+ const label = jskos . prefLabel ( concept )
254
+ result [ 1 ] . push ( ( notation ? notation + " " : "" ) + label )
255
+ result [ 2 ] . push ( "" )
256
+ result [ 3 ] . push ( concept . uri )
257
+ }
258
+ if ( concepts . _totalCount != undefined ) {
259
+ result . _totalCount = concepts . _totalCount
260
+ } else {
261
+ result . _totalCount = concepts . length
262
+ }
263
+ return result
264
+ }
265
+
266
+
187
267
async _loadConcept ( uri , config ) {
188
268
const data = await this . axios ( { ...config , url : `${ uri } .json` } )
189
269
0 commit comments