Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## [Unreleased]
- Fix `collections.restore_snapshot` to use PUT (POST always returned 404)
- Fix `clusters.remove_peer` to call `DELETE /cluster/peer/{peer_id}` instead of `POST /cluster/recover`
- `client.aliases` no longer caches the first response
- `points.list` now requires `ids:` (the API always required it)
- Deprecation warnings for `points.search`, `batch_search`, `recommend`, `batch_recommend` (use `points.query`), the lock API and `init_from:` (both removed in Qdrant 1.16)
- README: fix inaccurate examples and remove the nonexistent `backups` section

## [0.10.0] - 2026-08-12
- Drop Faraday dependency
Expand Down
95 changes: 47 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ client.collections.get(collection_name: "string")
client.collections.create(
collection_name: "string", # required
vectors: {}, # required
sparse_vectors: nil,
shard_number: nil,
replication_factor: nil,
write_consistency_factor: nil,
on_disk_payload: nil,
hnsw_config: nil,
wal_config: nil,
optimizers_config: nil,
init_from: nil,
quantization_config: nil
)

Expand All @@ -73,11 +73,14 @@ client.collections.update(
# Drop collection and all associated data
client.collections.delete(collection_name: "string")

# Get list of all aliases (for a collection)
# Get list of all aliases for a collection
client.collections.aliases(
collection_name: "string" # optional
collection_name: "string" # required
)

# Get list of all aliases across all collections
client.aliases

# Update aliases of the collections
client.collections.update_aliases(
actions: [{
Expand All @@ -101,9 +104,7 @@ client.collections.create_index(
# Delete field index for collection
client.collections.delete_index(
collection_name: "string", # required
field_name: "string", # required
wait: "boolean",
ordering: "ordering"
field_name: "string" # required
)

# Get cluster information for a collection
Expand All @@ -118,8 +119,7 @@ client.collections.update_cluster(
shard_id: "int",
to_peer_id: "int",
from_peer_id: "int"
},
timeout: "int"
}
)

# Create new snapshot for a collection
Expand Down Expand Up @@ -167,18 +167,18 @@ client.points.get(
client.points.get_all(
collection_name: "string", # required
ids: "[int]", # required
with_payload: "boolean"
with_vector: "boolean"
with_payload: "boolean",
with_vector: "boolean",
consistency: "int/string"
)

# Lists all data objects in reverse order of creation. The data will be returned as an array of objects.
# Retrieve full information of points by ids. Same as `get_all`.
client.points.list(
collection_name: "string", # required
ids: "[int/string]", # required
with_payload: nil,
with_vector: nil,
consistency: nil

)

# Insert or update points. Either `points:` or `batch:` is required.
Expand Down Expand Up @@ -278,6 +278,28 @@ client.points.scroll(
consistency: "int/string"
)

# Universally query points: covers search, recommend and discover, plus hybrid and multi-stage queries
client.points.query(
collection_name: "string", # required
query: "[float]/{}", # a vector, a point ID, or a query object such as {recommend: {positive: [1]}}
using: "string",
prefetch: {},
filter: {},
params: {},
score_threshold: "float",
limit: "int",
offset: "int",
with_payload: "boolean",
with_vector: "boolean",
lookup_from: {},
shard_key: "string",
consistency: "int/string",
timeout: "int"
)

# DEPRECATED (since Qdrant 1.13): `search`, `batch_search`, `recommend` and `batch_recommend`
# still work but are no longer documented by Qdrant and will be removed. Use `points.query` instead.

# Retrieve closest points based on vector similarity and given filtering conditions
client.points.search(
collection_name: "string", # required
Expand Down Expand Up @@ -310,9 +332,9 @@ client.points.recommend(
offset: "int",
with_payload: "boolean",
with_vector: "boolean",
score_threshold: "float"
score_threshold: "float",
using: "string",
lookup_from: {},
lookup_from: {}
)

# Look for the points which are closer to stored positive examples and at the same time further to negative examples.
Expand All @@ -333,58 +355,33 @@ client.points.count(
### Snapshots
```ruby
# Get list of snapshots of the whole storage
client.snapshots.list(
collection_name: "string" # optional
)
client.snapshots.list

# Create new snapshot of the whole storage
client.snapshots.create(
collection_name: "string" # required
)
client.snapshots.create

# Delete snapshot of the whole storage
client.snapshots.delete(
collection_name: "string", # required
snapshot_name: "string" # required
snapshot_name: "string" # required
)

# Download specified snapshot of the whole storage as a file
client.snapshots.download(
collection_name: "string", # required
snapshot_name: "string" # required
filepath: "~/Downloads/backup.txt" # required
)


# Get the backup
client.backups.get(
backend: "filesystem",
id: "my-first-backup"
)

# Restore backup
client.backups.restore(
backend: "filesystem",
id: "my-first-backup"
)

# Check the backup restore status
client.backups.restore_status(
backend: "filesystem",
id: "my-first-backup"
snapshot_name: "string", # required
filepath: "~/Downloads/backup.snapshot" # required
)
```

### Cluster
```ruby
# Get information about the current state and composition of the cluster
client.cluster.info
client.clusters.info

# Tries to recover current peer Raft state.
client.cluster.recover
client.clusters.recover

# Tries to remove peer from the cluster. Will return an error if peer has shards on it.
client.cluster.remove_peer(
client.clusters.remove_peer(
peer_id: "int", # required
force: "boolean"
)
Expand All @@ -402,12 +399,14 @@ client.metrics(
anonymize: "boolean" # optional
)

# DEPRECATED: the lock API was removed in Qdrant 1.16. These only work against older servers.

# Get lock options. If write is locked, all write operations and collection creation are forbidden
client.locks

# Set lock options. If write is locked, all write operations and collection creation are forbidden. Returns previous lock options
client.set_lock(
write: "boolean" # required
write: "boolean", # required
error_message: "string"
)
```
Expand Down
8 changes: 8 additions & 0 deletions lib/qdrant/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,13 @@ class Base
def initialize(client:)
@client = client
end

private

# Point the warning at the caller's code, skipping frames inside the gem and Forwardable.
def deprecation_warning(message)
uplevel = caller_locations.index { |loc| !loc.path.start_with?(__dir__) && !loc.path.end_with?("/forwardable.rb") }
warn("[DEPRECATION] Qdrant: #{message}", uplevel: uplevel + 1)
end
end
end
2 changes: 1 addition & 1 deletion lib/qdrant/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def connection
end

def aliases
@aliases ||= Qdrant::Aliases.new(client: self).list
Qdrant::Aliases.new(client: self).list
end

def collections
Expand Down
2 changes: 1 addition & 1 deletion lib/qdrant/clusters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def remove_peer(
peer_id:,
force: nil
)
response = client.connection.post("#{PATH}/recover") do |req|
response = client.connection.delete("#{PATH}/peer/#{peer_id}") do |req|
req.params["force"] = force if force
end
response.body
Expand Down
4 changes: 3 additions & 1 deletion lib/qdrant/collections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def create(
init_from: nil,
quantization_config: nil
)
deprecation_warning("`init_from:` was removed in Qdrant 1.16 and is ignored by the server.") unless init_from.nil?

response = client.connection.put("#{PATH}/#{collection_name}") do |req|
req.body = {}
req.body["vectors"] = vectors
Expand Down Expand Up @@ -176,7 +178,7 @@ def restore_snapshot(
priority: nil,
wait: nil
)
response = client.connection.post("#{PATH}/#{collection_name}/snapshots/recover") do |req|
response = client.connection.put("#{PATH}/#{collection_name}/snapshots/recover") do |req|
req.params["wait"] = wait unless wait.nil?

req.body = {
Expand Down
12 changes: 10 additions & 2 deletions lib/qdrant/points.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ module Qdrant
class Points < Base
PATH = "points"

# Lists all data objects in reverse order of creation. The data will be returned as an array of objects.
# Retrieve full information of points by ids. Same as `get_all`.
def list(
collection_name:,
ids: nil,
ids:,
with_payload: nil,
with_vector: nil,
consistency: nil
Expand Down Expand Up @@ -212,6 +212,8 @@ def search(
score_threshold: nil,
consistency: nil
)
deprecation_warning("`points.search` is deprecated since Qdrant 1.13 and will be removed from the server; use `points.query` instead.")

response = client.connection.post("collections/#{collection_name}/#{PATH}/search") do |req|
req.params["consistency"] = consistency unless consistency.nil?

Expand All @@ -234,6 +236,8 @@ def batch_search(
searches:,
consistency: nil
)
deprecation_warning("`points.batch_search` is deprecated since Qdrant 1.13 and will be removed from the server; use `points.query` instead.")

response = client.connection.post("collections/#{collection_name}/#{PATH}/search/batch") do |req|
req.params["consistency"] = consistency unless consistency.nil?

Expand All @@ -259,6 +263,8 @@ def recommend(
lookup_from: nil,
consistency: nil
)
deprecation_warning("`points.recommend` is deprecated since Qdrant 1.13 and will be removed from the server; use `points.query` instead.")

response = client.connection.post("collections/#{collection_name}/#{PATH}/recommend") do |req|
req.params["consistency"] = consistency unless consistency.nil?

Expand All @@ -284,6 +290,8 @@ def batch_recommend(
searches:,
consistency: nil
)
deprecation_warning("`points.batch_recommend` is deprecated since Qdrant 1.13 and will be removed from the server; use `points.query` instead.")

response = client.connection.post("collections/#{collection_name}/#{PATH}/recommend/batch") do |req|
req.params["consistency"] = consistency unless consistency.nil?

Expand Down
4 changes: 4 additions & 0 deletions lib/qdrant/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def set_lock(
write:,
error_message: nil
)
deprecation_warning("the lock API was removed in Qdrant 1.16; `set_lock` only works against older servers.")

response = client.connection.post("locks") do |req|
req.body = {
write: write
Expand All @@ -38,6 +40,8 @@ def set_lock(

# Get lock options. If write is locked, all write operations and collection creation are forbidden
def locks
deprecation_warning("the lock API was removed in Qdrant 1.16; `locks` only works against older servers.")

response = client.connection.get("locks")
response.body
end
Expand Down
14 changes: 7 additions & 7 deletions script/e2e.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,22 @@ def ok_body!(body, operation)
puts "e2e: point write and read round trip passed"

body = ok_body!(
client.points.search(
client.points.query(
collection_name: collection_name,
vector: [1.0, 0.0, 0.0],
query: [1.0, 0.0, 0.0],
limit: 1,
with_payload: true,
with_vector: false
),
"points.search"
"points.query"
)
hit = body.fetch("result").first
assert!(hit && hit["id"] == 1, "points.search did not return point 1 first")
assert!(hit["score"].to_f > 0.99, "points.search score was #{hit["score"].inspect}")
hit = body.dig("result", "points").first
assert!(hit && hit["id"] == 1, "points.query did not return point 1 first")
assert!(hit["score"].to_f > 0.99, "points.query score was #{hit["score"].inspect}")

body = ok_body!(client.points.count(collection_name: collection_name, exact: true), "points.count")
assert!(body.dig("result", "count") == 2, "points.count did not return 2")
puts "e2e: search and count passed"
puts "e2e: query and count passed"

body = ok_body!(client.points.delete(collection_name: collection_name, points: [1, 2], wait: true), "points.delete")
assert!(body.dig("result", "status") == "completed", "points.delete did not return a completed status")
Expand Down
9 changes: 9 additions & 0 deletions spec/qdrant/aliases_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,14 @@
it "return the nodes info" do
expect(client.aliases).to be_a(Hash)
end

it "fetches fresh data on every call" do
expect_any_instance_of(Qdrant::Client::Connection).to receive(:get)
.with(Qdrant::Aliases::PATH)
.twice
.and_return(response)

2.times { client.aliases }
end
end
end
16 changes: 15 additions & 1 deletion spec/qdrant/clusters_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@
end
end

xdescribe "#remove_peer" do
describe "#remove_peer" do
let(:request) { Qdrant::Client::RequestData.new({}, nil) }

before do
allow_any_instance_of(Qdrant::Client::Connection).to receive(:delete)
.with("cluster/peer/42") do |_connection, _path, &block|
block.call(request)
response
end
end

it "deletes the peer" do
expect(client.clusters.remove_peer(peer_id: 42, force: true).dig("status")).to eq("ok")
expect(request.params).to eq("force" => true)
end
end
end
Loading
Loading