qdrant-ruby 0.9.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.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Aliases < Base
5
+ PATH = "aliases"
6
+
7
+ # Get list of all aliases (for a collection)
8
+ def list
9
+ response = client.connection.get("aliases")
10
+ response.body
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Base
5
+ attr_reader :client
6
+
7
+ def initialize(client:)
8
+ @client = client
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "forwardable"
5
+
6
+ module Qdrant
7
+ class Client
8
+ extend Forwardable
9
+
10
+ attr_reader :url, :api_key, :adapter
11
+
12
+ def_delegators :service, :telemetry, :metrics, :locks, :set_lock
13
+
14
+ def initialize(
15
+ url:,
16
+ api_key: nil,
17
+ adapter: Faraday.default_adapter
18
+ )
19
+ @url = url
20
+ @api_key = api_key
21
+ @adapter = adapter
22
+ end
23
+
24
+ def connection
25
+ @connection ||= Faraday.new(url: url) do |faraday|
26
+ if api_key
27
+ faraday.headers["api-key"] = api_key
28
+ end
29
+ faraday.request :json
30
+ faraday.response :json, content_type: /\bjson$/
31
+ faraday.adapter adapter
32
+ end
33
+ end
34
+
35
+ def aliases
36
+ @aliases ||= Qdrant::Aliases.new(client: self).list
37
+ end
38
+
39
+ def collections
40
+ @collections ||= Qdrant::Collections.new(client: self)
41
+ end
42
+
43
+ def snapshots
44
+ @snapshots ||= Qdrant::Snapshots.new(client: self)
45
+ end
46
+
47
+ def service
48
+ @service ||= Qdrant::Service.new(client: self)
49
+ end
50
+
51
+ def clusters
52
+ @clusters ||= Qdrant::Clusters.new(client: self)
53
+ end
54
+
55
+ def points
56
+ @points ||= Qdrant::Points.new(client: self)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Clusters < Base
5
+ PATH = "cluster"
6
+
7
+ # Get information about the current state and composition of the cluster
8
+ def info
9
+ response = client.connection.get(PATH)
10
+ response.body
11
+ end
12
+
13
+ # Tries to recover current peer Raft state.
14
+ def recover
15
+ response = client.connection.post("#{PATH}/recover")
16
+ response.body
17
+ end
18
+
19
+ # Remove peer from the cluster
20
+ def remove_peer(
21
+ peer_id:,
22
+ force: nil
23
+ )
24
+ response = client.connection.post("#{PATH}/recover") do |req|
25
+ req.params["force"] = force if force
26
+ end
27
+ response.body
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Collections < Base
5
+ PATH = "collections"
6
+
7
+ # Get list name of all existing collections
8
+ def list
9
+ response = client.connection.get(PATH)
10
+ response.body
11
+ end
12
+
13
+ # Get detailed information about specified existing collection
14
+ def get(collection_name:)
15
+ response = client.connection.get("#{PATH}/#{collection_name}")
16
+ response.body
17
+ end
18
+
19
+ # Create new collection with given parameters
20
+ def create(
21
+ collection_name:,
22
+ vectors:,
23
+ shard_number: nil,
24
+ replication_factor: nil,
25
+ write_consistency_factor: nil,
26
+ on_disk_payload: nil,
27
+ hnsw_config: nil,
28
+ wal_config: nil,
29
+ optimizers_config: nil,
30
+ init_from: nil,
31
+ quantization_config: nil
32
+ )
33
+ response = client.connection.put("#{PATH}/#{collection_name}") do |req|
34
+ req.body = {}
35
+ req.body["vectors"] = vectors
36
+ req.body["shard_number"] = shard_number unless shard_number.nil?
37
+ req.body["replication_factor"] = replication_factor unless replication_factor.nil?
38
+ req.body["write_consistency_factor"] = write_consistency_factor unless write_consistency_factor.nil?
39
+ req.body["on_disk_payload"] = on_disk_payload unless on_disk_payload.nil?
40
+ req.body["hnsw_config"] = hnsw_config unless hnsw_config.nil?
41
+ req.body["wal_config"] = wal_config unless wal_config.nil?
42
+ req.body["optimizers_config"] = optimizers_config unless optimizers_config.nil?
43
+ req.body["init_from"] = init_from unless init_from.nil?
44
+ req.body["quantization_config"] = quantization_config unless quantization_config.nil?
45
+ end
46
+
47
+ response.body
48
+ end
49
+
50
+ # Update parameters of the existing collection
51
+ def update(
52
+ collection_name:,
53
+ optimizers_config: nil,
54
+ params: nil
55
+ )
56
+ response = client.connection.patch("#{PATH}/#{collection_name}") do |req|
57
+ req.body = {}
58
+ req.body["optimizers_config"] = optimizers_config unless optimizers_config.nil?
59
+ req.body["params"] = params unless params.nil?
60
+ end
61
+
62
+ response.body
63
+ end
64
+
65
+ # Drop collection and all associated data
66
+ def delete(collection_name:)
67
+ response = client.connection.delete("#{PATH}/#{collection_name}")
68
+ response.body
69
+ end
70
+
71
+ # Get list of all aliases for a collection
72
+ def aliases(collection_name:)
73
+ response = client.connection.get("#{PATH}/#{collection_name}/aliases")
74
+ response.body
75
+ end
76
+
77
+ # Update aliases of the collections
78
+ def update_aliases(actions:)
79
+ response = client.connection.post("#{PATH}/aliases") do |req|
80
+ req.body = {
81
+ actions: actions
82
+ }
83
+ end
84
+
85
+ response.body
86
+ end
87
+
88
+ # Create index for field in collection.
89
+ def create_index(
90
+ collection_name:,
91
+ field_name:,
92
+ field_schema: nil
93
+ )
94
+ response = client.connection.put("#{PATH}/#{collection_name}/index") do |req|
95
+ req.body = {
96
+ field_name: field_name
97
+ }
98
+ req.body["field_schema"] = field_schema unless field_schema.nil?
99
+ end
100
+
101
+ response.body
102
+ end
103
+
104
+ # Delete field index for collection
105
+ def delete_index(
106
+ collection_name:,
107
+ field_name:
108
+ )
109
+ response = client.connection.delete("#{PATH}/#{collection_name}/index/#{field_name}")
110
+ response.body
111
+ end
112
+
113
+ # Get cluster information for a collection
114
+ def cluster_info(collection_name:)
115
+ response = client.connection.get("#{PATH}/#{collection_name}/cluster")
116
+ response.body
117
+ end
118
+
119
+ # Update collection cluster setup
120
+ def update_cluster(
121
+ collection_name:,
122
+ move_shard:
123
+ )
124
+ response = client.connection.post("#{PATH}/#{collection_name}/cluster") do |req|
125
+ req.body = {
126
+ move_shard: move_shard
127
+ }
128
+ end
129
+ response.body
130
+ end
131
+
132
+ # Download specified snapshot from a collection as a file
133
+ def download_snapshot(
134
+ collection_name:,
135
+ snapshot_name:,
136
+ filepath:
137
+ )
138
+ response = client.connection.get("#{PATH}/#{collection_name}/snapshots/#{snapshot_name}")
139
+ File.open(File.expand_path(filepath), "wb+") { |fp| fp.write(response.body) }
140
+ end
141
+
142
+ # Delete snapshot for a collection
143
+ def delete_snapshot(
144
+ collection_name:,
145
+ snapshot_name:
146
+ )
147
+ response = client.connection.delete("#{PATH}/#{collection_name}/snapshots/#{snapshot_name}")
148
+ response.body
149
+ end
150
+
151
+ # Create new snapshot for a collection
152
+ def create_snapshot(
153
+ collection_name:
154
+ )
155
+ response = client.connection.post("#{PATH}/#{collection_name}/snapshots")
156
+ response.body
157
+ end
158
+
159
+ # Get list of snapshots for a collection
160
+ def list_snapshots(
161
+ collection_name:
162
+ )
163
+ response = client.connection.get("collections/#{collection_name}/snapshots")
164
+ response.body
165
+ end
166
+
167
+ # Recover local collection data from a snapshot. This will overwrite any data, stored on this node, for the collection. If collection does not exist - it will be created.
168
+ def restore_snapshot(
169
+ collection_name:,
170
+ filepath:,
171
+ priority: nil,
172
+ wait: nil
173
+ )
174
+ response = client.connection.post("#{PATH}/#{collection_name}/snapshots/recover") do |req|
175
+ req.params["wait"] = wait unless wait.nil?
176
+
177
+ req.body = {
178
+ location: filepath
179
+ }
180
+ req.body["priority"] = priority unless priority.nil?
181
+ end
182
+ response.body
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Points < Base
5
+ PATH = "points"
6
+
7
+ # Lists all data objects in reverse order of creation. The data will be returned as an array of objects.
8
+ def list(
9
+ collection_name:,
10
+ ids: nil,
11
+ with_payload: nil,
12
+ with_vector: nil,
13
+ consistency: nil
14
+ )
15
+ response = client.connection.post("collections/#{collection_name}/#{PATH}") do |req|
16
+ req.params["consistency"] = consistency unless consistency.nil?
17
+
18
+ req.body = {}
19
+ req.body["ids"] = ids
20
+ req.body["with_payload"] = with_payload unless with_payload.nil?
21
+ req.body["with_vector"] = with_vector unless with_vector.nil?
22
+ end
23
+ response.body
24
+ end
25
+
26
+ # Perform insert + updates on points. If point with given ID already exists - it will be overwritten.
27
+ def upsert(
28
+ collection_name:,
29
+ wait: nil,
30
+ ordering: nil,
31
+ batch: nil,
32
+ points: nil
33
+ )
34
+ response = client.connection.put("collections/#{collection_name}/#{PATH}") do |req|
35
+ req.params = {}
36
+ req.params["wait"] = wait unless wait.nil?
37
+ req.params["ordering"] = ordering unless ordering.nil?
38
+
39
+ req.body = {}
40
+ req.body["batch"] = batch unless batch.nil?
41
+ req.body["points"] = points unless points.nil?
42
+ end
43
+ response.body
44
+ end
45
+
46
+ # Delete points
47
+ def delete(
48
+ collection_name:,
49
+ points:, wait: nil,
50
+ ordering: nil
51
+ )
52
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/delete") do |req|
53
+ req.params["wait"] = wait unless wait.nil?
54
+ req.params["ordering"] = ordering unless ordering.nil?
55
+
56
+ req.body = {}
57
+ req.body["points"] = points
58
+ end
59
+ response.body
60
+ end
61
+
62
+ # Retrieve full information of single point by id
63
+ def get(
64
+ collection_name:,
65
+ id:,
66
+ consistency: nil
67
+ )
68
+ response = client.connection.get("collections/#{collection_name}/#{PATH}/#{id}") do |req|
69
+ req.params["consistency"] = consistency unless consistency.nil?
70
+ end
71
+ response.body
72
+ end
73
+
74
+ # Set payload values for points
75
+ def set_payload(
76
+ collection_name:,
77
+ payload:,
78
+ wait: nil,
79
+ ordering: nil,
80
+ points: nil,
81
+ filter: nil
82
+ )
83
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/payload") do |req|
84
+ req.params["wait"] = wait unless wait.nil?
85
+ req.params["ordering"] = ordering unless ordering.nil?
86
+
87
+ req.body = {}
88
+ req.body["payload"] = payload
89
+ req.body["points"] = points unless points.nil?
90
+ req.body["filter"] = filter unless filter.nil?
91
+ end
92
+ response.body
93
+ end
94
+
95
+ # Replace full payload of points with new one
96
+ def overwrite_payload(
97
+ collection_name:,
98
+ payload:, wait: nil,
99
+ ordering: nil,
100
+ points: nil,
101
+ filter: nil
102
+ )
103
+ response = client.connection.put("collections/#{collection_name}/#{PATH}/payload") do |req|
104
+ req.params["wait"] = wait unless wait.nil?
105
+ req.params["ordering"] = ordering unless ordering.nil?
106
+
107
+ req.body = {}
108
+ req.body["payload"] = payload
109
+ req.body["points"] = points unless points.nil?
110
+ req.body["filter"] = filter unless filter.nil?
111
+ end
112
+ response.body
113
+ end
114
+
115
+ # Delete specified key payload for points
116
+ def clear_payload_keys(
117
+ collection_name:,
118
+ keys:, wait: nil,
119
+ ordering: nil,
120
+ points: nil,
121
+ filter: nil
122
+ )
123
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/payload/delete") do |req|
124
+ req.params["wait"] = wait unless wait.nil?
125
+ req.params["ordering"] = ordering unless ordering.nil?
126
+
127
+ req.body = {}
128
+ req.body["keys"] = keys
129
+ req.body["points"] = points unless points.nil?
130
+ req.body["filter"] = filter unless filter.nil?
131
+ end
132
+ response.body
133
+ end
134
+
135
+ # Remove all payload for specified points
136
+ def clear_payload(
137
+ collection_name:,
138
+ wait: nil,
139
+ ordering: nil,
140
+ points: nil,
141
+ filter: nil
142
+ )
143
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/payload/clear") do |req|
144
+ req.params["wait"] = wait unless wait.nil?
145
+ req.params["ordering"] = ordering unless ordering.nil?
146
+
147
+ req.body = {}
148
+ req.body["points"] = points unless points.nil?
149
+ req.body["filter"] = filter unless filter.nil?
150
+ end
151
+ response.body
152
+ end
153
+
154
+ # Scroll request - paginate over all points which matches given filtering condition
155
+ def scroll(
156
+ collection_name:,
157
+ limit:,
158
+ filter: nil,
159
+ offset: nil,
160
+ with_payload: nil,
161
+ with_vector: nil,
162
+ consistency: nil
163
+ )
164
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/scroll") do |req|
165
+ req.params["consistency"] = consistency unless consistency.nil?
166
+
167
+ req.body = {}
168
+ req.body["limit"] = limit
169
+ req.body["filter"] = filter unless filter.nil?
170
+ req.body["offset"] = offset unless offset.nil?
171
+ req.body["with_payload"] = with_payload unless with_payload.nil?
172
+ req.body["with_vector"] = with_vector unless with_vector.nil?
173
+ end
174
+ response.body
175
+ end
176
+
177
+ # Retrieve closest points based on vector similarity and given filtering conditions
178
+ def search(
179
+ collection_name:,
180
+ vector:,
181
+ limit:,
182
+ filter: nil,
183
+ params: nil,
184
+ offset: nil,
185
+ with_payload: nil,
186
+ with_vector: nil,
187
+ score_threshold: nil,
188
+ consistency: nil
189
+ )
190
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/search") do |req|
191
+ req.params["consistency"] = consistency unless consistency.nil?
192
+
193
+ req.body = {}
194
+ req.body["vector"] = vector
195
+ req.body["limit"] = limit
196
+ req.body["filter"] = filter unless filter.nil?
197
+ req.body["params"] = params unless params.nil?
198
+ req.body["offset"] = offset unless offset.nil?
199
+ req.body["with_payload"] = with_payload unless with_payload.nil?
200
+ req.body["with_vector"] = with_vector unless with_vector.nil?
201
+ req.body["score_threshold"] = score_threshold unless score_threshold.nil?
202
+ end
203
+ response.body
204
+ end
205
+
206
+ # Retrieve by batch the closest points based on vector similarity and given filtering conditions
207
+ def batch_search(
208
+ collection_name:,
209
+ searches:,
210
+ consistency: nil
211
+ )
212
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/search/batch") do |req|
213
+ req.params["consistency"] = consistency unless consistency.nil?
214
+
215
+ req.body = {}
216
+ req.body["searches"] = searches
217
+ end
218
+ response.body
219
+ end
220
+
221
+ # Look for the points which are closer to stored positive examples and at the same time further to negative examples.
222
+ def recommend(
223
+ collection_name:,
224
+ positive:,
225
+ limit:,
226
+ negative: nil,
227
+ filter: nil,
228
+ params: nil,
229
+ offset: nil,
230
+ with_payload: nil,
231
+ with_vector: nil,
232
+ score_threshold: nil,
233
+ using: nil,
234
+ lookup_from: nil,
235
+ consistency: nil
236
+ )
237
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/recommend") do |req|
238
+ req.params["consistency"] = consistency unless consistency.nil?
239
+
240
+ req.body = {}
241
+ req.body["positive"] = positive
242
+ req.body["negative"] = negative unless negative.nil?
243
+ req.body["limit"] = limit
244
+ req.body["filter"] = filter unless filter.nil?
245
+ req.body["params"] = params unless params.nil?
246
+ req.body["offset"] = offset unless offset.nil?
247
+ req.body["with_payload"] = with_payload unless with_payload.nil?
248
+ req.body["with_vector"] = with_vector unless with_vector.nil?
249
+ req.body["score_threshold"] = score_threshold unless score_threshold.nil?
250
+ req.body["using"] = using unless using.nil?
251
+ req.body["lookup_from"] = lookup_from unless lookup_from.nil?
252
+ end
253
+ response.body
254
+ end
255
+
256
+ # Look for the points which are closer to stored positive examples and at the same time further to negative examples.
257
+ def batch_recommend(
258
+ collection_name:,
259
+ searches:,
260
+ consistency: nil
261
+ )
262
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/recommend/batch") do |req|
263
+ req.params["consistency"] = consistency unless consistency.nil?
264
+
265
+ req.body = {}
266
+ req.body["searches"] = searches
267
+ end
268
+ response.body
269
+ end
270
+
271
+ # Count points which matches given filtering condition
272
+ def count(
273
+ collection_name:,
274
+ filter: nil,
275
+ exact: nil
276
+ )
277
+ response = client.connection.post("collections/#{collection_name}/#{PATH}/count") do |req|
278
+ req.body = {}
279
+ req.body["filter"] = filter unless filter.nil?
280
+ req.body["exact"] = filter unless exact.nil?
281
+ end
282
+ response.body
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Service < Base
5
+ # Collect telemetry data including app info, system info, collections info, cluster info, configs and statistics
6
+ def telemetry(
7
+ anonymize: nil
8
+ )
9
+ response = client.connection.get("telemetry") do |req|
10
+ req.params["anonymize"] = anonymize if anonymize
11
+ end
12
+ response.body
13
+ end
14
+
15
+ # Collect metrics data including app info, collections info, cluster info and statistics
16
+ def metrics(
17
+ anonymize: nil
18
+ )
19
+ response = client.connection.get("metrics") do |req|
20
+ req.params["anonymize"] = anonymize if anonymize
21
+ end
22
+ response.body
23
+ end
24
+
25
+ # Set lock options. If write is locked, all write operations and collection creation are forbidden. Returns previous lock options
26
+ def set_lock(
27
+ write:,
28
+ error_message: nil
29
+ )
30
+ response = client.connection.post("locks") do |req|
31
+ req.body = {
32
+ write: write
33
+ }
34
+ req.body["error_message"] = error_message if error_message
35
+ end
36
+ response.body
37
+ end
38
+
39
+ # Get lock options. If write is locked, all write operations and collection creation are forbidden
40
+ def locks
41
+ response = client.connection.get("locks")
42
+ response.body
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ class Snapshots < Base
5
+ PATH = "snapshots"
6
+
7
+ # Get list of snapshots of the whole storage
8
+ def list
9
+ response = client.connection.get(PATH)
10
+ response.body
11
+ end
12
+
13
+ # Create new snapshot of the whole storage
14
+ def create
15
+ response = client.connection.post(PATH)
16
+ response.body
17
+ end
18
+
19
+ # Delete snapshot of the whole storage
20
+ def delete(
21
+ snapshot_name:
22
+ )
23
+ response = client.connection.delete("#{PATH}/#{snapshot_name}")
24
+ response.body
25
+ end
26
+
27
+ # Download specified snapshot of the whole storage as a file
28
+ def download(
29
+ snapshot_name:,
30
+ filepath:
31
+ )
32
+ response = client.connection.get("#{PATH}/#{snapshot_name}")
33
+ File.open(File.expand_path(filepath), "wb+") { |fp| fp.write(response.body) }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Qdrant
4
+ VERSION = "0.9.0"
5
+ end