-
Notifications
You must be signed in to change notification settings - Fork 1.4k
MONGOID-5128 Scoped associations #5017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Tracked in https://jira.mongodb.org/browse/MONGOID-5128 |
@agolin95 @p-mongo it would be great to get a code review on this when you have time. Please let me know if I'm headed in the right direction here. |
@johnnyshields I've asked to take @comandeo to review this while @p-mongo is on well-deserved PTO. We'll get back to you once we've had a chance to take a look. In the meantime, I've authorised the Evergreen patch so you may see some build results. |
OK, great. Yes, Evergreen being actually 🟢 green would be really helpful. |
4acffc5
to
1851b05
Compare
Hmmm... so PR is progressing well, however, I'm running into a bit of a wall the more I think about my use case. I'd like to do be able to define multiple scopes for the same association: class Game
has_many :players
has_many :active_players, scope: ->{ is_active: true }, class_name: 'Player', foreign_key: :game_id, inverse_of: nil
end This adds a lot of boilerplate to achieve what I want and feels hacky. There's got to be a better way. My first thought was to do it as an (extension)[https://docs.mongodb.com/mongoid/current/tutorials/mongoid-relations/#extensions], i.e.: class Game
has_many :players do
def active
where(is_active: true)
end
end
end However, this won't work with eager loading. Perhaps it could be defined as a scope such as: has_many :players do
scope :active_players, ->{ is_active: true } # Option A
end And do eager loading by callling Alternatively, we could do: class Game
has_many :players
has_many :active_players, extends: :players, scope: ->{ is_active: true } # Option B
# or
association_scope :active_players, :players, ->{ is_active: true } # Option C
end Option C |
I've implemented |
@p-mongo would like to get your comments on this. |
I support implementing the AR-compatible syntax for defining the scope criteria. Mongoid (and I believe AR too) already provides scoping under associations:
|
Yes, BUT there is one important point. You can't eager load scoped associations. In other words:
That being said I'm fine to move |
I believe that pattern is already possible to implement in Mongoid applications simply by defining additional associations for each needed scope and setting |
Hmmm.... I'm not following. Can you provide a code (or pseudo-code) example? |
9348320
to
29c74b4
Compare
@p-mongo this is now ready for review. I've dropped the |
29c74b4
to
c4e173d
Compare
end | ||
|
||
let(:object) do | ||
BSON::ObjectId.new | ||
end | ||
|
||
before do | ||
expect(Person).to receive(:where).with(association.primary_key => object).and_call_original | ||
expect_any_instance_of(Mongoid::Criteria).to receive(:where).with(association.primary_key => object).and_call_original |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this and similar changes throughout the PR please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's related to this change in buildable:
def query_criteria(object, base)
crit = klass.criteria # <== this line
crit = crit.apply_scope(scope)
crit = crit.where(foreign_key => object)
with_polymorphic_criterion(crit, base)
end
We're now explicitly creating the criteria first line, so the call doesn't happen on the Person class, it happens on a Criteria object.
Overall this looks to be very thorough, thank you for this PR. AR defines its association thusly:
The scope argument was added in 4.0. Previously the definition looked like this:
I imagine such a change would be a breaking one, hence I see the value in using a keyword argument for scope. At the same time it should be relatively straightforward to support both invocations by examining the type of
|
@p-mongo I am intentionally not matching ActiveRecord's "second arg" syntax. There is no strict requirement in Mongoid that we exactly match ActiveRecord's decisions; we should be free to deviate where it makes sense. |
@comandeo ready for your review. |
@comandeo What is your opinion on the question of whether Mongoid should match AR behavior and accept the scope as a positional argument? I see three options at this point: Positional argument only, matches AR behavior. Keyword argument only. This is the current implementation in this PR. Not compatible with AR in either direction. Both keyword argument and positional argument are allowed. Compatible with AR in that AR-targeting code will work with Mongoid, but permits also specifying all association options as keyword arguments which I can see how this can be more consistent. |
I think in this case it is better to be consistent and treat all arguments similarly. I agree that we should be as close as possible to AR in general; however, supporting both ways here won't bring us benefits, actually. |
@comandeo the AR syntax for this option in is quite ugly. It really doesn't make sense for |
|
I take back my previous comment. Having it as a positional arg makes the code messier. Let's keep it as a keyword arg, think it's cleaner. Another reason for it being a keyword is that it's not present on embedded relations, which do not exist in AR. |
I think we should merge this as soon as CI is green. @johnnyshields I gave the Evergreen a kick, and all configurations (except app tests) fail with the following:
It looks like something is missing there. Could you please take a look? Thank you! |
@comandeo fixed! |
* master: MONGOID-5151 Respect aliased fields in pluck/distinct by having Document.database_field_name recursively consider embedded docs (mongodb#5047) RUBY-2675 test coverage on the Mongoid side (mongodb#5030) MONGOID-4592 Add examples of using read_attribute and write_attribute to implement custom field behavior (mongodb#5082) MONGOId-5185 Remove tests for _id serialization (mongodb#5081) MONGOID-5103 Implement eq symbol operator (mongodb#5076) Fix MONGOID-5006 Link default auth source documentation to driver instead of incorrectly claiming "admin" is always the default (mongodb#5048) Create security policy following github's template (mongodb#5070)
I removed the link to named scopes because it was broken and I couldn't find anywhere in our docs where we actually document them. @johnnyshields where was that supposed to go? |
Named scopes was supposed to link to here: https://docs.mongodb.com/mongoid/current/tutorials/mongoid-queries/#named-scopes |
This reverts commit 8e4fea7.
* master: (26 commits) MONGOID-5128 Scoped associations (mongodb#5017) MONGOID-5005 - .sum, .count, and similar aggregables now ignore sort if not limiting/skipping (mongodb#5049) MONGOID-5098 Improve specs for timezone handling (specs only; no behavior change) (mongodb#5023) MONGOID-5151 Respect aliased fields in pluck/distinct by having Document.database_field_name recursively consider embedded docs (mongodb#5047) RUBY-2675 test coverage on the Mongoid side (mongodb#5030) MONGOID-4592 Add examples of using read_attribute and write_attribute to implement custom field behavior (mongodb#5082) MONGOId-5185 Remove tests for _id serialization (mongodb#5081) MONGOID-5103 Implement eq symbol operator (mongodb#5076) Fix MONGOID-5006 Link default auth source documentation to driver instead of incorrectly claiming "admin" is always the default (mongodb#5048) Create security policy following github's template (mongodb#5070) MONGOID-5131 Use proper mdb <-> ubuntu versions (mongodb#5067) MONGOID-5131: Set up build pipeline for outside contributors (mongodb#5043) MONGOID-5165 Fix documentation link (mongodb#5065) Fix indentation MONGOID-5029 #empty method should use an #exists? rather than a #count query (mongodb#5029) MONGOID-5177 Fix flaky contextual spec (mongodb#5064) MONGOID-5170 - Fix more typos mostly in code docs and code comments (mongodb#5056) MONGOID-5105 Allow block form in Mongoid::Association::EmbedsMany::Proxy#count (mongodb#5060) MONGOID-5162 Add a release note for the planned changes in ObjectId#as_json (mongodb#5059) MONGOID-5171 - Make the minimum Ruby version 2.5 (mongodb#5058) ...
* master: MONGOID-5128 Scoped associations (mongodb#5017) MONGOID-5005 - .sum, .count, and similar aggregables now ignore sort if not limiting/skipping (mongodb#5049) MONGOID-5098 Improve specs for timezone handling (specs only; no behavior change) (mongodb#5023) MONGOID-5151 Respect aliased fields in pluck/distinct by having Document.database_field_name recursively consider embedded docs (mongodb#5047) RUBY-2675 test coverage on the Mongoid side (mongodb#5030) MONGOID-4592 Add examples of using read_attribute and write_attribute to implement custom field behavior (mongodb#5082) MONGOId-5185 Remove tests for _id serialization (mongodb#5081) MONGOID-5103 Implement eq symbol operator (mongodb#5076) Fix MONGOID-5006 Link default auth source documentation to driver instead of incorrectly claiming "admin" is always the default (mongodb#5048) Create security policy following github's template (mongodb#5070)
Ready for review
Fixes MONGOID-5128
ActiveRecord allows you to do this:
I'm proposing equivalent syntax in Mongoid using
:scope
keyword arg.If you prefer I could match the Rails syntax exactly, but I thought this was a bit clearer:
In my implementation you can also use a Symbol to access a named scope:
I have some planned enhancements to this functionality but this PR is a good "minimum mergeable spec".
See: