Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-5619

_matches? does not account for comparison types

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Critical - P2 Critical - P2
    • None
    • Affects Version/s: None
    • Component/s: Query
    • None

      I believe that mongoid's function _matches? (in-memory comparison) should behave the same way a call to the database would. In the example below, I'll explain how I think _matches? is incorrectly casting values of the object, so that the type matches the comparison.

       

      class User
        include Mongoid::Document
      
        # Key has the type of String, value can be any data type (String, Date, Bool, etc)
        # Example: {"y" => "this is a string"}
        # Another example: {"x" => 1, "y" => "this is a string", "z" => Time.parse("2023-05-01")}
        field :a, type: Hash
      end

       

      Scenario 1

      # Add a document where the hash value of "y" is a String
      user1 = User.create({ :a => { :y => "2023-05-01" } })
      
      # Verify that the property is indeed a string
      user1[:a][:y].class # => String
      User.collection.find({}).first[:a][:y].class #  => String
      
      # Selector is a filter on the "y" key inside the Hash "a" using a Time param
      selector = {"a.y" => {"$gt" => Time.parse("2023-04-02 06:59:59 UTC")}}
      
      # Mongo driver returns zero results using this selector
      User.collection.find(selector).count # => 0
      
      # Using mongoid to run a query
      User.where(selector).count # => 0
      
      # But the _matches? method in mongoid returns as matching
      # I believe the value here should be false since there was no match in the db
      user1._matches?(selector) # => true 

       

      Scenario 2

      # Move the selector time into the future, still using Time as a param
      selector = {"a.y" => {"$gt" => Time.parse("2023-12-12 06:59:59 UTC")}}
      
      # Query still returns 0 as expected
      User.where(selector).count # => 0
      
      # Mongoid now returns false, leading me to believe user1[:a][:y] is being coerced into Time
      user1._matches?(selector) # => false 

       

      Scenario 3

      # Let's try again where user's "a.y" cannot be coerced to Time
      user2 = User.create({:a => { :y => "foo bar" }})
      
      
      # Mongo driver still returns 0 as expected
      User.where(selector).count # => 0
      
      # Mongoid as expected returns false
      user2._matches?(selector) # => false 

       

      I think the implicit coercion would be desired if the field we're working with was specified as Time, but in the case of a Hash - the type is unknown and seems to be returning incorrect results.

            Assignee:
            Unassigned Unassigned
            Reporter:
            cameron.moreau@braze.com Cameron Moreau
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated: