typesense 0.8.0pre → 0.11.1
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.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/examples/aliases.rb +0 -2
- data/examples/client_initialization.rb +21 -21
- data/examples/collections_and_documents.rb +50 -2
- data/examples/overrides.rb +11 -7
- data/examples/search.rb +22 -57
- data/examples/synonyms.rb +127 -0
- data/lib/typesense.rb +4 -0
- data/lib/typesense/api_call.rb +45 -76
- data/lib/typesense/client.rb +4 -1
- data/lib/typesense/collection.rb +2 -1
- data/lib/typesense/document.rb +4 -0
- data/lib/typesense/documents.rb +36 -9
- data/lib/typesense/error.rb +8 -0
- data/lib/typesense/keys.rb +2 -0
- data/lib/typesense/multi_search.rb +15 -0
- data/lib/typesense/operations.rb +15 -0
- data/lib/typesense/overrides.rb +2 -2
- data/lib/typesense/synonym.rb +25 -0
- data/lib/typesense/synonyms.rb +31 -0
- data/lib/typesense/version.rb +1 -1
- data/typesense.gemspec +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff00b5a30813dd1e6ea053ee9f602ba3216a08a7e1b22858a8c8bdba289a65bf
|
4
|
+
data.tar.gz: 74f2987e407bea8f2b2424e8f620c98516db16750de2d9104b7a3f9bd56cec8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7352da49cddd9e1ade9cee9e1d9df1b6ab211b5f728e5da7b4d61bd5bb83349e3cdd209e28bd813df9d528086589f0bac6f87aac9a4cc9cf4d7760b527dbddc
|
7
|
+
data.tar.gz: 1e90c09b1a1c73a3ff29e1a8f0349daccba142e18b470639d88ad09bd05449d793eb123b2154ffb8dd99abd89f0db7cb3f89dff305bcf697ce2c5bc51697d0a7
|
data/README.md
CHANGED
@@ -33,7 +33,10 @@ Tests are also a good place to know how the the library works internally: [spec]
|
|
33
33
|
|
34
34
|
| Typesense Server | typesense-ruby |
|
35
35
|
|------------------|----------------|
|
36
|
-
| \>= v0.
|
36
|
+
| \>= v0.19.0 | \>= v0.11.0 |
|
37
|
+
| \>= v0.18.0 | \>= v0.10.0 |
|
38
|
+
| \>= v0.17.0 | \>= v0.9.0 |
|
39
|
+
| \>= v0.16.0 | \>= v0.8.0 |
|
37
40
|
| \>= v0.15.0 | \>= v0.7.0 |
|
38
41
|
| \>= v0.12.1 | \>= v0.5.0 |
|
39
42
|
| \>= v0.12.0 | \>= v0.4.0 |
|
data/examples/aliases.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# These examples walk you through operations specifically related to aliases
|
5
|
-
# # This is a Typesense Premium feature (see: https://typesense.org/premium)
|
6
|
-
# Be sure to add `--license-key=<>` as a parameter, when starting a Typesense Premium server
|
7
5
|
|
8
6
|
require_relative './client_initialization'
|
9
7
|
|
@@ -12,7 +12,7 @@ AwesomePrint.defaults = {
|
|
12
12
|
## Setup
|
13
13
|
#
|
14
14
|
### Option 1: Start a single-node cluster
|
15
|
-
# $ docker run -i -p 8108:8108 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.
|
15
|
+
# $ docker run -i -p 8108:8108 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 8108 --enable-cors
|
16
16
|
#
|
17
17
|
### Option 2: Start a 3-node cluster
|
18
18
|
#
|
@@ -20,13 +20,13 @@ AwesomePrint.defaults = {
|
|
20
20
|
# $ echo '172.17.0.2:8107:8108,172.17.0.3:7107:7108,172.17.0.4:9107:9108' > `pwd`/typesense-server-peers
|
21
21
|
#
|
22
22
|
# Start node 1:
|
23
|
-
# $ docker run -i -p 8108:8108 -p 8107:8107 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.
|
23
|
+
# $ docker run -i -p 8108:8108 -p 8107:8107 -v/tmp/typesense-server-data-1b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 8108 --peering-port 8107 --enable-cors --nodes=/typesense-server-peers
|
24
24
|
#
|
25
25
|
# Start node 2:
|
26
|
-
# $ docker run -i -p 7108:7108 -p 7107:7107 -v/tmp/.typesense-server-data-2b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.
|
26
|
+
# $ docker run -i -p 7108:7108 -p 7107:7107 -v/tmp/.typesense-server-data-2b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 7108 --peering-port 7107 --enable-cors --nodes=/typesense-server-peers
|
27
27
|
#
|
28
28
|
# Start node 3:
|
29
|
-
# $ docker run -i -p 9108:9108 -p 9107:9107 -v/tmp/.typesense-server-data-3b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.
|
29
|
+
# $ docker run -i -p 9108:9108 -p 9107:9107 -v/tmp/.typesense-server-data-3b/:/data -v`pwd`/typesense-server-peers:/typesense-server-peers typesense/typesense:0.19.0 --data-dir /data --api-key=xyz --listen-port 9108 --peering-port 9107 --enable-cors --nodes=/typesense-server-peers
|
30
30
|
#
|
31
31
|
# Note: Be sure to add `--license-key=<>` at the end when starting a Typesense Premium server
|
32
32
|
|
@@ -38,31 +38,31 @@ AwesomePrint.defaults = {
|
|
38
38
|
host: 'localhost',
|
39
39
|
port: 8108,
|
40
40
|
protocol: 'http'
|
41
|
-
},
|
42
|
-
# Uncomment if starting a 3-node cluster, using Option 2 under Setup instructions above
|
43
|
-
{
|
44
|
-
host: 'localhost',
|
45
|
-
port: 7108,
|
46
|
-
protocol: 'http'
|
47
|
-
},
|
48
|
-
{
|
49
|
-
host: 'localhost',
|
50
|
-
port: 9108,
|
51
|
-
protocol: 'http'
|
52
41
|
}
|
42
|
+
# Uncomment if starting a 3-node cluster, using Option 2 under Setup instructions above
|
43
|
+
# {
|
44
|
+
# host: 'localhost',
|
45
|
+
# port: 7108,
|
46
|
+
# protocol: 'http'
|
47
|
+
# },
|
48
|
+
# {
|
49
|
+
# host: 'localhost',
|
50
|
+
# port: 9108,
|
51
|
+
# protocol: 'http'
|
52
|
+
# }
|
53
53
|
],
|
54
54
|
# If this optional key is specified, requests are always sent to this node first if it is healthy
|
55
55
|
# before falling back on the nodes mentioned in the `nodes` key. This is useful when running a distributed set of search clusters.
|
56
|
-
'nearest_node': {
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
},
|
56
|
+
# 'nearest_node': {
|
57
|
+
# 'host': 'localhost',
|
58
|
+
# 'port': '8108',
|
59
|
+
# 'protocol': 'http'
|
60
|
+
# },
|
61
61
|
api_key: 'xyz',
|
62
62
|
num_retries: 10,
|
63
63
|
healthcheck_interval_seconds: 1,
|
64
64
|
retry_interval_seconds: 0.01,
|
65
65
|
connection_timeout_seconds: 10,
|
66
66
|
logger: Logger.new($stdout),
|
67
|
-
log_level: Logger::
|
67
|
+
log_level: Logger::INFO
|
68
68
|
)
|
@@ -167,6 +167,10 @@ ap document
|
|
167
167
|
# "num_employees" => 5215
|
168
168
|
# }
|
169
169
|
|
170
|
+
# You can also upsert a document, which will update the document if it already exists or create a new one if it doesn't exist
|
171
|
+
document = @typesense.collections['companies'].documents.upsert(document)
|
172
|
+
ap document
|
173
|
+
|
170
174
|
##
|
171
175
|
# Retrieve a document
|
172
176
|
sleep 0.5 # Give Typesense cluster a few hundred ms to create the document on all nodes, before reading it right after (eventually consistent)
|
@@ -180,6 +184,25 @@ ap document
|
|
180
184
|
# "num_employees" => 5215
|
181
185
|
# }
|
182
186
|
|
187
|
+
##
|
188
|
+
# Update a document. Unlike upsert, update will error out if the doc doesn't already exist.
|
189
|
+
document = @typesense.collections['companies'].documents['124'].update(
|
190
|
+
'id' => 1,
|
191
|
+
'num_employees' => 5500
|
192
|
+
)
|
193
|
+
ap document
|
194
|
+
|
195
|
+
# {
|
196
|
+
# "id" => "124",
|
197
|
+
# "num_employees" => 5500
|
198
|
+
# }
|
199
|
+
|
200
|
+
# This should error out, since document 145 doesn't exist
|
201
|
+
# document = @typesense.collections['companies'].documents['145'].update(
|
202
|
+
# 'num_employees' => 5500
|
203
|
+
# )
|
204
|
+
# ap document
|
205
|
+
|
183
206
|
##
|
184
207
|
# Delete a document
|
185
208
|
# Deleting a document, returns the document after deletion
|
@@ -208,11 +231,36 @@ documents = [
|
|
208
231
|
'country' => 'France'
|
209
232
|
}
|
210
233
|
]
|
211
|
-
ap @typesense.collections['companies'].documents.
|
234
|
+
ap @typesense.collections['companies'].documents.import(documents)
|
212
235
|
|
213
|
-
## If you already have documents in JSONL format, you can also
|
236
|
+
## If you already have documents in JSONL format, you can also pass it directly to #import, to avoid the JSON parsing overhead:
|
214
237
|
# @typesense.collections['companies'].documents.import(documents_in_jsonl_format)
|
215
238
|
|
239
|
+
## You can bulk upsert documents, by adding an upsert action option to #import
|
240
|
+
documents << {
|
241
|
+
'id' => '126',
|
242
|
+
'company_name' => 'Stark Industries 2',
|
243
|
+
'num_employees' => 200,
|
244
|
+
'country' => 'USA'
|
245
|
+
}
|
246
|
+
ap @typesense.collections['companies'].documents.import(documents, action: :upsert)
|
247
|
+
|
248
|
+
## You can bulk update documents, by adding an update action option to #import
|
249
|
+
# `action: update` will throw an error if the document doesn't already exist
|
250
|
+
# This document will error out, since id: 1200 doesn't exist
|
251
|
+
documents << {
|
252
|
+
'id' => '1200',
|
253
|
+
'country' => 'USA'
|
254
|
+
}
|
255
|
+
documents << {
|
256
|
+
'id' => '126',
|
257
|
+
'num_employees' => 300
|
258
|
+
}
|
259
|
+
ap @typesense.collections['companies'].documents.import(documents, action: :update)
|
260
|
+
|
261
|
+
## You can also bulk delete documents, using filter_by fields:
|
262
|
+
ap @typesense.collections['companies'].documents.delete(filter_by: 'num_employees:>100')
|
263
|
+
|
216
264
|
##
|
217
265
|
# Export all documents in a collection in JSON Lines format
|
218
266
|
# We use JSON Lines format for performance reasons. You can choose to parse selected lines as needed, by splitting on \n.
|
data/examples/overrides.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
##
|
4
|
-
# These examples walk you through operations specifically related to overrides
|
5
|
-
# This is a Typesense Premium feature (see: https://typesense.org/premium)
|
6
|
-
# Be sure to add `--license-key=<>` as a parameter, when starting a Typesense Premium server
|
4
|
+
# These examples walk you through operations specifically related to result overrides / curation
|
7
5
|
|
8
6
|
require_relative './client_initialization'
|
9
7
|
|
8
|
+
# Delete the collection if it already exists
|
9
|
+
begin
|
10
|
+
@typesense.collections['companies'].delete
|
11
|
+
rescue Typesense::Error::ObjectNotFound
|
12
|
+
end
|
13
|
+
|
10
14
|
##
|
11
15
|
# Create a collection
|
12
16
|
schema = {
|
@@ -63,16 +67,16 @@ schema = {
|
|
63
67
|
##
|
64
68
|
# Create overrides
|
65
69
|
|
66
|
-
@typesense.collections['companies'].overrides.
|
67
|
-
|
70
|
+
@typesense.collections['companies'].overrides.upsert(
|
71
|
+
'promote-doofenshmirtz',
|
68
72
|
"rule": {
|
69
73
|
"query": 'doofen',
|
70
74
|
"match": 'exact'
|
71
75
|
},
|
72
76
|
"includes": [{ 'id' => '126', 'position' => 1 }]
|
73
77
|
)
|
74
|
-
@typesense.collections['companies'].overrides.
|
75
|
-
|
78
|
+
@typesense.collections['companies'].overrides.upsert(
|
79
|
+
'promote-acme',
|
76
80
|
"rule": {
|
77
81
|
"query": 'stark',
|
78
82
|
"match": 'exact'
|
data/examples/search.rb
CHANGED
@@ -73,37 +73,6 @@ results = @typesense.collections['companies'].documents.search(
|
|
73
73
|
)
|
74
74
|
ap results
|
75
75
|
|
76
|
-
# {
|
77
|
-
# "facet_counts" => [],
|
78
|
-
# "found" => 2,
|
79
|
-
# "hits" => [
|
80
|
-
# [0] {
|
81
|
-
# "document" => {
|
82
|
-
# "company_name" => "Stark Industries",
|
83
|
-
# "country" => "USA",
|
84
|
-
# "id" => "124",
|
85
|
-
# "num_employees" => 5215
|
86
|
-
# },
|
87
|
-
# "highlight" => {
|
88
|
-
# "company_name" => "<mark>Stark</mark> Industries"
|
89
|
-
# }
|
90
|
-
# },
|
91
|
-
# [1] {
|
92
|
-
# "document" => {
|
93
|
-
# "company_name" => "Stark Corp",
|
94
|
-
# "country" => "USA",
|
95
|
-
# "id" => "127",
|
96
|
-
# "num_employees" => 1031
|
97
|
-
# },
|
98
|
-
# "highlight" => {
|
99
|
-
# "company_name" => "<mark>Stark</mark> Corp"
|
100
|
-
# }
|
101
|
-
# }
|
102
|
-
# ],
|
103
|
-
# "page" => 1,
|
104
|
-
# "search_time_ms" => 0
|
105
|
-
# }
|
106
|
-
|
107
76
|
##
|
108
77
|
# Search for more documents
|
109
78
|
results = @typesense.collections['companies'].documents.search(
|
@@ -114,25 +83,28 @@ results = @typesense.collections['companies'].documents.search(
|
|
114
83
|
)
|
115
84
|
ap results
|
116
85
|
|
117
|
-
|
118
|
-
#
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
#
|
134
|
-
|
135
|
-
|
86
|
+
##
|
87
|
+
# Search for more multiple documents
|
88
|
+
results = @typesense.multi_search.perform(
|
89
|
+
{
|
90
|
+
searches: [
|
91
|
+
{
|
92
|
+
'q' => 'Inc',
|
93
|
+
'filter_by' => 'num_employees:<100',
|
94
|
+
'sort_by' => 'num_employees:desc'
|
95
|
+
},
|
96
|
+
{
|
97
|
+
'q' => 'Stark'
|
98
|
+
}
|
99
|
+
]
|
100
|
+
},
|
101
|
+
{
|
102
|
+
# Parameters that are common to all searches, can be mentioned here
|
103
|
+
'collection' => 'companies',
|
104
|
+
'query_by' => 'company_name'
|
105
|
+
}
|
106
|
+
)
|
107
|
+
ap results
|
136
108
|
|
137
109
|
##
|
138
110
|
# Search for more documents
|
@@ -142,13 +114,6 @@ results = @typesense.collections['companies'].documents.search(
|
|
142
114
|
)
|
143
115
|
ap results
|
144
116
|
|
145
|
-
# {
|
146
|
-
# "found" => 0,
|
147
|
-
# "hits" => [],
|
148
|
-
# "page" => 1,
|
149
|
-
# "search_time_ms" => 0
|
150
|
-
# }
|
151
|
-
|
152
117
|
##
|
153
118
|
# Cleanup
|
154
119
|
# Drop the collection
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# These examples walk you through operations specifically related to synonyms
|
5
|
+
|
6
|
+
require_relative './client_initialization'
|
7
|
+
|
8
|
+
# Delete the collection if it already exists
|
9
|
+
begin
|
10
|
+
@typesense.collections['companies'].delete
|
11
|
+
rescue Typesense::Error::ObjectNotFound
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Create a collection
|
16
|
+
schema = {
|
17
|
+
'name' => 'companies',
|
18
|
+
'fields' => [
|
19
|
+
{
|
20
|
+
'name' => 'company_name',
|
21
|
+
'type' => 'string'
|
22
|
+
},
|
23
|
+
{
|
24
|
+
'name' => 'num_employees',
|
25
|
+
'type' => 'int32'
|
26
|
+
},
|
27
|
+
{
|
28
|
+
'name' => 'country',
|
29
|
+
'type' => 'string',
|
30
|
+
'facet' => true
|
31
|
+
}
|
32
|
+
],
|
33
|
+
'default_sorting_field' => 'num_employees'
|
34
|
+
}
|
35
|
+
|
36
|
+
@typesense.collections.create(schema)
|
37
|
+
|
38
|
+
# Let's create a couple documents for us to use in our search examples
|
39
|
+
@typesense.collections['companies'].documents.create(
|
40
|
+
'id' => '124',
|
41
|
+
'company_name' => 'Stark Industries',
|
42
|
+
'num_employees' => 5215,
|
43
|
+
'country' => 'USA'
|
44
|
+
)
|
45
|
+
|
46
|
+
@typesense.collections['companies'].documents.create(
|
47
|
+
'id' => '127',
|
48
|
+
'company_name' => 'Stark Corp',
|
49
|
+
'num_employees' => 1031,
|
50
|
+
'country' => 'USA'
|
51
|
+
)
|
52
|
+
|
53
|
+
@typesense.collections['companies'].documents.create(
|
54
|
+
'id' => '125',
|
55
|
+
'company_name' => 'Acme Corp',
|
56
|
+
'num_employees' => 1002,
|
57
|
+
'country' => 'France'
|
58
|
+
)
|
59
|
+
|
60
|
+
@typesense.collections['companies'].documents.create(
|
61
|
+
'id' => '126',
|
62
|
+
'company_name' => 'Doofenshmirtz Inc',
|
63
|
+
'num_employees' => 2,
|
64
|
+
'country' => 'Tri-State Area'
|
65
|
+
)
|
66
|
+
|
67
|
+
##
|
68
|
+
# Create synonyms
|
69
|
+
|
70
|
+
ap @typesense.collections['companies'].synonyms.upsert(
|
71
|
+
'synonyms-doofenshmirtz',
|
72
|
+
{
|
73
|
+
'synonyms' => %w[Doofenshmirtz Heinz Evil]
|
74
|
+
}
|
75
|
+
)
|
76
|
+
|
77
|
+
##
|
78
|
+
# Search for documents
|
79
|
+
# Should return Doofenshmirtz Inc, since it's set as a synonym
|
80
|
+
results = @typesense.collections['companies'].documents.search(
|
81
|
+
'q' => 'Heinz',
|
82
|
+
'query_by' => 'company_name'
|
83
|
+
)
|
84
|
+
ap results
|
85
|
+
|
86
|
+
##
|
87
|
+
# List all synonyms
|
88
|
+
ap @typesense.collections['companies'].synonyms.retrieve
|
89
|
+
|
90
|
+
##
|
91
|
+
# Retrieve specific synonym
|
92
|
+
ap @typesense.collections['companies'].synonyms['synonyms-doofenshmirtz'].retrieve
|
93
|
+
|
94
|
+
##
|
95
|
+
# Update synonym to a one-way synonym
|
96
|
+
ap @typesense.collections['companies'].synonyms.upsert(
|
97
|
+
'synonyms-doofenshmirtz',
|
98
|
+
{
|
99
|
+
'root' => 'Evil',
|
100
|
+
'synonyms' => %w[Doofenshmirtz Heinz]
|
101
|
+
}
|
102
|
+
)
|
103
|
+
|
104
|
+
##
|
105
|
+
# Search for documents
|
106
|
+
# Should return Doofenshmirtz Inc, since it's set as a synonym
|
107
|
+
results = @typesense.collections['companies'].documents.search(
|
108
|
+
'q' => 'Evil',
|
109
|
+
'query_by' => 'company_name'
|
110
|
+
)
|
111
|
+
ap results
|
112
|
+
|
113
|
+
# Should not return any results, since this is a one-way synonym
|
114
|
+
results = @typesense.collections['companies'].documents.search(
|
115
|
+
'q' => 'Heinz',
|
116
|
+
'query_by' => 'company_name'
|
117
|
+
)
|
118
|
+
ap results
|
119
|
+
|
120
|
+
##
|
121
|
+
# Delete synonym
|
122
|
+
ap @typesense.collections['companies'].synonyms['synonyms-doofenshmirtz'].delete
|
123
|
+
|
124
|
+
##
|
125
|
+
# Cleanup
|
126
|
+
# Drop the collection
|
127
|
+
@typesense.collections['companies'].delete
|
data/lib/typesense.rb
CHANGED
@@ -13,11 +13,15 @@ require_relative 'typesense/documents'
|
|
13
13
|
require_relative 'typesense/document'
|
14
14
|
require_relative 'typesense/overrides'
|
15
15
|
require_relative 'typesense/override'
|
16
|
+
require_relative 'typesense/synonyms'
|
17
|
+
require_relative 'typesense/synonym'
|
16
18
|
require_relative 'typesense/aliases'
|
17
19
|
require_relative 'typesense/alias'
|
18
20
|
require_relative 'typesense/keys'
|
19
21
|
require_relative 'typesense/key'
|
22
|
+
require_relative 'typesense/multi_search'
|
20
23
|
require_relative 'typesense/debug'
|
21
24
|
require_relative 'typesense/health'
|
22
25
|
require_relative 'typesense/metrics'
|
26
|
+
require_relative 'typesense/operations'
|
23
27
|
require_relative 'typesense/error'
|
data/lib/typesense/api_call.rb
CHANGED
@@ -7,6 +7,8 @@ module Typesense
|
|
7
7
|
class ApiCall
|
8
8
|
API_KEY_HEADER_NAME = 'X-TYPESENSE-API-KEY'
|
9
9
|
|
10
|
+
attr_reader :logger
|
11
|
+
|
10
12
|
def initialize(configuration)
|
11
13
|
@configuration = configuration
|
12
14
|
|
@@ -24,45 +26,40 @@ module Typesense
|
|
24
26
|
@current_node_index = -1
|
25
27
|
end
|
26
28
|
|
27
|
-
def post(endpoint,
|
28
|
-
headers, query, body = split_post_put_parameters(parameters)
|
29
|
-
|
29
|
+
def post(endpoint, body_parameters = {}, query_parameters = {})
|
30
30
|
perform_request :post,
|
31
31
|
endpoint,
|
32
|
-
|
33
|
-
|
34
|
-
body: body
|
32
|
+
query_parameters: query_parameters,
|
33
|
+
body_parameters: body_parameters
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
38
|
-
|
36
|
+
def patch(endpoint, body_parameters = {}, query_parameters = {})
|
37
|
+
perform_request :patch,
|
38
|
+
endpoint,
|
39
|
+
query_parameters: query_parameters,
|
40
|
+
body_parameters: body_parameters
|
41
|
+
end
|
39
42
|
|
43
|
+
def put(endpoint, body_parameters = {}, query_parameters = {})
|
40
44
|
perform_request :put,
|
41
45
|
endpoint,
|
42
|
-
|
43
|
-
|
44
|
-
body: body
|
46
|
+
query_parameters: query_parameters,
|
47
|
+
body_parameters: body_parameters
|
45
48
|
end
|
46
49
|
|
47
|
-
def get(endpoint,
|
48
|
-
headers, query = extract_headers_and_query_from(parameters)
|
49
|
-
|
50
|
+
def get(endpoint, query_parameters = {})
|
50
51
|
perform_request :get,
|
51
52
|
endpoint,
|
52
|
-
|
53
|
-
params: query
|
53
|
+
query_parameters: query_parameters
|
54
54
|
end
|
55
55
|
|
56
|
-
def delete(endpoint,
|
57
|
-
headers, query = extract_headers_and_query_from(parameters)
|
58
|
-
|
56
|
+
def delete(endpoint, query_parameters = {})
|
59
57
|
perform_request :delete,
|
60
58
|
endpoint,
|
61
|
-
|
62
|
-
params: query
|
59
|
+
query_parameters: query_parameters
|
63
60
|
end
|
64
61
|
|
65
|
-
def perform_request(method, endpoint,
|
62
|
+
def perform_request(method, endpoint, query_parameters: nil, body_parameters: nil, additional_headers: {})
|
66
63
|
@configuration.validate!
|
67
64
|
last_exception = nil
|
68
65
|
@logger.debug "Performing #{method.to_s.upcase} request: #{endpoint}"
|
@@ -72,15 +69,23 @@ module Typesense
|
|
72
69
|
@logger.debug "Attempting #{method.to_s.upcase} request Try ##{num_tries} to Node #{node[:index]}"
|
73
70
|
|
74
71
|
begin
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
72
|
+
request_options = {
|
73
|
+
method: method,
|
74
|
+
timeout: @connection_timeout_seconds,
|
75
|
+
headers: default_headers.merge(additional_headers)
|
76
|
+
}
|
77
|
+
request_options.merge!(params: query_parameters) unless query_parameters.nil?
|
78
|
+
|
79
|
+
unless body_parameters.nil?
|
80
|
+
body = body_parameters
|
81
|
+
body = Oj.dump(body_parameters, mode: :compat) if request_options[:headers]['Content-Type'] == 'application/json'
|
82
|
+
request_options.merge!(body: body)
|
83
|
+
end
|
84
|
+
|
85
|
+
response = Typhoeus::Request.new(uri_for(endpoint, node), request_options).run
|
81
86
|
set_node_healthcheck(node, is_healthy: true) if response.code >= 1 && response.code <= 499
|
82
87
|
|
83
|
-
@logger.debug "Request to Node #{node[:index]} was successfully made (at the network layer). Response Code was #{response.code}."
|
88
|
+
@logger.debug "Request #{method}:#{uri_for(endpoint, node)} to Node #{node[:index]} was successfully made (at the network layer). Response Code was #{response.code}."
|
84
89
|
|
85
90
|
parsed_response = if response.headers && (response.headers['content-type'] || '').include?('application/json')
|
86
91
|
Oj.load(response.body)
|
@@ -101,7 +106,7 @@ module Typesense
|
|
101
106
|
# other languages that might not support the same construct.
|
102
107
|
set_node_healthcheck(node, is_healthy: false)
|
103
108
|
last_exception = e
|
104
|
-
@logger.warn "Request to Node #{node[:index]} failed due to \"#{e.class}: #{e.message}\""
|
109
|
+
@logger.warn "Request #{method}:#{uri_for(endpoint, node)} to Node #{node[:index]} failed due to \"#{e.class}: #{e.message}\""
|
105
110
|
@logger.warn "Sleeping for #{@retry_interval_seconds}s and then retrying request..."
|
106
111
|
sleep @retry_interval_seconds
|
107
112
|
end
|
@@ -112,43 +117,6 @@ module Typesense
|
|
112
117
|
|
113
118
|
private
|
114
119
|
|
115
|
-
def split_post_put_parameters(parameters)
|
116
|
-
if json_request?(parameters)
|
117
|
-
headers = { 'Content-Type' => 'application/json' }
|
118
|
-
query = parameters[:query]
|
119
|
-
body = Oj.dump(sanitize_parameters(parameters))
|
120
|
-
else
|
121
|
-
headers = {}
|
122
|
-
query = parameters[:query]
|
123
|
-
body = parameters[:body]
|
124
|
-
end
|
125
|
-
[headers, query, body]
|
126
|
-
end
|
127
|
-
|
128
|
-
def extract_headers_and_query_from(parameters)
|
129
|
-
if json_request?(parameters)
|
130
|
-
headers = { 'Content-Type' => 'application/json' }
|
131
|
-
query = sanitize_parameters(parameters)
|
132
|
-
else
|
133
|
-
headers = {}
|
134
|
-
query = parameters[:query]
|
135
|
-
end
|
136
|
-
[headers, query]
|
137
|
-
end
|
138
|
-
|
139
|
-
def json_request?(parameters)
|
140
|
-
parameters[:as_json].nil? ? true : parameters[:as_json]
|
141
|
-
end
|
142
|
-
|
143
|
-
def sanitize_parameters(parameters)
|
144
|
-
sanitized_parameters = parameters.dup
|
145
|
-
sanitized_parameters.delete(:as_json)
|
146
|
-
sanitized_parameters.delete(:body)
|
147
|
-
sanitized_parameters.delete(:query)
|
148
|
-
|
149
|
-
sanitized_parameters
|
150
|
-
end
|
151
|
-
|
152
120
|
def uri_for(endpoint, node)
|
153
121
|
"#{node[:protocol]}://#{node[:host]}:#{node[:port]}#{endpoint}"
|
154
122
|
end
|
@@ -209,28 +177,29 @@ module Typesense
|
|
209
177
|
|
210
178
|
def custom_exception_klass_for(response)
|
211
179
|
if response.code == 400
|
212
|
-
Typesense::Error::RequestMalformed
|
180
|
+
Typesense::Error::RequestMalformed.new(response: response)
|
213
181
|
elsif response.code == 401
|
214
|
-
Typesense::Error::RequestUnauthorized
|
182
|
+
Typesense::Error::RequestUnauthorized.new(response: response)
|
215
183
|
elsif response.code == 404
|
216
|
-
Typesense::Error::ObjectNotFound
|
184
|
+
Typesense::Error::ObjectNotFound.new(response: response)
|
217
185
|
elsif response.code == 409
|
218
|
-
Typesense::Error::ObjectAlreadyExists
|
186
|
+
Typesense::Error::ObjectAlreadyExists.new(response: response)
|
219
187
|
elsif response.code == 422
|
220
|
-
Typesense::Error::ObjectUnprocessable
|
188
|
+
Typesense::Error::ObjectUnprocessable.new(response: response)
|
221
189
|
elsif response.code >= 500 && response.code <= 599
|
222
|
-
Typesense::Error::ServerError
|
190
|
+
Typesense::Error::ServerError.new(response: response)
|
223
191
|
elsif response.timed_out?
|
224
|
-
Typesense::Error::TimeoutError
|
192
|
+
Typesense::Error::TimeoutError.new(response: response)
|
225
193
|
elsif response.code.zero?
|
226
|
-
Typesense::Error::HTTPStatus0Error
|
194
|
+
Typesense::Error::HTTPStatus0Error.new(response: response)
|
227
195
|
else
|
228
|
-
Typesense::Error::HTTPError
|
196
|
+
Typesense::Error::HTTPError.new(response: response)
|
229
197
|
end
|
230
198
|
end
|
231
199
|
|
232
200
|
def default_headers
|
233
201
|
{
|
202
|
+
'Content-Type' => 'application/json',
|
234
203
|
API_KEY_HEADER_NAME.to_s => @api_key,
|
235
204
|
'User-Agent' => 'Typesense Ruby Client'
|
236
205
|
}
|
data/lib/typesense/client.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module Typesense
|
4
4
|
class Client
|
5
|
-
attr_reader :configuration, :collections, :aliases, :keys, :debug, :health, :metrics
|
5
|
+
attr_reader :configuration, :collections, :aliases, :keys, :debug, :health, :metrics, :operations,
|
6
|
+
:multi_search
|
6
7
|
|
7
8
|
def initialize(options = {})
|
8
9
|
@configuration = Configuration.new(options)
|
@@ -10,9 +11,11 @@ module Typesense
|
|
10
11
|
@collections = Collections.new(@api_call)
|
11
12
|
@aliases = Aliases.new(@api_call)
|
12
13
|
@keys = Keys.new(@api_call)
|
14
|
+
@multi_search = MultiSearch.new(@api_call)
|
13
15
|
@debug = Debug.new(@api_call)
|
14
16
|
@health = Health.new(@api_call)
|
15
17
|
@metrics = Metrics.new(@api_call)
|
18
|
+
@operations = Operations.new(@api_call)
|
16
19
|
end
|
17
20
|
end
|
18
21
|
end
|
data/lib/typesense/collection.rb
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
module Typesense
|
4
4
|
class Collection
|
5
|
-
attr_reader :documents, :overrides
|
5
|
+
attr_reader :documents, :overrides, :synonyms
|
6
6
|
|
7
7
|
def initialize(name, api_call)
|
8
8
|
@name = name
|
9
9
|
@api_call = api_call
|
10
10
|
@documents = Documents.new(@name, @api_call)
|
11
11
|
@overrides = Overrides.new(@name, @api_call)
|
12
|
+
@synonyms = Synonyms.new(@name, @api_call)
|
12
13
|
end
|
13
14
|
|
14
15
|
def retrieve
|
data/lib/typesense/document.rb
CHANGED
data/lib/typesense/documents.rb
CHANGED
@@ -16,17 +16,40 @@ module Typesense
|
|
16
16
|
@api_call.post(endpoint_path, document)
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
results_in_jsonl_format = import(documents_in_jsonl_format)
|
22
|
-
results_in_jsonl_format.split("\n").map { |r| Oj.load(r) }
|
19
|
+
def upsert(document)
|
20
|
+
@api_call.post(endpoint_path, document, action: :upsert)
|
23
21
|
end
|
24
22
|
|
25
|
-
def
|
26
|
-
@api_call.post(endpoint_path
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
def update(document)
|
24
|
+
@api_call.post(endpoint_path, document, action: :update)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_many(documents, options = {})
|
28
|
+
@api_call.logger.warn('#create_many is deprecated and will be removed in a future version. Use #import instead, which now takes both an array of documents or a JSONL string of documents')
|
29
|
+
import(documents, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [Array,String] documents An array of document hashes or a JSONL string of documents.
|
33
|
+
def import(documents, options = {})
|
34
|
+
documents_in_jsonl_format = if documents.is_a?(Array)
|
35
|
+
documents.map { |document| Oj.dump(document) }.join("\n")
|
36
|
+
else
|
37
|
+
documents
|
38
|
+
end
|
39
|
+
|
40
|
+
results_in_jsonl_format = @api_call.perform_request(
|
41
|
+
'post',
|
42
|
+
endpoint_path('import'),
|
43
|
+
query_parameters: options,
|
44
|
+
body_parameters: documents_in_jsonl_format,
|
45
|
+
additional_headers: { 'Content-Type' => 'text/plain' }
|
46
|
+
)
|
47
|
+
|
48
|
+
if documents.is_a?(Array)
|
49
|
+
results_in_jsonl_format.split("\n").map { |r| Oj.load(r) }
|
50
|
+
else
|
51
|
+
results_in_jsonl_format
|
52
|
+
end
|
30
53
|
end
|
31
54
|
|
32
55
|
def export
|
@@ -41,6 +64,10 @@ module Typesense
|
|
41
64
|
@documents[document_id] ||= Document.new(@collection_name, document_id, @api_call)
|
42
65
|
end
|
43
66
|
|
67
|
+
def delete(query_parameters = {})
|
68
|
+
@api_call.delete(endpoint_path, query_parameters)
|
69
|
+
end
|
70
|
+
|
44
71
|
private
|
45
72
|
|
46
73
|
def endpoint_path(operation = nil)
|
data/lib/typesense/error.rb
CHANGED
data/lib/typesense/keys.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Typesense
|
4
|
+
class MultiSearch
|
5
|
+
RESOURCE_PATH = '/multi_search'
|
6
|
+
|
7
|
+
def initialize(api_call)
|
8
|
+
@api_call = api_call
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform(searches, query_params = {})
|
12
|
+
@api_call.post(RESOURCE_PATH, searches, query_params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Typesense
|
4
|
+
class Operations
|
5
|
+
RESOURCE_PATH = '/operations'
|
6
|
+
|
7
|
+
def initialize(api_call)
|
8
|
+
@api_call = api_call
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform(operation_name, query_params = {})
|
12
|
+
@api_call.post("#{RESOURCE_PATH}/#{operation_name}", {}, query_params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/typesense/overrides.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Typesense
|
4
|
+
class Synonym
|
5
|
+
def initialize(collection_name, synonym_id, api_call)
|
6
|
+
@collection_name = collection_name
|
7
|
+
@synonym_id = synonym_id
|
8
|
+
@api_call = api_call
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve
|
12
|
+
@api_call.get(endpoint_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
@api_call.delete(endpoint_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def endpoint_path
|
22
|
+
"#{Collections::RESOURCE_PATH}/#{@collection_name}#{Synonyms::RESOURCE_PATH}/#{@synonym_id}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Typesense
|
4
|
+
class Synonyms
|
5
|
+
RESOURCE_PATH = '/synonyms'
|
6
|
+
|
7
|
+
def initialize(collection_name, api_call)
|
8
|
+
@collection_name = collection_name
|
9
|
+
@api_call = api_call
|
10
|
+
@synonyms = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def upsert(synonym_id, params)
|
14
|
+
@api_call.put(endpoint_path(synonym_id), params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def retrieve
|
18
|
+
@api_call.get(endpoint_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](synonym_id)
|
22
|
+
@synonyms[synonym_id] ||= Synonym.new(@collection_name, synonym_id, @api_call)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def endpoint_path(operation = nil)
|
28
|
+
"#{Collections::RESOURCE_PATH}/#{@collection_name}#{Synonyms::RESOURCE_PATH}#{operation.nil? ? '' : "/#{operation}"}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/typesense/version.rb
CHANGED
data/typesense.gemspec
CHANGED
@@ -40,6 +40,6 @@ Gem::Specification.new do |spec|
|
|
40
40
|
spec.add_development_dependency 'timecop', '~> 0.9'
|
41
41
|
spec.add_development_dependency 'webmock', '~> 3.8'
|
42
42
|
|
43
|
-
spec.add_dependency 'oj', '~> 3.
|
43
|
+
spec.add_dependency 'oj', '~> 3.11'
|
44
44
|
spec.add_dependency 'typhoeus', '~> 1.4'
|
45
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typesense
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Typesense, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|
@@ -226,14 +226,14 @@ dependencies:
|
|
226
226
|
requirements:
|
227
227
|
- - "~>"
|
228
228
|
- !ruby/object:Gem::Version
|
229
|
-
version: '3.
|
229
|
+
version: '3.11'
|
230
230
|
type: :runtime
|
231
231
|
prerelease: false
|
232
232
|
version_requirements: !ruby/object:Gem::Requirement
|
233
233
|
requirements:
|
234
234
|
- - "~>"
|
235
235
|
- !ruby/object:Gem::Version
|
236
|
-
version: '3.
|
236
|
+
version: '3.11'
|
237
237
|
- !ruby/object:Gem::Dependency
|
238
238
|
name: typhoeus
|
239
239
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,6 +274,7 @@ files:
|
|
274
274
|
- examples/keys.rb
|
275
275
|
- examples/overrides.rb
|
276
276
|
- examples/search.rb
|
277
|
+
- examples/synonyms.rb
|
277
278
|
- lib/typesense.rb
|
278
279
|
- lib/typesense/alias.rb
|
279
280
|
- lib/typesense/aliases.rb
|
@@ -290,8 +291,12 @@ files:
|
|
290
291
|
- lib/typesense/key.rb
|
291
292
|
- lib/typesense/keys.rb
|
292
293
|
- lib/typesense/metrics.rb
|
294
|
+
- lib/typesense/multi_search.rb
|
295
|
+
- lib/typesense/operations.rb
|
293
296
|
- lib/typesense/override.rb
|
294
297
|
- lib/typesense/overrides.rb
|
298
|
+
- lib/typesense/synonym.rb
|
299
|
+
- lib/typesense/synonyms.rb
|
295
300
|
- lib/typesense/version.rb
|
296
301
|
- typesense.gemspec
|
297
302
|
homepage: https://typesense.org
|
@@ -309,9 +314,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
309
314
|
version: '2.4'
|
310
315
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
311
316
|
requirements:
|
312
|
-
- - "
|
317
|
+
- - ">="
|
313
318
|
- !ruby/object:Gem::Version
|
314
|
-
version:
|
319
|
+
version: '0'
|
315
320
|
requirements: []
|
316
321
|
rubygems_version: 3.0.6
|
317
322
|
signing_key:
|