typesense 0.7.0 → 0.11.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f663250e6124fb4e46d1cba2ff8b33747b3b34ca5f643e4746193a495a9ec3ef
4
- data.tar.gz: 74a083d9ba5302be52473bff5cfb5588456d8744c886647050abfd84c7451997
3
+ metadata.gz: 47b1bddb975dd2055704e6184451e05a2e9fd05585021068f788610963044ebd
4
+ data.tar.gz: 326c4515546569fdc3bc01b5681369aa75a1242ae112f1e7f40b36320ec56d20
5
5
  SHA512:
6
- metadata.gz: 229c87aab72ca56db2e4e4c1677a6e606f007617f7d967584e75c7c5a4e48c63d65f198b9d7659a0570708b7babb1eb480bce41673f736aad6ec3fc3674fe752
7
- data.tar.gz: 61aaf27f9a5f8fdbc7eb35ac2d50e8c29025297c56a6ed8421cb45f02e9eab6566ee3d1bd61caa47f7bda59543677d2c8bcf36b63eb2733c5fe477fdad9b043e
6
+ metadata.gz: 64918475f966d9fdc1625c25a5e6c9273dc88a71ca8b3cbe3fed2d4387cc58c694fc0d6285588c55c960353e3bf2782df3ef36ff4e1cccd4633d47740b81f482
7
+ data.tar.gz: d9f8381fba8ffcfa82034fe5f62d3f176a7a8485876848ea75dd2cfde7798483a909c756acc5670ae18fd876f6bc570943ccc986d56e5816fa7f6abc8f1b645f
data/README.md CHANGED
@@ -33,6 +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.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 |
36
40
  | \>= v0.15.0 | \>= v0.7.0 |
37
41
  | \>= v0.12.1 | \>= v0.5.0 |
38
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.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 8108 --enable-cors
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.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 8108 --peering-port 8107 --enable-cors --nodes=/typesense-server-peers
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.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 7108 --peering-port 7107 --enable-cors --nodes=/typesense-server-peers
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.12.1.rc1 --data-dir /data --api-key=xyz --listen-port 9108 --peering-port 9107 --enable-cors --nodes=/typesense-server-peers
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
- 'host': 'localhost',
58
- 'port': '8108',
59
- 'protocol': 'http'
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::DEBUG
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.create_many(documents)
234
+ ap @typesense.collections['companies'].documents.import(documents)
212
235
 
213
- ## If you already have documents in JSONL format, you can also use #import instead, to avoid the JSON parsing overhead:
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.
@@ -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.create(
67
- "id": 'promote-doofenshmirtz',
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.create(
75
- "id": 'promote-acme',
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
- # "facet_counts" => [],
119
- # "found" => 1,
120
- # "hits" => [
121
- # [0] {
122
- # "document" => {
123
- # "company_name" => "Doofenshmirtz Inc",
124
- # "country" => "Tri-State Area",
125
- # "id" => "126",
126
- # "num_employees" => 2
127
- # },
128
- # "highlight" => {
129
- # "company_name" => "Doofenshmirtz <mark>Inc</mark>"
130
- # }
131
- # }
132
- # ],
133
- # "page" => 1,
134
- # "search_time_ms" => 0
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'
@@ -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,43 +26,40 @@ module Typesense
24
26
  @current_node_index = -1
25
27
  end
26
28
 
27
- def post(endpoint, parameters = {})
28
- headers, body = extract_headers_and_body_from(parameters)
29
-
29
+ def post(endpoint, body_parameters = {}, query_parameters = {})
30
30
  perform_request :post,
31
31
  endpoint,
32
- headers,
33
- body: body
32
+ query_parameters: query_parameters,
33
+ body_parameters: body_parameters
34
34
  end
35
35
 
36
- def put(endpoint, parameters = {})
37
- headers, body = extract_headers_and_body_from(parameters)
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
38
42
 
43
+ def put(endpoint, body_parameters = {}, query_parameters = {})
39
44
  perform_request :put,
40
45
  endpoint,
41
- headers,
42
- body: body
46
+ query_parameters: query_parameters,
47
+ body_parameters: body_parameters
43
48
  end
44
49
 
45
- def get(endpoint, parameters = {})
46
- headers, query = extract_headers_and_query_from(parameters)
47
-
50
+ def get(endpoint, query_parameters = {})
48
51
  perform_request :get,
49
52
  endpoint,
50
- headers,
51
- params: query
53
+ query_parameters: query_parameters
52
54
  end
53
55
 
54
- def delete(endpoint, parameters = {})
55
- headers, query = extract_headers_and_query_from(parameters)
56
-
56
+ def delete(endpoint, query_parameters = {})
57
57
  perform_request :delete,
58
58
  endpoint,
59
- headers,
60
- params: query
59
+ query_parameters: query_parameters
61
60
  end
62
61
 
63
- def perform_request(method, endpoint, headers = {}, options = {})
62
+ def perform_request(method, endpoint, query_parameters: nil, body_parameters: nil, additional_headers: {})
64
63
  @configuration.validate!
65
64
  last_exception = nil
66
65
  @logger.debug "Performing #{method.to_s.upcase} request: #{endpoint}"
@@ -70,15 +69,23 @@ module Typesense
70
69
  @logger.debug "Attempting #{method.to_s.upcase} request Try ##{num_tries} to Node #{node[:index]}"
71
70
 
72
71
  begin
73
- response = Typhoeus::Request.new(uri_for(endpoint, node),
74
- {
75
- method: method,
76
- headers: default_headers.merge(headers),
77
- timeout: @connection_timeout_seconds
78
- }.merge(options)).run
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
79
86
  set_node_healthcheck(node, is_healthy: true) if response.code >= 1 && response.code <= 499
80
87
 
81
- @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}."
82
89
 
83
90
  parsed_response = if response.headers && (response.headers['content-type'] || '').include?('application/json')
84
91
  Oj.load(response.body)
@@ -99,7 +106,7 @@ module Typesense
99
106
  # other languages that might not support the same construct.
100
107
  set_node_healthcheck(node, is_healthy: false)
101
108
  last_exception = e
102
- @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}\""
103
110
  @logger.warn "Sleeping for #{@retry_interval_seconds}s and then retrying request..."
104
111
  sleep @retry_interval_seconds
105
112
  end
@@ -110,41 +117,6 @@ module Typesense
110
117
 
111
118
  private
112
119
 
113
- def extract_headers_and_body_from(parameters)
114
- if json_request?(parameters)
115
- headers = { 'Content-Type' => 'application/json' }
116
- body = Oj.dump(sanitize_parameters(parameters))
117
- else
118
- headers = {}
119
- body = parameters[:body]
120
- end
121
- [headers, body]
122
- end
123
-
124
- def extract_headers_and_query_from(parameters)
125
- if json_request?(parameters)
126
- headers = { 'Content-Type' => 'application/json' }
127
- query = sanitize_parameters(parameters)
128
- else
129
- headers = {}
130
- query = parameters[:query]
131
- end
132
- [headers, query]
133
- end
134
-
135
- def json_request?(parameters)
136
- parameters[:as_json].nil? ? true : parameters[:as_json]
137
- end
138
-
139
- def sanitize_parameters(parameters)
140
- sanitized_parameters = parameters.dup
141
- sanitized_parameters.delete(:as_json)
142
- sanitized_parameters.delete(:body)
143
- sanitized_parameters.delete(:query)
144
-
145
- sanitized_parameters
146
- end
147
-
148
120
  def uri_for(endpoint, node)
149
121
  "#{node[:protocol]}://#{node[:host]}:#{node[:port]}#{endpoint}"
150
122
  end
@@ -227,6 +199,7 @@ module Typesense
227
199
 
228
200
  def default_headers
229
201
  {
202
+ 'Content-Type' => 'application/json',
230
203
  API_KEY_HEADER_NAME.to_s => @api_key,
231
204
  'User-Agent' => 'Typesense Ruby Client'
232
205
  }
@@ -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
@@ -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
@@ -16,6 +16,10 @@ module Typesense
16
16
  @api_call.delete(endpoint_path)
17
17
  end
18
18
 
19
+ def update(partial_document)
20
+ @api_call.patch(endpoint_path, partial_document)
21
+ end
22
+
19
23
  private
20
24
 
21
25
  def endpoint_path
@@ -16,14 +16,40 @@ module Typesense
16
16
  @api_call.post(endpoint_path, document)
17
17
  end
18
18
 
19
- def create_many(documents)
20
- documents_in_jsonl_format = documents.map { |document| Oj.dump(document) }.join("\n")
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 import(documents_in_jsonl_format)
26
- @api_call.post(endpoint_path('import'), as_json: false, body: documents_in_jsonl_format)
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
27
53
  end
28
54
 
29
55
  def export
@@ -38,6 +64,10 @@ module Typesense
38
64
  @documents[document_id] ||= Document.new(@collection_name, document_id, @api_call)
39
65
  end
40
66
 
67
+ def delete(query_parameters = {})
68
+ @api_call.delete(endpoint_path, query_parameters)
69
+ end
70
+
41
71
  private
42
72
 
43
73
  def endpoint_path(operation = nil)
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'base64'
4
+ require 'json'
5
+ require 'openssl'
4
6
 
5
7
  module Typesense
6
8
  class Keys
@@ -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
@@ -10,8 +10,8 @@ module Typesense
10
10
  @overrides = {}
11
11
  end
12
12
 
13
- def create(params)
14
- @api_call.put(endpoint_path, params)
13
+ def upsert(override_id, params)
14
+ @api_call.put(endpoint_path(override_id), params)
15
15
  end
16
16
 
17
17
  def retrieve
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Typesense
4
- VERSION = '0.7.0'
4
+ VERSION = '0.11.0'
5
5
  end
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.10'
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.7.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Typesense, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-19 00:00:00.000000000 Z
11
+ date: 2021-02-16 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.10'
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.10'
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