-
Type:
Bug
-
Resolution: Duplicate
-
Priority:
Major - P3
-
None
-
Affects Version/s: 9.1.0
-
Component/s: None
-
None
-
- 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.
-
- 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
```
-
- 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.
-
- 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.
-
- 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`.
-
- 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.
-
- 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.
-
- Environment
Mongoid 9.1.0
The application stores explicit empty foreign-key arrays in its association data.
- is fixed by
-
MONGOID-6001 Mongoid 9.1.0: resetting a has_and_belongs_to_many association with an empty foreign-key array raises NoMethodError
-
- Closed
-