Class: Zvec::Collection
- Inherits:
-
Object
- Object
- Zvec::Collection
- Defined in:
- lib/zvec/collection.rb
Overview
A vector collection backed by the zvec C++ engine. Provides CRUD operations, vector similarity search, and index management.
Collections must be explicitly closed via #close before they can be
reopened from the same path. Use the closed? method to check state.
All mutating operations are thread-safe (protected by a Monitor).
Instance Attribute Summary collapse
-
#schema ⇒ Zvec::Schema?
readonly
The schema, if provided at creation time.
Class Method Summary collapse
-
.create_and_open(path, schema, read_only: false, enable_mmap: true) ⇒ Zvec::Collection
Create a new collection on disk and open it.
-
.open(path, read_only: false, enable_mmap: true) ⇒ Zvec::Collection
Open an existing collection from disk.
- .validate_path!(path) ⇒ Object
Instance Method Summary collapse
-
#add(pk:, **fields) ⇒ Array
Convenience method to insert a document from keyword arguments.
-
#close ⇒ void
Close the collection, releasing the underlying C++ resources.
-
#closed? ⇒ Boolean
True if the collection has been closed.
-
#collection_name ⇒ String?
The collection name (from schema or explicit).
-
#create_index(field_name, index_params) ⇒ self
Create an index on a field.
-
#delete(*pks) ⇒ Array<Array(Boolean, String)>
Delete documents by primary key(s).
-
#delete_by_filter(filter) ⇒ void
Delete documents matching a filter expression.
-
#destroy ⇒ void
Destroy the collection, removing all data from disk.
-
#doc_count ⇒ Integer
The number of documents in the collection.
-
#drop_index(field_name) ⇒ self
Drop an index on a field.
-
#fetch(*pks) ⇒ Hash{String => Zvec::Doc}
Fetch documents by primary key(s).
-
#flush ⇒ self
Flush pending writes to disk.
-
#initialize(ext_collection, schema: nil, name: nil) ⇒ Collection
constructor
A new instance of Collection.
-
#insert(docs) ⇒ Array<Array(Boolean, String)>
Insert one or more documents.
-
#optimize ⇒ self
Optimize the collection (compact segments, rebuild indexes).
-
#path ⇒ String
The on-disk path of the collection.
-
#query(field_name:, vector:, topk: 10, filter: nil, include_vector: false, output_fields: nil, query_params: nil) ⇒ Array<Zvec::Doc>
Execute a vector similarity search with full control over parameters.
-
#search(vector, field: nil, top_k: 10, filter: nil) ⇒ Array<Zvec::Doc>
Convenience method for simple vector similarity search.
-
#stats ⇒ Ext::CollectionStats
Collection statistics.
-
#update(docs) ⇒ Array<Array(Boolean, String)>
Update one or more existing documents.
-
#upsert(docs) ⇒ Array<Array(Boolean, String)>
Upsert (insert or update) one or more documents.
Constructor Details
#initialize(ext_collection, schema: nil, name: nil) ⇒ Collection
Returns a new instance of Collection.
36 37 38 39 40 41 42 |
# File 'lib/zvec/collection.rb', line 36 def initialize(ext_collection, schema: nil, name: nil) @ext = ext_collection @schema = schema @name = name @monitor = Monitor.new @closed = false end |
Instance Attribute Details
#schema ⇒ Zvec::Schema? (readonly)
Returns the schema, if provided at creation time.
31 32 33 |
# File 'lib/zvec/collection.rb', line 31 def schema @schema end |
Class Method Details
.create_and_open(path, schema, read_only: false, enable_mmap: true) ⇒ Zvec::Collection
Create a new collection on disk and open it.
55 56 57 58 59 60 61 62 63 64 |
# File 'lib/zvec/collection.rb', line 55 def self.create_and_open(path, schema, read_only: false, enable_mmap: true) validate_path!(path) raise ArgumentError, "schema must be a Zvec::Schema" unless schema.is_a?(Schema) opts = Ext::CollectionOptions.new opts.read_only = read_only opts.enable_mmap = enable_mmap ext = Ext::Collection.create_and_open(path, schema.ext_schema, opts) new(ext, schema: schema, name: schema.name) end |
.open(path, read_only: false, enable_mmap: true) ⇒ Zvec::Collection
Open an existing collection from disk.
76 77 78 79 80 81 82 83 84 |
# File 'lib/zvec/collection.rb', line 76 def self.open(path, read_only: false, enable_mmap: true) validate_path!(path) opts = Ext::CollectionOptions.new opts.read_only = read_only opts.enable_mmap = enable_mmap ext = Ext::Collection.open(path, opts) new(ext) end |
.validate_path!(path) ⇒ Object
422 423 424 |
# File 'lib/zvec/collection.rb', line 422 def self.validate_path!(path) raise ArgumentError, "path must be a non-empty string" if path.nil? || path.to_s.strip.empty? end |
Instance Method Details
#add(pk:, **fields) ⇒ Array
Convenience method to insert a document from keyword arguments.
377 378 379 380 381 382 |
# File 'lib/zvec/collection.rb', line 377 def add(pk:, **fields) ensure_open! raise ArgumentError, "#{error_prefix}pk must not be nil" if pk.nil? doc = Doc.new(pk: pk, fields: fields, schema: @schema) insert(doc) end |
#close ⇒ void
This method returns an undefined value.
Close the collection, releasing the underlying C++ resources. The collection must be closed before it can be reopened from the same path.
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/zvec/collection.rb', line 125 def close raise CollectionError, "#{error_prefix}Collection is already closed" if @closed @monitor.synchronize do begin @ext.close rescue NoMethodError # C++ extension may not expose a close method; the GC will handle it. end @closed = true end end |
#closed? ⇒ Boolean
Returns true if the collection has been closed.
111 112 113 |
# File 'lib/zvec/collection.rb', line 111 def closed? @closed end |
#collection_name ⇒ String?
Returns the collection name (from schema or explicit).
87 88 89 |
# File 'lib/zvec/collection.rb', line 87 def collection_name @name || (@schema ? @schema.name : nil) end |
#create_index(field_name, index_params) ⇒ self
Create an index on a field.
152 153 154 155 156 157 158 159 160 |
# File 'lib/zvec/collection.rb', line 152 def create_index(field_name, index_params) ensure_open! raise ArgumentError, "field_name must be a non-empty string" if field_name.nil? || field_name.to_s.strip.empty? @monitor.synchronize do @ext.create_index(field_name.to_s, index_params) end self end |
#delete(*pks) ⇒ Array<Array(Boolean, String)>
Delete documents by primary key(s).
273 274 275 276 277 278 279 280 |
# File 'lib/zvec/collection.rb', line 273 def delete(*pks) ensure_open! pks = pks.flatten raise ArgumentError, "#{error_prefix}No primary keys provided for delete" if pks.empty? pks = pks.map(&:to_s) results = @monitor.synchronize { @ext.delete_pks(pks) } check_write_results!(results) end |
#delete_by_filter(filter) ⇒ void
This method returns an undefined value.
Delete documents matching a filter expression.
291 292 293 294 295 |
# File 'lib/zvec/collection.rb', line 291 def delete_by_filter(filter) ensure_open! raise ArgumentError, "#{error_prefix}filter must be a non-empty string" if filter.nil? || filter.to_s.strip.empty? @monitor.synchronize { @ext.delete_by_filter(filter) } end |
#destroy ⇒ void
This method returns an undefined value.
Destroy the collection, removing all data from disk.
202 203 204 205 206 207 208 |
# File 'lib/zvec/collection.rb', line 202 def destroy ensure_open! @monitor.synchronize do @ext.destroy @closed = true end end |
#doc_count ⇒ Integer
Returns the number of documents in the collection.
105 106 107 108 |
# File 'lib/zvec/collection.rb', line 105 def doc_count ensure_open! @ext.stats.doc_count end |
#drop_index(field_name) ⇒ self
Drop an index on a field.
168 169 170 171 172 173 174 175 176 |
# File 'lib/zvec/collection.rb', line 168 def drop_index(field_name) ensure_open! raise ArgumentError, "field_name must be a non-empty string" if field_name.nil? || field_name.to_s.strip.empty? @monitor.synchronize do @ext.drop_index(field_name.to_s) end self end |
#fetch(*pks) ⇒ Hash{String => Zvec::Doc}
Fetch documents by primary key(s).
356 357 358 359 360 361 362 363 364 365 |
# File 'lib/zvec/collection.rb', line 356 def fetch(*pks) ensure_open! pks = pks.flatten raise ArgumentError, "#{error_prefix}No primary keys provided for fetch" if pks.empty? pks = pks.map(&:to_s) raw = @monitor.synchronize { @ext.fetch(pks) } raw.transform_values do |h| Doc.new(pk: nil, fields: h, schema: @schema) end end |
#flush ⇒ self
Flush pending writes to disk.
192 193 194 195 196 |
# File 'lib/zvec/collection.rb', line 192 def flush ensure_open! @monitor.synchronize { @ext.flush } self end |
#insert(docs) ⇒ Array<Array(Boolean, String)>
Insert one or more documents.
224 225 226 227 228 229 230 231 |
# File 'lib/zvec/collection.rb', line 224 def insert(docs) ensure_open! docs = [docs] unless docs.is_a?(Array) validate_docs!(docs) ext_docs = docs.map { |d| d.is_a?(Doc) ? d.ext_doc : d } results = @monitor.synchronize { @ext.insert(ext_docs) } check_write_results!(results) end |
#optimize ⇒ self
Optimize the collection (compact segments, rebuild indexes).
182 183 184 185 186 |
# File 'lib/zvec/collection.rb', line 182 def optimize ensure_open! @monitor.synchronize { @ext.optimize } self end |
#path ⇒ String
Returns the on-disk path of the collection.
92 93 94 |
# File 'lib/zvec/collection.rb', line 92 def path @ext.path end |
#query(field_name:, vector:, topk: 10, filter: nil, include_vector: false, output_fields: nil, query_params: nil) ⇒ Array<Zvec::Doc>
Execute a vector similarity search with full control over parameters.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/zvec/collection.rb', line 322 def query(field_name:, vector:, topk: 10, filter: nil, include_vector: false, output_fields: nil, query_params: nil) ensure_open! validate_query_vector!(vector, field_name) vq = VectorQuery.new( field_name: field_name, vector: vector, topk: topk, filter: filter, include_vector: include_vector, output_fields: output_fields, query_params: query_params ) raw_results = @monitor.synchronize { @ext.query(vq.ext_query) } raw_results.map do |h| Doc.new( pk: h["pk"], fields: h.reject { |k, _| %w[pk score doc_id].include?(k) }, schema: @schema ).tap { |d| d.instance_variable_set(:@score, h["score"]) } end end |
#search(vector, field: nil, top_k: 10, filter: nil) ⇒ Array<Zvec::Doc>
Convenience method for simple vector similarity search.
Auto-detects the vector field from the schema if not specified.
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 |
# File 'lib/zvec/collection.rb', line 400 def search(vector, field: nil, top_k: 10, filter: nil) ensure_open! raise ArgumentError, "#{error_prefix}vector must be a non-empty Array" unless vector.is_a?(Array) && !vector.empty? # Auto-detect vector field if not specified fname = field&.to_s unless fname if @schema vfield = @schema.ext_schema.vector_fields.first raise CollectionError, "#{error_prefix}No vector fields in schema" unless vfield fname = vfield.name else vfields = @ext.schema.vector_fields raise CollectionError, "#{error_prefix}No vector fields in schema" if vfields.empty? fname = vfields.first.name end end query(field_name: fname, vector: vector, topk: top_k, filter: filter) end |
#stats ⇒ Ext::CollectionStats
Returns collection statistics.
98 99 100 101 |
# File 'lib/zvec/collection.rb', line 98 def stats ensure_open! @ext.stats end |
#update(docs) ⇒ Array<Array(Boolean, String)>
Update one or more existing documents.
254 255 256 257 258 259 260 261 |
# File 'lib/zvec/collection.rb', line 254 def update(docs) ensure_open! docs = [docs] unless docs.is_a?(Array) validate_docs!(docs) ext_docs = docs.map { |d| d.is_a?(Doc) ? d.ext_doc : d } results = @monitor.synchronize { @ext.update(ext_docs) } check_write_results!(results) end |
#upsert(docs) ⇒ Array<Array(Boolean, String)>
Upsert (insert or update) one or more documents.
239 240 241 242 243 244 245 246 |
# File 'lib/zvec/collection.rb', line 239 def upsert(docs) ensure_open! docs = [docs] unless docs.is_a?(Array) validate_docs!(docs) ext_docs = docs.map { |d| d.is_a?(Doc) ? d.ext_doc : d } results = @monitor.synchronize { @ext.upsert(ext_docs) } check_write_results!(results) end |