ExportXMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Unresolved
    • Priority: Unknown
    • None
    • Affects Version/s: 8.1.13
    • Component/s: Attributes
    • None
    • None
    • Ruby Drivers
    • None
    • None
    • None
    • None
    • None
    • None

      Summary

      In Mongoid 8.1, a Hash assigned to a field (including hashes nested in an Array field) keeps its original keys in memory, while the persisted value has string keys. This was fixed in 9.0.0 by MONGOID-5410 ("Indifferent access into hashes nested in array field does not work before persistence"), which changed Hash.mongoize to always return a BSON::Document. That change has not been backported to 8.1.

      I'd like to propose backporting MONGOID-5410 to 8.1 behind a new opt-in config flag, in the same way as Mongoid::Config.immutable_ids. The flag would default to false, so existing behavior is unchanged, and applications could set it to true to adopt the 9.0 behavior before upgrading.

      Background

      I originally reported this in MONGOID-5871, which was closed as "Works as Designed". The explanation there was that the attributes hash is always a plain Hash in 9.x, so keys are not converted. However, in 9.x the assigned values themselves are normalized by MONGOID-5410, so the behavior reported in MONGOID-5871 does not reproduce on 9.0. Since I cannot reopen MONGOID-5871, I am filing this ticket instead.

      Steps to Reproduce

      class Request
        include Mongoid::Document
        field :params, type: Array
      end
      
      req = Request.create!(params: [{ date_from: "2025-05-01" }, 1])
      
      req.params
      # Mongoid 8.1.13 => [{:date_from=>"2025-05-01"}, 1]    (symbol key, in memory)
      # Mongoid 9.0.12 => [{"date_from"=>"2025-05-01"}, 1]   (string key)
      
      Request.find(req.id).params
      # both versions  => [{"date_from"=>"2025-05-01"}, 1]   (string key, persisted)
      

      On 8.1 the in-memory value differs from the persisted value until the document is reloaded. With legacy_attributes: true, documents loaded from the database happen to show string keys (because their attributes hash is a BSON::Document), but documents created with Model.new still keep symbol keys, so the behavior is inconsistent in that mode too.

      Proposed Change

      • Add Mongoid::Config.mongoize_hashes_as_bson_documents (default: false). The name is open to suggestions.
      • When the flag is true, Hash.mongoize converts a plain Hash to a BSON::Document, as in 9.0.
      • When the flag is false, the current 8.1 behavior is preserved.
      • Add specs and a section in the 8.1 release notes, following the existing immutable_ids section.

      I have a working implementation on 8.1-stable and will open a PR referencing this ticket. Locally, with the flag at its default (false), hash_spec, fields_spec, attributes_spec, changeable_spec and config_spec pass (1088 examples, 0 failures). With the flag defaulted to true, the only failures are the 4 examples whose expectations were updated by the original MONGOID-5410 change (PR #5420), which confirms the flag reproduces the 9.0 behavior.

      Motivation

      We are running Mongoid 8.1 in production and are preparing to upgrade to 9.0. Being able to opt in to this behavior on 8.1 lets us find and fix code that depends on symbol keys in mongoized hashes ahead of the major upgrade, instead of discovering the differences all at once during the 9.0 migration.

      Environment

      • Mongoid: 8.1.10 (our application), also verified on 8.1-stable (8.1.13); compared with 9.0.12
      • MongoDB: 7.0
      • Ruby: 3.3
      • Rails: 8.0

      Related

            Assignee:
            Unassigned
            Reporter:
            Jin Oketani (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: