qdrant-ruby 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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