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

Hash/array fields mongoize values upon writes recursively, permanently altering the values that user provided

    • Type: Icon: Bug Bug
    • Resolution: Unresolved
    • Priority: Icon: Unknown Unknown
    • None
    • Affects Version/s: None
    • Component/s: None

      When a type with a nontrivial `mongoize` implementation is stored in a Mongoid document as a value in a field of type Hash or Array, the value gets mongoized (which is a transformation) and subsequent attribute reads/accesses return the mongoized value which is different from the value that was provided to the model to begin with.

      Example:

      class Mop
        include Mongoid::Document
      
        field :f, type: Hash
        field :g, type: Array
      end
      
      irb(main):071:0> a=Mop.new(f: {hello: 1..2})
      => #<Mop _id: 61dfff7da15d5d6a60e99471, int_field: nil, array_field: nil, date_field: nil, tim...
      irb(main):072:0> a.f[:hello]
      => {"min"=>1, "max"=>2}
      irb(main):073:0> a.f
      => {:hello=>{"min"=>1, "max"=>2}}
      
      irb(main):080:0> a=Mop.new(g: [hello: 1..2])
      => #<Mop _id: 61dfffa6a15d5d6a60e99472, int_field: nil, array_field: nil, date_field: nil, tim...
      irb(main):081:0> a.g
      => [{:hello=>{"min"=>1, "max"=>2}}]
      

      Here, Range has non-trivial mongoization behavior, and once the f or g attributes are written, subsequent reads produce the range as a hash which is presumably how Mongoid would write it to the database.

      This behavior becomes more problematic when BigDecimal values are involved, as those would be converted to BSON::Decimal128 which then users will struggle using.

      Possible solution: add recursive demongoize methods to Hash and Array.

      A little further explanation on this possible solution. All values are mongoized on assignment and insertion into the attributes hash. However, the difference between those values and those nested in a hash or array is, these values are demongoized on reading them. So for example if Mop was:

      class Mop
        include Mongoid::Document
      
        field :f, type: Hash
        field :g, type: Array
        field :h, type: Range
      end
      
      a=Mop.new(h:1..2)
      a.h 
      # => 1..2
      a.attributes['h']
      # => {"min"=>1, "max"=>2}
      

      As you can see, the value for h is stored in its mongoized form and then demongoized when it's retrieved. If we can demongoize hashes recursively then we can get a similar functionality.

            Assignee:
            Unassigned Unassigned
            Reporter:
            oleg.pudeyev@mongodb.com Oleg Pudeyev (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: