Skip to content

Commit

Permalink
MONGOID-5408 .evolve should support wrapper class (#5448)
Browse files Browse the repository at this point in the history
Co-authored-by: shields <[email protected]>
  • Loading branch information
johnnyshields and johnnyshields authored Feb 14, 2023
1 parent 5295b32 commit 67c211d
Show file tree
Hide file tree
Showing 9 changed files with 791 additions and 36 deletions.
82 changes: 48 additions & 34 deletions docs/reference/queries.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,58 +168,45 @@ name, as follows:
# class: Band
# embedded: false>

Embedded Documents
==================

To match values of fields of embedded documents, use the dot notation:

.. code-block:: ruby

Band.where('manager.name' => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>"Smith"}
# options: {}
# class: Band
# embedded: false>

Band.where(:'manager.name'.ne => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>{"$ne"=>"Smith"}}
# options: {}
# class: Band
# embedded: false>

.. note::
Fields
======

Queries always return top-level model instances, even if all of the
conditions are referencing embedded documents.

Field Types
===========
Querying on Defined Fields
--------------------------

In order to query on a field, it is not necessary to add the field to
:ref:`the model class definition <fields>`. However, if a field is defined in
the model class, the type of the field is taken into account when constructing
the query:
the model class, Mongoid will coerce query values to match defined field types
when constructing the query:

.. code-block:: ruby

Band.where(name: 2020)
Band.where(name: 2020, founded: "2020")
# => #<Mongoid::Criteria
# selector: {"name"=>"2020"}
# selector: {"name"=>"2020", "founded"=>2020}
# options: {}
# class: Band
# embedded: false>

Band.where(founded: 2020)
Querying for Raw Values
-----------------------

If you'd like to bypass Mongoid's query type coercion behavior and query
directly for the raw-typed value in the database, wrap the query value in
``Mongoid::RawValue`` class. This can be useful when working with legacy data.

.. code-block:: ruby

Band.where(founded: Mongoid::RawValue("2020"))
# => #<Mongoid::Criteria
# selector: {"founded"=>2020}
# selector: {"founded"=>"2020"}
# options: {}
# class: Band
# embedded: false>

Aliases
=======
Field Aliases
-------------

Queries take into account :ref:`storage field names <storage-field-names>`
and :ref:`field aliases <field-aliases>`:
Expand All @@ -245,6 +232,33 @@ Since ``id`` and ``_id`` fields are aliases, either one can be used for queries:
# embedded: false>


Embedded Documents
==================

To match values of fields of embedded documents, use the dot notation:

.. code-block:: ruby

Band.where('manager.name' => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>"Smith"}
# options: {}
# class: Band
# embedded: false>

Band.where(:'manager.name'.ne => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>{"$ne"=>"Smith"}}
# options: {}
# class: Band
# embedded: false>

.. note::

Queries always return top-level model instances, even if all of the
conditions are referencing embedded documents.


.. _logical-operations:

Logical Operations
Expand Down
18 changes: 18 additions & 0 deletions docs/release-notes/mongoid-9.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ ignored for embedded documents; an embedded document now always uses the persist
context of its parent.


Support for Passing Raw Values into Queries
-------------------------------------------

When performing queries, it is now possible skip Mongoid's type coercion logic
using the ``Mongoid::RawValue`` wrapper class. This can be useful when legacy
data in the database is of a different type than the field definition.

.. code-block:: ruby

class Person
include Mongoid::Document
field :age, type: Integer
end

# Query for the string "42", not the integer 42
Person.where(age: Mongoid::RawValue("42"))


Raise AttributeNotLoaded error when accessing fields omitted from query projection
----------------------------------------------------------------------------------

Expand Down
6 changes: 4 additions & 2 deletions lib/mongoid/criteria/queryable/selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def to_pipeline

# Get the store name and store value. If the value is of type range,
# we need may need to change the store_name as well as the store_value,
# therefore, we cannot just use the evole method.
# therefore, we cannot just use the evolve method.
#
# @param [ String ] name The name of the field.
# @param [ Object ] serializer The optional serializer for the field.
Expand Down Expand Up @@ -151,6 +151,8 @@ def evolve_multi(specs)
# @return [ Object ] The serialized object.
def evolve(serializer, value)
case value
when Mongoid::RawValue
value.raw_value
when Hash
evolve_hash(serializer, value)
when Array
Expand Down Expand Up @@ -228,7 +230,7 @@ def evolve_hash(serializer, value)
#
# @api private
#
# @param [ String ] key The to store the range for.
# @param [ String ] key The key at which to store the range.
# @param [ Object ] serializer The optional serializer for the field.
# @param [ Range ] value The Range to serialize.
#
Expand Down
1 change: 1 addition & 0 deletions lib/mongoid/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def transform_keys
require "mongoid/extensions/object"
require "mongoid/extensions/object_id"
require "mongoid/extensions/range"
require "mongoid/extensions/raw_value"
require "mongoid/extensions/regexp"
require "mongoid/extensions/set"
require "mongoid/extensions/string"
Expand Down
32 changes: 32 additions & 0 deletions lib/mongoid/extensions/raw_value.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

# Wrapper class used when a value cannot be casted in evolve method.
module Mongoid

# Instantiates a new Mongoid::RawValue object. Used as a syntax shortcut.
#
# @example Create a Mongoid::RawValue object.
# Mongoid::RawValue("Beagle")
#
# @return [ Mongoid::RawValue ] The object.
def RawValue(*args)
RawValue.new(*args)
end

class RawValue

attr_reader :raw_value

def initialize(raw_value)
@raw_value = raw_value
end

# Returns a string containing a human-readable representation of
# the object, including the inspection of the underlying value.
#
# @return [ String ] The object inspection.
def inspect
"RawValue: #{raw_value.inspect}"
end
end
end
Loading

0 comments on commit 67c211d

Please sign in to comment.