@sebspark/opensearch 0.3.3 → 1.0.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 CHANGED
@@ -2,94 +2,141 @@
2
2
 
3
3
  A wrapper for OpenSearch Client to assist with typed queries, indices etc
4
4
 
5
- ## Usage
5
+ ## Add
6
6
 
7
7
  ```zsh
8
8
  yarn add @sebspark/opensearch @opensearch-project/opensearch
9
9
  ```
10
10
 
11
- **Note:** Data types require a property called `id` of type `string`
11
+ ## Usage
12
+
13
+ Everything starts with an index definition. This must be declared as a const which **satisfies** `OpenSearchIndexMapping`. From this you can then derive your documents and search queries.
14
+
15
+ ```typescript
16
+ import type {
17
+ IndexDefinition,
18
+ DocumentFor,
19
+ SearchRequest,
20
+ } from '@sebspark/opensearch'
21
+
22
+ export const personIndex = {
23
+ index: 'person',
24
+ body: {
25
+ mappings: {
26
+ properties: {
27
+ name: { type: 'keyword' },
28
+ age: { type: 'integer' },
29
+ },
30
+ },
31
+ },
32
+ } as const satisfies IndexDefinition
33
+
34
+ export type PersonIndex = typeof personIndex
35
+ export type PersonDocument = DocumentFor<PersonIndex>
36
+ export type PersonSearch = SearchRequest<PersonIndex>
37
+ ```
38
+
39
+ Using the index definition and your types, you can now start interacting with OpenSearch with typeahead:
12
40
 
13
41
  ```typescript
14
- import { Client } from '@opensearch-project/opensearch'
15
- import { helper } from '@sebspark/opensearch'
42
+ import { OpenSearchClient } from '@sebspark/opensearch'
43
+ import {
44
+ personIndex,
45
+ type PersonIndex,
46
+ type PersonDocument,
47
+ type PersonSearch,
48
+ } from './personIndex'
49
+
50
+ async function run () {
51
+ const client = new OpenSearchClient()
16
52
 
17
- const client = new Client({})
18
- const typedClient = helper(client)
53
+ // Check if the index exists
54
+ const { body: exists } = await client.indices.exists<PersonIndex>({ index: 'person' })
19
55
 
20
- type Data = {
21
- id: string
22
- user: {
23
- name: string
24
- age: number
56
+ // If not: create it
57
+ if (!exists) {
58
+ await client.indices.create(personIndex)
25
59
  }
26
- blog: {
27
- posts: Array<{
28
- title: string
29
- text: string
30
- }>
60
+
61
+ // Create a document
62
+ const doc: PersonDocument = { // <- This will auto complete on fields and types
63
+ name: 'John Wick',
64
+ age: 52,
31
65
  }
32
- }
33
66
 
34
- type UserOnly = Pick<Data, 'id' | 'user'>
67
+ // Store it
68
+ await client.index<PersonIndex>({ // <- This will auto complete on index name
69
+ index: 'person',
70
+ body: doc,
71
+ })
35
72
 
36
- // Typed index creation
37
- async function createIndex() {
38
- await helper(client as Client).typedIndexCreate<Data>('data', {
39
- mappings: {
40
- properties: {
41
- user: {
42
- age: {
43
- type: 'integer'
44
- },
45
- },
46
- isTrue: {
47
- type: 'boolean'
73
+ // Find it
74
+ const searchQuery: PersonSearch = { // <- This will auto complete on fields and types
75
+ index: personIndexName,
76
+ body: {
77
+ query: {
78
+ match: {
79
+ name: 'John Wick'
48
80
  }
49
81
  }
50
82
  }
51
- })
83
+ }
84
+ const result = await client.search(searchQuery)
85
+
86
+ // result.body.hits.hits <- This has type PersonDocument[]
52
87
  }
88
+ ```
53
89
 
54
- // Typed insert
55
- async function indexDocument() {
56
- await helper(client as Client).typedIndex<Data>('data', {
57
- id: 'foo',
58
- isTrue: true,
59
- user: {
60
- age: 42,
61
- name: 'Arthur Dent',
62
- }
63
- })
64
- }
90
+ ## Helpers
65
91
 
66
- // Typed search
67
- async function loadData() {
68
- // result: Data[]
69
- const { result } = await typedClient.typedSearch<Data>({
70
- index: 'data',
71
- body: {
72
- query: {
73
- term: {
74
- 'user.name': {
75
- value: 'Arthur Dent',
76
- },
77
- },
78
- },
79
- },
80
- })
81
- return result
82
- }
83
- async function loadPartialData() {
84
- // result: UserOnly[]
85
- const { result } = await typedClient.typedSearch<Data, UserOnly>({
86
- index: 'data',
87
- body: {
88
- query: {
89
- fields: ['user.age', 'user.name'],
90
- },
91
- },
92
- })
93
- return result
94
- }
92
+ ### Bulk operations
93
+
94
+ Since bulk operations are a bit tricky to call, this library offers a few utility functions to simplify:
95
+
96
+ ```typescript
97
+ import {
98
+ bulkIndex,
99
+ bulkCreate,
100
+ bulkUpdate,
101
+ bulkDelete,
102
+ } from '@sebspark/opensearch'
103
+
104
+ // create a bunch of documents with automatic id:s
105
+ const indexWithAutoId = bulkIndex<PersonIndex>('persons', [
106
+ { name: 'John Wick', age: 52 },
107
+ { name: 'Jason Bourne', age: 50 },
108
+ ])
109
+ await opensearchClient.bulk(indexWithAutoId)
110
+
111
+ // Name to lower case without spaces
112
+ const idGen = (doc: PersonDocument) =>
113
+ doc.name.replace(/\s/g, '').toLowerCase()
114
+
115
+ // create a bunch of documents with id generator function
116
+ const indexWithIdGen = bulkIndex<PersonIndex>('persons', [
117
+ { name: 'John Wick', age: 52 },
118
+ { name: 'Jason Bourne', age: 50 },
119
+ ], idGen)
120
+ await opensearchClient.bulk(indexWithIdGen)
121
+
122
+ // create a bunch of documents and fail if id exists
123
+ const createDocs = bulkCreate<PersonIndex>('persons', [
124
+ { name: 'John Wick', age: 52 },
125
+ { name: 'Jason Bourne', age: 50 },
126
+ ], idGen)
127
+ await opensearchClient.bulk(createDocs)
128
+
129
+ // update a bunch of documents
130
+ const updateDocs = bulkUpdate<PersonIndex>('persons', [
131
+ { doc: { name: 'John Wick', age: 53 } },
132
+ { doc: { name: 'Jason Bourne', age: 51 } },
133
+ ], idGen)
134
+ await opensearchClient.bulk(updateDocs)
135
+
136
+ // delete a bunch of documents
137
+ const deleteDocs = bulkDelete<PersonIndex>('persons', [
138
+ 'johnwick',
139
+ 'jasonbourne',
140
+ ])
141
+ await opensearchClient.bulk(deleteDocs)
95
142
  ```