diff --git a/docs/tutorials/mongoid-documents.txt b/docs/tutorials/mongoid-documents.txt index 2b57529010..06f875f3ac 100644 --- a/docs/tutorials/mongoid-documents.txt +++ b/docs/tutorials/mongoid-documents.txt @@ -377,6 +377,31 @@ or else the values will not store properly. end end + +Time Fields +----------- + +``Time`` fields store values as ``Time`` instances in the :ref:`configured +time zone `. + +``Date`` and ``DateTime`` instances are converted to ``Time`` instances upon +assignment to a ``Time`` field: + +.. code-block:: ruby + + class Voter + include Mongoid::Document + + field :registered_at, type: Time + end + + Voter.new(registered_at: Date.today) + # => # + +In the above example, the value was interpreted as the beginning of today in +local time, because the application was not configured to use UTC times. + + Date Fields ----------- diff --git a/docs/tutorials/mongoid-queries.txt b/docs/tutorials/mongoid-queries.txt index 6963cbf42f..7a025d4d69 100644 --- a/docs/tutorials/mongoid-queries.txt +++ b/docs/tutorials/mongoid-queries.txt @@ -1478,6 +1478,63 @@ It is also possible to query using PCRE syntax by constructing # => # +Conditions On Fields +==================== + +When a condition uses a field defined in the model, the value being specified +in the condition is converted according to the rules of the field, if any. +For example, consider the following model definition that contains a ``Time`` +field, a ``Date`` field and an implicit ``Object`` field, and also +intentionally does not define a field called ``deregistered_at``: + + .. code-block:: ruby + + class Voter + include Mongoid::Document + + field :born_on, type: Date + field :registered_at, type: Time + field :voted_at + end + +Queries on ``born_on`` and ``registered_at`` fields using ``Date`` and ``Time`` +values, respectively, are straightforward: + + .. code-block:: ruby + + Voter.where(born_on: Date.today).selector + # => {"born_on"=>2020-12-18 00:00:00 UTC} + + Voter.where(registered_at: Time.now).selector + # => {"registered_at"=>2020-12-19 04:33:36.939788067 UTC} + +But, note the differences in behavior when providing a ``Date`` instance +in all possible scenarios: + + .. code-block:: ruby + + Voter.where(born_on: Date.today).selector + # => {"born_on"=>2020-12-18 00:00:00 UTC} + + Voter.where(registered_at: Date.today).selector + # => {"registered_at"=>2020-12-18 00:00:00 -0500} + + Voter.where(voted_at: Date.today).selector + # => {"voted_at"=>Fri, 18 Dec 2020} + + Voter.where(deregistered_at: Date.today).selector + # => {"deregistered_at"=>2020-12-18 00:00:00 UTC} + +When using the ``registered_at`` field which is of type ``Time``, the date +was interpreted to be in local time (as per the :ref:`configured time zone +`). When using the ``born_on`` field which is of type ``Date``, +the date was interpreted to be in UTC. When using the ``voted_at`` field +which was defined without a type (hence implicitly as an ``Object``), +the date was used unmodified in the constructed query. When using a +nonexistent field ``deregistered_at`` the date was interpreted to be in UTC +and converted to a time, matching the behavior of querying a ``Date`` field. + + Queries + Persistence ===================== diff --git a/lib/mongoid/criteria/queryable/selector.rb b/lib/mongoid/criteria/queryable/selector.rb index e02a743495..6c13fb7fac 100644 --- a/lib/mongoid/criteria/queryable/selector.rb +++ b/lib/mongoid/criteria/queryable/selector.rb @@ -123,10 +123,6 @@ def evolve_multi(specs) # {'foo' => {'$lt' => 5}}. This step should be done after all # value-based processing is complete. if key.is_a?(Key) - if serializer && evolved_value != value - raise NotImplementedError, "This method is not prepared to handle key being a Key and serializer being not nil" - end - evolved_value = key.transform_value(evolved_value) end diff --git a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb index 2e5a7fea00..ebf41c3ce1 100644 --- a/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +++ b/spec/mongoid/criteria/queryable/selectable_logical_spec.rb @@ -1517,6 +1517,42 @@ end end end + + context 'when using multiple criteria and symbol operators' do + context 'when using fields that meaningfully evolve values' do + + let(:query) do + Dictionary.any_of({a: 1}, :published.gt => Date.new(2020, 2, 3)) + end + + it 'generates the expected query' do + query.selector.should == {'$or' => [ + {'a' => 1}, + # Date instance is converted to a Time instance in local time, + # because we are querying on a Time field and dates are interpreted + # in local time when assigning to Time fields + {'published' => {'$gt' => Time.local(2020, 2, 3)}}, + ]} + end + end + + context 'when using fields that do not meaningfully evolve values' do + + let(:query) do + Dictionary.any_of({a: 1}, :submitted_on.gt => Date.new(2020, 2, 3)) + end + + it 'generates the expected query' do + query.selector.should == {'$or' => [ + {'a' => 1}, + # Date instance is converted to a Time instance in UTC, + # because we are querying on a Date field and dates are interpreted + # in UTC when persisted as dates by Mongoid + {'submitted_on' => {'$gt' => Time.utc(2020, 2, 3)}}, + ]} + end + end + end end describe "#not" do diff --git a/spec/support/models/dictionary.rb b/spec/support/models/dictionary.rb index a435b21d64..8bd948cc3c 100644 --- a/spec/support/models/dictionary.rb +++ b/spec/support/models/dictionary.rb @@ -6,7 +6,13 @@ class Dictionary field :name, type: String field :publisher, type: String field :year, type: Integer + + # This field must be a Time field :published, type: Time + + # This field must be a Date + field :submitted_on, type: Date + field :description, type: String, localize: true field :l, type: String, as: :language has_many :words, validate: false