Module: Mongoid::SearchIndexable::ClassMethods
| Relationships & Source Files | |
| Extension / Inclusion / Inheritance Descendants | |
|
Extended In:
| |
| Defined in: | lib/mongoid/search_indexable.rb |
Overview
Implementations for the feature's class-level methods.
Instance Method Summary
-
#auto_embed_search(text, index: nil, path: nil, limit: 10, num_candidates: nil, filter: nil, exact: false, model: nil, pipeline: []) ⇒ Array<Mongoid::Document>
Performs an Atlas Vector Search query using auto-embedding.
-
#create_search_indexes ⇒ Array<String>
Request the creation of all registered search indices.
-
#remove_search_index(name: nil, id: nil)
Removes the search index specified by the given name or id.
-
#remove_search_indexes
Request the removal of all registered search indexes.
-
#search_index(name_or_defn, defn = nil)
Adds an index definition for the provided single or compound keys.
-
#search_indexes(options = {})
A convenience method for querying the search indexes available on the current model's collection.
-
#vector_search(vector, index: nil, path: nil, limit: 10, num_candidates: nil, exact: false, filter: nil, pipeline: []) ⇒ Array<Mongoid::Document>
Performs an Atlas Vector Search query and returns matching documents.
-
#vector_search_index(name_or_defn, defn = nil)
Adds a vector search index definition.
-
#wait_for_search_indexes(names, interval: 5) {|SearchIndexable::Status| ... }
Waits for the named search indexes to be created.
-
#get_indexes(names) ⇒ Array<Hash>
private
Retrieves the index records for the indexes with the given names.
-
#infer_auto_embed_path(spec) ⇒ String
private
Infers the text field path from an index definition by locating the first field declared with type 'autoEmbed'.
-
#infer_vector_path(spec) ⇒ String
private
Infers the vector field path from the index definition by locating the first field declared with type 'vector'.
-
#resolve_auto_embed_index(index, path) ⇒ Array<String>
private
Resolves the index name and text field path for an auto-embedding query, applying inference when either is omitted.
-
#resolve_vector_index(index, path) ⇒ Array<String>
private
Resolves the index name and vector path from the declared specs, applying inference when either is omitted.
-
#select_auto_embed_spec(specs, index) ⇒ Hash
private
Picks one spec from the list of auto-embed specs, guided by
index. -
#validate_vector_index_definition!(defn)
private
Validates the vector index definition, raising ArgumentError for combinations that MongoDB does not support.
Instance Method Details
#auto_embed_search(text, index: nil, path: nil, limit: 10, num_candidates: nil, filter: nil, exact: false, model: nil, pipeline: []) ⇒ Array<Mongoid::Document>
Performs an Atlas Vector Search query using auto-embedding. Atlas generates the query vector from the supplied text at query time; no pre-computed embedding is required.
Each returned document has a vector_search_score attribute populated
with its relevance score. Unlike #vector_search, the indexed text field
is retained in returned documents.
# File 'lib/mongoid/search_indexable.rb', line 378
def (text, index: nil, path: nil, limit: 10, num_candidates: nil, filter: nil, exact: false, model: nil, pipeline: []) # rubocop:disable Metrics/ParameterLists resolved_index, resolved_path = (index, path) = { 'index' => resolved_index, 'path' => resolved_path, 'query' => { 'text' => text }, 'limit' => limit } ['numCandidates'] = num_candidates || (limit * 10) unless exact ['exact'] = true if exact ['filter'] = filter if filter ['model'] = model if model agg_pipeline = [ { '$vectorSearch' => }, { '$addFields' => { 'vector_search_score' => { '$meta' => 'vectorSearchScore' } } } ] agg_pipeline.concat(Array(pipeline)) collection.aggregate(agg_pipeline).map { |doc| instantiate(doc) } end
#create_search_indexes ⇒ Array<String>
Request the creation of all registered search indices. Note that the search indexes are created asynchronously, and may take several minutes to be fully available.
# File 'lib/mongoid/search_indexable.rb', line 161
def create_search_indexes return if search_index_specs.empty? Threaded.with_collection_management do collection.search_indexes.create_many(search_index_specs) end end
#get_indexes(names) ⇒ Array<Hash> (private)
Retrieves the index records for the indexes with the given names.
# File 'lib/mongoid/search_indexable.rb', line 423
def get_indexes(names) collection.search_indexes.select { |i| names.include?(i['name']) } end
#infer_auto_embed_path(spec) ⇒ String (private)
Infers the text field path from an index definition by locating the first field declared with type 'autoEmbed'.
# File 'lib/mongoid/search_indexable.rb', line 525
def (spec) field_list = spec.dig(:definition, :fields) || spec.dig(:definition, 'fields') || [] ae_field = field_list.find { |f| (f[:type] || f['type']) == 'autoEmbed' } unless ae_field raise ArgumentError, "Cannot infer path on #{name}: no 'autoEmbed' field in index definition; specify path:" end (ae_field[:path] || ae_field['path']).to_s end
#infer_vector_path(spec) ⇒ String (private)
Infers the vector field path from the index definition by locating the first field declared with type 'vector'.
# File 'lib/mongoid/search_indexable.rb', line 463
def infer_vector_path(spec) field_list = spec.dig(:definition, :fields) || spec.dig(:definition, 'fields') || [] vector_field = field_list.find { |f| (f[:type] || f['type']) == 'vector' } unless vector_field raise ArgumentError, "Cannot infer vector path on #{name}: no 'vector' type field in index definition; specify path:" end (vector_field[:path] || vector_field['path']).to_s end
#remove_search_index(name: nil, id: nil)
Removes the search index specified by the given name or id. Either name OR id must be given, but not both.
# File 'lib/mongoid/search_indexable.rb', line 207
def remove_search_index(name: nil, id: nil) Threaded.with_collection_management do logger.info( "MONGOID: Removing search index '#{name || id}' " \ "on collection '#{collection.name}'." ) collection.search_indexes.drop_one(name: name, id: id) end end
#remove_search_indexes
It would be nice if this could remove ONLY the search indexes
Request the removal of all registered search indexes. Note that the search indexes are removed asynchronously, and may take several minutes to be fully deleted.
that have been declared on the model, but because the model may not name the index, we can't guarantee that we'll know the name or id of the corresponding indexes. It is not unreasonable to assume, though, that the intention is for the model to declare, one-to-one, all desired search indexes, so removing all search indexes ought to suffice. If a specific index or set of indexes needs to be removed instead, consider using search_indexes.each with remove_search_index.
# File 'lib/mongoid/search_indexable.rb', line 230
def remove_search_indexes search_indexes.each do |spec| remove_search_index id: spec['id'] end end
#resolve_auto_embed_index(index, path) ⇒ Array<String> (private)
Resolves the index name and text field path for an auto-embedding query, applying inference when either is omitted.
# File 'lib/mongoid/search_indexable.rb', line 482
def (index, path) = search_index_specs.select do |s| s[:type] == 'vectorSearch' && (s.dig(:definition, :fields) || s.dig(:definition, 'fields') || []) .any? { |f| (f[:type] || f['type']) == 'autoEmbed' } end raise ArgumentError, "No auto-embed indexes declared on #{name}" if .empty? # Extracted to keep cyclomatic complexity within RuboCop's threshold. spec = (, index) resolved_index = spec[:name] || 'default' resolved_path = path ? path.to_s : (spec) [ resolved_index, resolved_path ] end
#resolve_vector_index(index, path) ⇒ Array<String> (private)
Resolves the index name and vector path from the declared specs, applying inference when either is omitted.
# File 'lib/mongoid/search_indexable.rb', line 434
def resolve_vector_index(index, path) vector_specs = search_index_specs.select { |s| s[:type] == 'vectorSearch' } raise ArgumentError, "No vector search indexes declared on #{name}" if vector_specs.empty? spec = if index found = vector_specs.find { |s| s[:name] == index.to_s } raise ArgumentError, "No vector search index '#{index}' declared on #{name}" unless found found elsif vector_specs.size == 1 vector_specs.first else raise ArgumentError, "#{name} has multiple vector search indexes; specify index: to select one" end resolved_index = spec[:name] || 'default' resolved_path = path ? path.to_s : infer_vector_path(spec) [ resolved_index, resolved_path ] end
#search_index(name_or_defn, defn = nil)
Adds an index definition for the provided single or compound keys.
# File 'lib/mongoid/search_indexable.rb', line 249
def search_index(name_or_defn, defn = nil) name = name_or_defn name, defn = nil, name if name.is_a?(Hash) spec = { definition: defn }.tap { |s| s[:name] = name.to_s if name } search_index_specs.push(spec) end
#search_indexes(options = {})
A convenience method for querying the search indexes available on the current model's collection.
# File 'lib/mongoid/search_indexable.rb', line 196
def search_indexes( = {}) Threaded.with_collection_management do collection.search_indexes() end end
#select_auto_embed_spec(specs, index) ⇒ Hash (private)
Picks one spec from the list of auto-embed specs, guided by index.
# File 'lib/mongoid/search_indexable.rb', line 505
def (specs, index) if index found = specs.find { |s| s[:name] == index.to_s } raise ArgumentError, "No auto-embed index '#{index}' declared on #{name}" unless found found elsif specs.size == 1 specs.first else raise ArgumentError, "#{name} has multiple auto-embed indexes; specify index: to select one" end end
#validate_vector_index_definition!(defn) (private)
Validates the vector index definition, raising ArgumentError for combinations that MongoDB does not support.
# File 'lib/mongoid/search_indexable.rb', line 407
def validate_vector_index_definition!(defn) fields = defn[:fields] || defn['fields'] || [] fields.each do |field| method = field[:indexingMethod] || field['indexingMethod'] next unless method.to_s == 'flat' next unless field[:hnswOptions] || field['hnswOptions'] raise ArgumentError, 'hnswOptions is only supported with indexingMethod: hnsw' end end
#vector_search(vector, index: nil, path: nil, limit: 10, num_candidates: nil, exact: false, filter: nil, pipeline: []) ⇒ Array<Mongoid::Document>
Performs an Atlas Vector Search query and returns matching documents.
Each returned document has a vector_search_score attribute populated
with its relevance score.
The vector field (given by path:) is excluded from the returned
documents by default, as vectors are large and rarely useful after
retrieval.
# File 'lib/mongoid/search_indexable.rb', line 324
def vector_search(vector, index: nil, path: nil, limit: 10, num_candidates: nil, exact: false, filter: nil, pipeline: []) # rubocop:disable Metrics/ParameterLists resolved_index, resolved_path = resolve_vector_index(index, path) = { 'index' => resolved_index, 'path' => resolved_path, 'queryVector' => vector, 'limit' => limit } ['numCandidates'] = num_candidates || (limit * 10) unless exact ['exact'] = true if exact ['filter'] = filter if filter agg_pipeline = [ { '$vectorSearch' => }, { '$addFields' => { 'vector_search_score' => { '$meta' => 'vectorSearchScore' } } }, { '$project' => { resolved_path => 0 } } ] agg_pipeline.concat(Array(pipeline)) collection.aggregate(agg_pipeline).map { |doc| instantiate(doc) } end
#vector_search_index(name_or_defn, defn = nil)
Adds a vector search index definition. Also defines a read-only
vector_search_score field on the model the first time it is called,
which is populated on documents returned by #vector_search.
# File 'lib/mongoid/search_indexable.rb', line 280
def vector_search_index(name_or_defn, defn = nil) name = name_or_defn name, defn = nil, name if name.is_a?(Hash) validate_vector_index_definition!(defn) spec = { type: 'vectorSearch', definition: defn }.tap { |s| s[:name] = name.to_s if name } search_index_specs.push(spec) return if fields.key?('vector_search_score') field :vector_search_score, type: Float attr_readonly :vector_search_score end
#wait_for_search_indexes(names, interval: 5) {|SearchIndexable::Status| ... }
Waits for the named search indexes to be created.
# File 'lib/mongoid/search_indexable.rb', line 176
def wait_for_search_indexes(names, interval: 5) loop do status = Status.new(get_indexes(names)) yield status if block_given? break if status.ready? sleep interval end end