1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
- const path = require('path')
- const lunr = require('lunr')
- const { get } = require('lodash')
- const readFileAsync = require('../readfile-async')
- const { namePrefix } = require('./config')
- const { decompress } = require('./compress')
- const LUNR_DIR = './indexes'
- const lunrIndexes = new Map()
- const lunrRecords = new Map()
- module.exports = async function loadLunrResults ({ version, language, query, limit }) {
- const indexName = `${namePrefix}-${version}-${language}`
- if (!lunrIndexes.has(indexName) || !lunrRecords.has(indexName)) {
- lunrIndexes.set(indexName, await loadLunrIndex(indexName))
- lunrRecords.set(indexName, await loadLunrRecords(indexName))
- }
- const results = lunrIndexes.get(indexName)
- .search(query)
- .slice(0, limit)
- .map((result) => {
- const record = lunrRecords.get(indexName)[result.ref]
- return {
- url: result.ref,
- breadcrumbs: field(result, record, 'breadcrumbs'),
- heading: field(result, record, 'heading'),
- title: field(result, record, 'title'),
- content: field(result, record, 'content'),
- // don't highlight the topics array
- topics: record.topics
- }
- })
- return results
- }
- async function loadLunrIndex (indexName) {
- const filePath = path.posix.join(__dirname, LUNR_DIR, `${indexName}.json.br`)
- // Do not set to 'utf8' on file reads
- return readFileAsync(filePath)
- .then(decompress)
- .then(JSON.parse)
- .then(lunr.Index.load)
- }
- async function loadLunrRecords (indexName) {
- const filePath = path.posix.join(__dirname, LUNR_DIR, `${indexName}-records.json.br`)
- // Do not set to 'utf8' on file reads
- return readFileAsync(filePath)
- .then(decompress)
- .then(JSON.parse)
- }
- // Highlight a match within an attribute field
- function field (result, record, name) {
- const text = record[name]
- if (!text) return text
- // First, get a list of all the positions of the matching tokens
- const positions = Object.values(result.matchData.metadata)
- .map(fields => get(fields, [name, 'position']))
- .filter(Boolean)
- .flat()
- .sort((a, b) => a[0] - b[0])
- .map(([start, length]) => [start, start + length])
- .map(([start, end], i, a) => [i && a[i - 1][1], start, end])
- // If this field has no token matches, no highlighting
- if (!positions.length) return text
- // Highlight the text
- return positions
- .map(([prev, start, end], i) => [
- text.slice(prev, start),
- mark(text.slice(start, end)),
- i === positions.length - 1 && text.slice(end)
- ])
- .flat()
- .filter(Boolean)
- .join('')
- }
- function mark (text) {
- return `<mark>${text}</mark>`
- }
|