ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Duplicate
    • Priority: Major - P3
    • None
    • Affects Version/s: 9.1.0
    • Component/s: None
    • None
    • None
    • Ruby Drivers
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

        1. Description

      Mongoid 9.1.0 raises `NoMethodError` when a `has_and_belongs_to_many` association stores an empty foreign-key array and the association is reset before it is enumerated. Mongoid 9.0.9 does not reproduce the issue.

      Mongoid itself can create this data. For example, `Team.create!(user_ids: [])` stores an explicit empty array.

        1. Reproduction

      ```ruby
      class User
      include Mongoid::Document
      end

      class Team
      include Mongoid::Document
      has_and_belongs_to_many :users
      end

      team = Team.create!(user_ids: [])
      team.reload

      users = team.users
      users.reset
      users.empty?
      users.size
      users.to_a
      ```

      On Mongoid 9.1.0, calls after `reset` raise `NoMethodError`. For example, `to_a` raises:

      ```text
      NoMethodError: undefined method `selector` for nil
      ```

        1. Expected behaviour

      An explicitly stored empty foreign-key array should represent an empty association. After `reset`, `empty?`, `size`, and enumeration should return the empty result without querying the database.

        1. Actual behaviour

      `HasAndBelongsToMany::Buildable#build` treats an empty array as `$lookup` output because `[].all?

      { |o| o.is_a?(Hash) }

      ` returns `true`. It returns a plain Array, so `HasMany::Enumerable` has no unloaded criteria. After `reset`, `empty?`, `size`, or enumeration can call methods on the missing criteria object.

        1. Root cause

      The array-of-hashes `$lookup` branch was added by MONGOID-5731 in [pull request 6081](https://github.com/mongodb/mongoid/pull/6081). Mongoid 9.0.9 did not have this branch, so the empty array reached `query_criteria` instead.

      The same Array target issue also exists for some `$lookup` results, including non-empty `has_and_belongs_to_many` results and empty `has_many` results. Those cases predate this regression and are outside this fix. They should be addressed separately in `HasMany::Enumerable`.

        1. Related Mongoid tickets

      MONGOID-5030 covers short-circuiting queries whose `$in` value is an empty array. This issue needs an association-specific `none` criteria because the target's `empty?` and `size` methods must avoid a query even when the general short-circuit option is disabled. Proxy `count` and `exists?` are outside this guarantee because they build criteria from the stored IDs.

        1. Suggested fix

      The `$lookup` branch should accept only non-empty arrays of hashes. For an empty foreign-key array, the implementation should pass `nil` to `query_criteria`, which returns `crit.none`. This preserves a criteria-backed target and avoids database queries from the target's `empty?`, `size`, and enumeration methods.

      The existing eager-load specs cover the `includes`/preload path, including empty results without an extra query. The persisted empty-array path needs a separate regression test because it reaches `Buildable#build` through a different route.

        1. Environment

      Mongoid 9.1.0

      The application stores explicit empty foreign-key arrays in its association data.

            Assignee:
            Dmitry Rybakov
            Reporter:
            Tatsuhiro Ujihisa (EXT)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: