The MongoDB Ruby team is pleased to announce version 9.1.0 of the mongoid gem - an Ruby ODM for MongoDB. This is a new minor release in the 9.x series of Mongoid.
Install this release using RubyGems via the command line as follows:
gem install -v 9.1.0 mongoid
Or simply add it to your Gemfile:
gem 'mongoid', '9.1.0'
Have any feedback? Click on through to MongoDB's JIRA and open a new ticket to let us know what's on your mind 🧠.
New Features
MONGOID-5411 allow results to be returned as demongoized hashes (PR)
Mongoid now supports a new raw directive when building queries. When present, the resulting query will be returned as hashes, rather than instantiated document models. The hashes will not be "demongoized" -- the values will be returned exactly as provided by the database, directly.
If you specify typed: true, the hashes will be returned with the values translated from the raw value stored in the database, into the corresponding type defined by the field definitions on the model. (That is to say, they will be "demongoized".)
For example:
result = Person.raw.first
p result #=> {"_id"=>BSON::ObjectId(...), "name" => "...", "birth_date" => 2002-01-01 00:00:00 -0600, ... }
result = Person.raw(typed: true).first
p result #=> {"_id"=>BSON::ObjectId(...), "name" => "...", "birth_date" => 2002-01-01, ... }
results = Person.where(...).limit(...).raw
p results.to_a #=> [ { "_id"=>BSON::ObjectId(...), ... }, { "_id"=>BSON::ObjectId(...), ... }, ... ]The typed: true option will also honor embedded documents, correctly demongoizing the embedded hashes according to their declared types.
MONGOID-5752 Add Mongoid::Criteria#to_mql (PR)
To help with inspecting the queries that Mongoid generates, you can now call #to_mql on Criteria instances.
Person.where(...).to_mqlMONGOID-5827 Allow duplicate indexes to be declared (PR)
Mongoid will ignore indexes that differ only in the options used to declare them. This includes index names! This behavior will change in the future, but to ease the transition, you can set Mongoid.allow_duplicate_index_declarations = true to allow these near-identical indexes to be declared and processed by the server.
MONGOID-5882 Isolation state (PR)
Some applications will use threads for concurrency. Others will use fibers. Prior to this PR, Mongoid worked fine with threads, but it's internal state could get into odd states when run under fibers.
This PR allows applications to indicate which isolation level they wish to use, and Mongoid's state will be isolated to that scope.
# the default -- inherit the isolation level from `ActiveSupport::IsolatedExecutionState` if possible
Mongoid.isolation_level = :rails
# The following two explicit settings are supported:
Mongoid.isolation_level = :thread # the classic behavior
Mongoid.isolation_level = :fiber # NEW!When using the :fiber isolation level, Mongoid's internal state will be inherited from any parent fiber. If you want to make sure a fiber begins with a clean slate, you can wipe the isolation state with Mongoid::Threaded.reset!.
MONGOID-5892 - Treat serialize option only with values of nil and [] differently (PR)
Previously, passing only: [] to serializable_hash was treated the same as passing only: nil, meaning all fields were included. The correct behavior is to treat an empty array as "include no fields," suppressing all output.
This fix is gated behind the Mongoid.serializable_hash_with_legacy_only feature flag. In 9.1 and later, the flag defaults to true (legacy behavior); set it to false to opt into the new behavior. The default will flip to false in Mongoid 10.0, and the flag will be removed in 11.0.
MONGOID-5910 Implement enumerable methods on top level models (any?, many?, and one?) (PR)
You may now invoke the enumerable methods #any?, #many?, and #one? on Criteria instances. Invoking them at the class level will delegate to those methods, as well.
User.any? #-> returns true if any (1 or more) user records exist
User.where(admin: true).one? #-> returns true if there is exactly one admin record
User.where(banned: true).many? #-> returns true if there are "many" (2 or more) banned usersMONGOID-5731 Add Criteria#eager_load method to use aggregation pipeline for eager loading (PR)
A new Criteria method, #eager_load, has been added. It is effectively the same as using #includes (joining associations into a single result), but it uses an aggregation pipeline under the covers to issue a single query, instead of the multiple queries that #includes may emit. The #eager_load method will typically perform better on has_many and has_and_belongs_to_many associations, and on queries where you are joining on multiple belongs_to or has_one associations.
people = Person.eager_load(children: %i[ hobbies favorites ], :employer, :partner)If you use #eager_load to attempt to join on an embedded association (embeds_one, embeds_many), it will silently fall back to using #includes.
MONGOID-5930 Add Mongoid::Config.allow_reparenting_via_nested_attributes (PR)
Add Mongoid::Config.allow_reparenting_via_nested_attributes, defaulting to false. When false, this prevents dependent has_many records from being reparented via use of nested attributes. When true, records may be reparented via nested attributes.
This setting will be removed in Mongoid 10, and reparenting via nested attributes will not be allowed.
MONGOID-5898 Add vector search support (PR)
Mongoid can now define vector search indexes, and provides a helper interface for querying documents by their semantic meaning using those indexed vector fields.
The simplest interface will define an Array field, and a corresponding vector search index:
class Article
include Mongoid::Document
# Define `embedding` as an Array field, and define a vector search index on it
# with `dimensions` set to 1536.
vector_field :embedding, dimensions: 1536
endFor more sophisticated configurations, you may also declare the field and the index separately:
class Article
include Mongoid::Document
field :embedding, type: Array
vector_search_index :embedding_index,
fields: [
{ type: 'vector',
path: 'embedding',
dimensions: 1536,
...
}
]
endThen, create the new search indexes:
$ rake db:mongoid:create_search_indexesOnce the indexes are created, you can query them using the vector_search method:
articles = Article.vector_search(vector, limit: 5)
# or, search for documents that are similar to an existing document
neighbors = article.vector_search(limit: 5)MONGOID-5923 Support auto emdedding vector search indexes and searches (PR)
Mongoid can now define vector search indexes with auto-embedding, and provides a helper interface for querying documents by their semantic meaning using those indexed vector fields.
The simplest interface will define a String field using the voyage-4 model.
class Article
include Mongoid::Document
# Define `body` as a String field, indexed using the `voyage-4` auto-embedding model.
auto_embed_field :body
endYou may declare the model and other parameters as well, if desired:
class Article
include Mongoid::Document
auto_embed_field :body,
model: 'voyage-4-large'
num_dimensions: 512,
quantization: 'binary',
similarity: 'cosine',
index: :body_index
endThen, create the new search indexes:
$ rake db:mongoid:create_search_indexesOnce the indexes are created, you can query them using the auto_embed_search method:
articles = Article.auto_embed_search('machine learning', limit: 5)
# or, search for documents that are similar to an existing document
neighbors = article.auto_embed_search(limit: 5)MONGOID-5750 Allow build_foo on embeds_one/has_one/etc. to specify the associated document's type (PR)
When using build_<association> on an embeds_one/has_one/belongs_to (etc.) association, you can now specify the type of the associated document. For example:
class Job
include Mongoid::Document
has_one :runner
end
class Runner
include Mongoid::Document
belongs_to :job
field :name, type: String
end
class HeadlessRunner < Runner
end
job = Job.create
job.build_runner({ name: 'My Runner' }, HeadlessRunner)The same syntax works with create_<association>.
MONGOID-5030 Short-circuit trivial queries where $in is given an empty array (PR)
When Mongoid.allow_short_circuit_queries == true, any top-level $in query condition that evaluates to an empty array will cause the query to be short-circuited, returning an empty array without sending the query to the database.
Mongoid.allow_short_circuit_queries = true
Document.in([]) #=> empty array, no database query issued
Mongoid.allow_short_circuit_queries = false
Document.in([]) #=> empty array, one database query issuedThe Mongoid.allow_short_circuit_queries setting defaults to false.
Other New Features
- MONGOID-4680 implement cache_version for better Rails caching support (PR)
- Use in-memory aggregation for eager-loaded has_many associations (PR)
- MONGOID-5720 deprecate Criteria#max_scan (PR)
Bug Fixes
MONGOID-5867 Merge touch updates into embedded document insert (PR)
When inserting an embedded document with a touchable parent (the default for embedded_in), Mongoid issues two separate update_one calls:
$push— inserts the embedded document (insert_as_embedded)$set— updatesupdated_aton the parent chain (after_savetouch callback)
This commit merges both into a single update_one, eliminating the redundant round-trip.
insert_as_embeddedgathers touch updates via_gather_touch_updatesand merges the$setinto the same operation as the$push- The
after_savetouch callback checks aThreadedflag and skips the now-redundant persistence, while still running:touchcallbacks and cleaning up dirty tracking - The
Threadedflag follows the existingbegin_autosave/exit_autosave/autosaved?pattern
MONGOID-5751 avoid unnecessary autosaves of unchanged subtrees (PR)
The legacy behavior of associations with autosave: true resulted in all #save being invoked on all children of those associations, whether those children actually needed it or not. All corresponding after_save hooks were invoked as well, recursively, clear to the bottom of the autosave tree.
This PR changes this behavior, ensuring that subtrees are only autosaved if there are any changed documents in the subtree. As there may be users that depend on the legacy behavior (expecting after_save hooks to be invoked even if the record has not changed, for instance), a feature flag has been added to allow the legacy behavior to be opted into.
Mongoid.autosave_saves_unchanged_documents = trueWhen true, the legacy behavior prevails. If your program depends on this legacy behavior, you are encouraged to rewrite the affected code, because this flag will go away in Mongoid 10, and the legacy autosave behavior will be removed.
Other Bug Fixes
- MONGOID-5822: Embedded association validations break when association depends on parent update (PR)
- Fix typos (PR)
- MONGOID-5800 When initializing an embedded association with an invalid hash, got undefined method `with_indifferent_access' error (PR)
- MONGOID-5842 fix numericality validator when given non-numeric strings (PR)
- MONGOID-5927 init_atomic_updates from updatable.rb does not account for conflicts between set and unset commands (PR)
- MONGOID-5922 fall back to #includes if #eager_load is given associations in different clusters (PR)
- MONGOID-5759 ensure unpersisted documents are picked up via HasMany#find (PR)
- MONGOID-5684 Ensure field_was returns the same type as field for hash-typed fields (PR)