-
Notifications
You must be signed in to change notification settings - Fork 2
Rename 'fields' to 'projection' #1
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
base: master
Are you sure you want to change the base?
Conversation
Hi @Nauzer. Thanks for your interest in this package. Can you give a brief example of the problem this solves? Are you using I've tried your fork on my own project, and it does the opposite - e.g, from
Instead of just returning the Compared to the
It only returns the Where is it documented that Meteor's Mongo Driver internally renames { fields: ... } to { projection: ...} , and where is it documented that we should be using Have you tried modifying your own code to use |
Hi @wildhart , I was actually not querying the user collection, rather another partitioned collection called GroupStats in my code.
Since I was on 1.2.0 of your plugin, logging the return value of my method showed that it was returning the whole Document instead of leaving out 'group.game' as I had been expecting it to do. Never validated this in the past to be frank. So I decided to fork Partitioner, checkout the corresponding commit (c97c6b2) and see why it was ignoring my passed 'fields' instruction.
This gave the following output: ![]() As you see on the 1st log, it 'magically' has 'projection' and not 'fields' although my code specifically adds 'fields', not 'projection'. Then fields _groupId: 0 is added, ending up with both projection and fields in the final situation (3). It was - at that point - an unconfirmed assumption that this is a result of Meteor handling backwards compatibility by mapping fields to projection internally. Diving into the Mongo Driver revealed that it seems to be the case: It seems that fields prevails over projection when both are present so that explains why it considers fields, but not the projection. Let me know what you think. We might need to handle Meteor's internal users collection differently from 'regular' collections (?) |
Can you share how your The hooks created within this package should be applied well before any lower level mongo driver changes the name of the |
We initiate all
Meteor-hooks are applied to insert but never on find. PS, awesome to see you The method is running on the server only by the way. |
Just out of interest, what happens if you import Partitioner before Mongo? (Might need to change the order on all field where they're imported). Or do you have other files where they're imported in a different order? Either way, the PR as you've submitted it will not be suitable, because in other projects the Thanks for the link to the driver code where the field/projection name is getting changed. I haven't seen that before. I'll have to do some research on when that was introduced. |
Based on this comment in the 2.6 Release Migration Guide:
Which was introduced in the same 2.6 release PR which added these bits of code you referenced: projection: cursorOptions.fields || cursorOptions.projection, It's seems that To retain attribution of your contribution to this PR (for which I'm very greatful), rather then making the changes myself, I will instead suggest some changes to your PR code which you can try out yourself and commit if you agree with them... |
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.
I've made my suggestions to make this backwards compatible. For completeness we should probably do the same to grouping_client.js
, i.e. add this:
// Since [email protected] {projection: {}} has been preferred over the deprecated {fields: {}}
// Note that string comparison breaks since "2.16" is newer than "2.9" - so need to pad with zeros
let release = Meteor.release.split('@')[1].split('.')
release = release[0] + '.' + release[1].padStart(2, '0')
let defaultProjectionField = parseFloat(release) >= 2.16 ? 'projection' : 'fields';
And then replacing both occurances of {fields: ...}
with {[defaultProjectionField]: ...}
Are you happy to add that to you PR?
// Publish admin and group for users that have it | ||
Meteor.publish(null, function () { | ||
return this.userId && Meteor.users._partitionerDirect.find(this.userId, {fields: {admin: 1, group: 1}}); | ||
return this.userId && Meteor.users._partitionerDirect.find(this.userId, {projection: {admin: 1, group: 1}}); |
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.
return this.userId && Meteor.users._partitionerDirect.find(this.userId, {projection: {admin: 1, group: 1}}); | |
// Since Meteor.[email protected] {projection: {}} has been preferred over the deprecated {fields: {}} | |
// Note that string comparison breaks since "2.16" is newer than "2.9" - so need to pad with zeros | |
let release = Meteor.release.split('@')[1].split('.') | |
release = release[0] + '.' + release[1].padStart(2, '0') | |
let defaultProjectionField = parseFloat(release) >= 2.16 ? 'projection' : 'fields'; | |
// Publish admin and group for users that have it | |
Meteor.publish(null, function () { | |
return this.userId && Meteor.users._partitionerDirect.find(this.userId, {[defaultProjectionField]: {admin: 1, group: 1}}); |
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 a reason you check for 2.16 instead of 2.6 here?
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.
Sorry, my mistake. I was testing this on a project on 2.16, so I had that number in my head when I wrote this. The test, when converted to floating point, should be parseFloat(release) >= 2.06
.
Well spotted, thanks.
|
||
if (!groupId) { | ||
const user = Meteor.users._partitionerDirect.findOne(userId, {fields: {group: 1}}); | ||
const user = Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}); |
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.
const user = Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}); | |
const user = Meteor.users._partitionerDirect.findOne(userId, {[defaultProjectionField]: {group: 1}}); |
// If user is admin and not in a group, proceed as normal (select all users) | ||
// do user2 findOne separately so that the findOne above can hit the cache | ||
if (!groupId && Meteor.users._partitionerDirect.findOne(userId, {fields: {admin: 1}}).admin) return true; | ||
if (!groupId && Meteor.users._partitionerDirect.findOne(userId, {projection: {admin: 1}}).admin) return true; |
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.
if (!groupId && Meteor.users._partitionerDirect.findOne(userId, {projection: {admin: 1}}).admin) return true; | |
if (!groupId && Meteor.users._partitionerDirect.findOne(userId, {[defaultProjectionField]: {admin: 1}}).admin) return true; |
// If options already exist, add {_groupId: 0} unless projection has {foo: 1} somewhere | ||
if (options.projection == null) options.projection = {}; | ||
if (!Object.values(options.projection).some(v => v)) options.projection._groupId = 0; |
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.
I think this whole block could be:
// Adjust options to not return _groupId
// Pre Meteor 2.16 we will get options?.fields, post Meteor 2.16 we might get either.
let projectionField = options?.fields ? 'fields' : 'projection';
if (options == null) {
this.args[1] = {[defaultProjectionField]: {_groupId: 0}};
} else if (!options[projectionField]) {
options[defaultProjectionField] = {_groupId: 0};
} else if (!Object.values(options[projectionField]).some(v => v)) {
// If projection/fields already exist, add {_groupId: 0} unless it already has {foo: 1} somewhere
options[projectionField]._groupId = 0;
}
check(groupId, String); | ||
|
||
if (Meteor.users._partitionerDirect.findOne(userId, {fields: {group: 1}}).group) { | ||
if (Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}).group) { |
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.
if (Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}).group) { | |
if (Meteor.users._partitionerDirect.findOne(userId, {[defaultProjectionField]: {group: 1}}).group) { |
getUserGroup(userId) { | ||
check(userId, String); | ||
return (Meteor.users._partitionerDirect.findOne(userId, {fields: {group: 1}}) || {}).group; | ||
return (Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}) || {}).group; |
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.
return (Meteor.users._partitionerDirect.findOne(userId, {projection: {group: 1}}) || {}).group; | |
return (Meteor.users._partitionerDirect.findOne(userId, {[defaultProjectionField]: {group: 1}}) || {}).group; |
|
||
_isAdmin(_id) { | ||
return !!Meteor.users._partitionerDirect.findOne({_id, admin: true}, {fields: {_id: 1}}); | ||
return !!Meteor.users._partitionerDirect.findOne({_id, admin: true}, {projection: {_id: 1}}); |
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.
return !!Meteor.users._partitionerDirect.findOne({_id, admin: true}, {projection: {_id: 1}}); | |
return !!Meteor.users._partitionerDirect.findOne({_id, admin: true}, {[defaultProjectionField]: {_id: 1}}); |
} | ||
|
||
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {fields: {_groupId: 1}})?._groupId; | ||
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {projection: {_groupId: 1}})?._groupId; |
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.
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {projection: {_groupId: 1}})?._groupId; | |
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {[defaultProjectionField]: {_groupId: 1}})?._groupId; |
} | ||
|
||
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {fields: {_groupId: 1}})?._groupId; | ||
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {projection: {_groupId: 1}})?._groupId; |
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.
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {projection: {_groupId: 1}})?._groupId; | |
let currentGroupIds = collection._partitionerDirect.findOne(entityId, {[defaultProjectionField]: {_groupId: 1}})?._groupId; |
Awesome - I will have a look at it. In essence I think your suggestions to remain backwards compatibility are good. Let me know and I will update the PR for your review. Thanks for letting me contribute.
I just tested with Meteor 3.0 and the issue is the same here. Funny thing is, that the Meteor docs (even for 3.0) still use 'fields' instead of 'projection' and the internal mapping remains even though the 2.6 Release deprecated 'fields' over 'projection' as you correctly stated. https://docs.meteor.com/api/collections.html#fieldspecifiers Do you have connections to anyone in the Meteor core team? Otherwise @filipenevola might have a suggestion here. |
Because Meteor's Mongo Driver internally renames { fields: ... } to { projection: ...} to maintain backwards compatibility.
The issue we have had for quite a while and only discovered recently due to a significant performance issue is that because Partitioner users 'fields' internally to detect whether or not to exclude _groupId from the returned set of documents, it loses 'projection' options. Thereby returning all fields of a document always, no matter if you exclude or include a specific projection.
Only by wrapping in Partitioner.directOperation we were able to retrieve the documents using the correct projection. Updating fields to projection resolves it for us.
Happy to start a discussion and see if this can be merged.