search-gold 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -24
- package/package.json +13 -11
- package/src/search.ts +23 -9
package/README.md
CHANGED
|
@@ -2,20 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
Your web page displays a list of columnar information,
|
|
5
|
+
Your web page displays a list of columnar information, most often coming from one of the following two common data structures:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```javascript
|
|
8
|
+
// an array of objects
|
|
9
|
+
Record<string, any>[]
|
|
10
|
+
|
|
11
|
+
// a Map in which the unique key's value is an object
|
|
12
|
+
Map<string | number, Record<string, any>>
|
|
13
|
+
````
|
|
8
14
|
|
|
9
|
-
For an improved UX, you
|
|
15
|
+
For an improved UX in the front end, you can add a search field so the user can filter the displayed records.
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
`search-gold` module can also be used outside of a browser environment.
|
|
18
|
+
|
|
19
|
+
The search engine is easy to set up and use!
|
|
12
20
|
|
|
13
21
|
## Installation
|
|
14
22
|
|
|
15
23
|
```bash
|
|
16
24
|
$ npm i search-gold
|
|
17
25
|
```
|
|
26
|
+
|
|
18
27
|
OR
|
|
28
|
+
|
|
19
29
|
```bash
|
|
20
30
|
$ bun i search-gold
|
|
21
31
|
```
|
|
@@ -26,11 +36,16 @@ The following should provide enough to get local search up and running and keep
|
|
|
26
36
|
|
|
27
37
|
Provide the following in your component, e.g., `my-data-list-component.ts`.
|
|
28
38
|
|
|
29
|
-
### 1. Import
|
|
39
|
+
### 1. Import applicable types and methods.
|
|
40
|
+
|
|
30
41
|
```javascript
|
|
31
42
|
import {
|
|
43
|
+
// types
|
|
32
44
|
ConfigParams,
|
|
33
45
|
ItemRecord,
|
|
46
|
+
MapRecords,
|
|
47
|
+
|
|
48
|
+
// methods
|
|
34
49
|
searchHandler,
|
|
35
50
|
searchSetConfig,
|
|
36
51
|
} from 'search-gold'
|
|
@@ -40,30 +55,32 @@ import {
|
|
|
40
55
|
|
|
41
56
|
This wrapper method only needs to be called in the following two cases:
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
- when component is initialized and when data is ready
|
|
59
|
+
- when data changes (record has been created, edited, deleted)
|
|
45
60
|
|
|
46
61
|
You can name this wrapper method anything you want.
|
|
47
62
|
|
|
48
63
|
```javascript
|
|
49
64
|
const initSearchConfig = () => {
|
|
50
65
|
const config: ConfigParams = {
|
|
51
|
-
//
|
|
52
|
-
// objects
|
|
66
|
+
// Required: items must reference an array of
|
|
67
|
+
// objects or a Map<string, Record<string, any>>.
|
|
68
|
+
//
|
|
69
|
+
// Search results are returned in a separate array or Map.
|
|
53
70
|
//
|
|
54
|
-
// 'search-gold' package only reads
|
|
55
|
-
//
|
|
71
|
+
// 'search-gold' package only reads the original data,
|
|
72
|
+
// leaving it unchanged.
|
|
56
73
|
items: MyData,
|
|
57
74
|
|
|
58
|
-
//
|
|
75
|
+
// Required: keys are the object properties you
|
|
59
76
|
// want to expose to search queries. List only
|
|
60
77
|
// the fields which make sense to you.
|
|
61
78
|
keys: ['productName', 'model', 'status'],
|
|
62
79
|
|
|
63
|
-
//
|
|
80
|
+
// Optional: default is false
|
|
64
81
|
caseSensitive: true,
|
|
65
82
|
|
|
66
|
-
//
|
|
83
|
+
// Optional: default is true
|
|
67
84
|
//
|
|
68
85
|
// You should only ever consider disabling
|
|
69
86
|
// memoization if you observe browser memory
|
|
@@ -72,7 +89,7 @@ const initSearchConfig = () => {
|
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
// Call the imported method with config arg.
|
|
75
|
-
searchSetConfig(config)
|
|
92
|
+
searchSetConfig(config),
|
|
76
93
|
|
|
77
94
|
// After setting the config, you can call searchHandler()
|
|
78
95
|
// as many times as necessary without having to set the
|
|
@@ -86,23 +103,27 @@ const initSearchConfig = () => {
|
|
|
86
103
|
//
|
|
87
104
|
// Passing an empty string effectively returns the
|
|
88
105
|
// the unfiltered set of records, the same as MyData.
|
|
89
|
-
myFilteredData = <ItemRecord[]>searchHandler('')
|
|
106
|
+
myFilteredData = <ItemRecord[]>searchHandler(''),
|
|
107
|
+
|
|
108
|
+
// OR
|
|
109
|
+
|
|
110
|
+
myFilteredData = <MapRecords>searchHandler(''),
|
|
90
111
|
}
|
|
91
|
-
|
|
112
|
+
```
|
|
92
113
|
|
|
93
114
|
### 3. Initialize the search engine.
|
|
94
115
|
|
|
95
|
-
This method (from Step 2.) should be called when your component
|
|
96
|
-
initialized and your data is ready.
|
|
116
|
+
This method (from Step 2.) should be called when your component is being
|
|
117
|
+
initialized and when your data is ready.
|
|
97
118
|
|
|
98
|
-
Also, this `initSearchConfig()` method should be called whenever your
|
|
119
|
+
Also, this `initSearchConfig()` method should be called again whenever your
|
|
99
120
|
data changes after a record has been created, edited, or deleted.
|
|
100
121
|
|
|
101
122
|
```javascript
|
|
102
123
|
// For this example we chose this method name; you
|
|
103
124
|
// can name this wrapper method anything you want.
|
|
104
125
|
initSearchConfig()
|
|
105
|
-
|
|
126
|
+
```
|
|
106
127
|
|
|
107
128
|
### 4. Execute search queries and get results.
|
|
108
129
|
|
|
@@ -110,9 +131,13 @@ Presumably, a method is binded to the UI's search field (in this example it's ca
|
|
|
110
131
|
`searchMyData`) and is called on every keystroke, debounced keystrokes, or an explicit
|
|
111
132
|
and separate submit event. Ultimately, the UX behavior is left up to the front end
|
|
112
133
|
engineer / design team.
|
|
134
|
+
|
|
113
135
|
```javascript
|
|
114
136
|
const searchMyData = (query: string) => {
|
|
115
137
|
// Call the imported method and get results.
|
|
138
|
+
//
|
|
139
|
+
// MyFilteredData is binded to the template for filtered display.
|
|
140
|
+
// Use the variable name and appropriate syntax for your app.
|
|
116
141
|
myFilteredData = searchHandler(query)
|
|
117
142
|
}
|
|
118
143
|
```
|
|
@@ -120,15 +145,16 @@ const searchMyData = (query: string) => {
|
|
|
120
145
|
## Author's Development
|
|
121
146
|
|
|
122
147
|
During development of the `search-gold` package, I integrated it
|
|
123
|
-
into several apps I had previously created with Vue using its Composition API.
|
|
148
|
+
into several apps I had previously created with Vue (using its Composition API).
|
|
124
149
|
|
|
125
150
|
In this documentation I tried to keep the Usage code snippets framework neutral.
|
|
126
151
|
|
|
127
|
-
`search-gold` itself is framework neutral and should work in any TS codebase.
|
|
152
|
+
`search-gold` itself is framework neutral and should work in any TS codebase, with or without a browser environment.
|
|
153
|
+
|
|
128
154
|
# Credits
|
|
129
155
|
|
|
130
156
|
G. Gold
|
|
131
157
|
|
|
132
|
-
|
|
158
|
+
February, July 2026
|
|
133
159
|
|
|
134
160
|
Have fun!
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "search-gold",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0
|
|
5
|
-
"description": "fast search engine targeting
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"description": "fast search engine targeting array of objects or Map",
|
|
6
6
|
"author": "gold",
|
|
7
7
|
"main": "./src/search.ts",
|
|
8
8
|
"type": "module",
|
|
@@ -10,18 +10,20 @@
|
|
|
10
10
|
"./src/search.ts"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"lint": "npx oxlint ./src/search.ts && npx eslint ."
|
|
13
|
+
"lint": "npx oxlint --type-aware ./src/search.ts && npx eslint ."
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"@typescript-eslint/eslint-plugin": "
|
|
17
|
-
"@typescript-eslint/parser": "
|
|
18
|
-
"eslint": "
|
|
19
|
-
"eslint-plugin-oxlint": "1.
|
|
20
|
-
"globals": "17.
|
|
21
|
-
"oxfmt": "0.
|
|
22
|
-
"oxlint": "1.
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "8.63.0",
|
|
17
|
+
"@typescript-eslint/parser": "8.63.0",
|
|
18
|
+
"eslint": "10.6.0",
|
|
19
|
+
"eslint-plugin-oxlint": "1.71.0",
|
|
20
|
+
"globals": "17.7.0",
|
|
21
|
+
"oxfmt": "0.58.0",
|
|
22
|
+
"oxlint": "1.73.0",
|
|
23
|
+
"oxlint-tsgolint": "0.24.0",
|
|
23
24
|
"tslib": "2.8.1",
|
|
24
|
-
"typescript
|
|
25
|
+
"typescript": "6.0.3",
|
|
26
|
+
"typescript-eslint": "8.63.0"
|
|
25
27
|
},
|
|
26
28
|
"repository": {
|
|
27
29
|
"type": "git",
|
package/src/search.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export type ItemRecord = Record<string, any>
|
|
2
|
+
export type MapRecords = Map<string | number, ItemRecord>
|
|
2
3
|
|
|
3
4
|
export interface ConfigParams {
|
|
4
|
-
items: ItemRecord[]
|
|
5
|
+
items: ItemRecord[] | MapRecords
|
|
5
6
|
keys: string[]
|
|
6
7
|
caseSensitive?: boolean
|
|
7
8
|
enableMemoize?: boolean
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
interface Config {
|
|
11
|
-
Items: ItemRecord[]
|
|
12
|
+
Items: ItemRecord[] | MapRecords
|
|
12
13
|
Keys: string[]
|
|
13
14
|
RegexFlag: 'i' | undefined
|
|
14
15
|
EnableMemoize: boolean
|
|
@@ -25,6 +26,8 @@ const escapeRegExp = (str: string): string => {
|
|
|
25
26
|
const cache = new Map()
|
|
26
27
|
|
|
27
28
|
const Conf: Config = {
|
|
29
|
+
// Items could be either an array or a map.
|
|
30
|
+
// run-time will handle detection and applicable syntax.
|
|
28
31
|
Items: [],
|
|
29
32
|
Keys: [],
|
|
30
33
|
RegexFlag: 'i',
|
|
@@ -49,25 +52,36 @@ export const searchSetConfig = (params: ConfigParams): void => {
|
|
|
49
52
|
// that it behooves the caller to this method be responsible for
|
|
50
53
|
// correctly setting the config for this module and to check that the
|
|
51
54
|
// search's query argument is a string.
|
|
52
|
-
export const searchHandler = (query: string): ItemRecord[] => {
|
|
53
|
-
const results: ItemRecord[] = []
|
|
55
|
+
export const searchHandler = (query: string): ItemRecord[] | MapRecords => {
|
|
54
56
|
const qTrimmed = escapeRegExp(query.trim())
|
|
55
57
|
|
|
56
58
|
if (qTrimmed === '') {
|
|
57
59
|
return Conf.Items
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
let results: ItemRecord[] | MapRecords
|
|
63
|
+
|
|
60
64
|
if (Conf.EnableMemoize && cache.has(qTrimmed)) {
|
|
61
65
|
return cache.get(qTrimmed)
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
const rx = new RegExp(qTrimmed, Conf.RegexFlag)
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if (Conf.Items instanceof Map) {
|
|
71
|
+
results = <MapRecords>new Map()
|
|
72
|
+
for (const [key, item] of Conf.Items) {
|
|
73
|
+
const searchString = Conf.Keys.map((k) => item[k]).join('^')
|
|
74
|
+
if (rx.test(searchString)) {
|
|
75
|
+
results.set(key, item)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
results = <ItemRecord[]>[]
|
|
80
|
+
for (const item of Conf.Items) {
|
|
81
|
+
const searchString = Conf.Keys.map((k) => item[k]).join('^')
|
|
82
|
+
if (rx.test(searchString)) {
|
|
83
|
+
results.push(item)
|
|
84
|
+
}
|
|
71
85
|
}
|
|
72
86
|
}
|
|
73
87
|
|