Reputation: 6018
What I am trying to achieve is, within a given date range, I want to group Users by First time
and then by userId
.
I tried below query to group by Multiple Fields,
ReactiveAggregate(this, Questionaire,
[
{
"$match": {
"time": {$gte: fromDate, $lte: toDate},
"userId": {'$regex' : regex}
}
},
{
$group : {
"_id": {
"userId": "$userId",
"date": { $dateToString: { format: "%Y-%m-%d", date: "$time" } }
},
"total": { "$sum": 1 }
}
}
], { clientCollection: "Questionaire" }
);
But When I execute it on server side, it shows me below error,
Exception from sub Questionaire id kndfrx9EuZ5EejKmE
Error: Meteor does not currently support objects other than ObjectID as ids
Upvotes: 2
Views: 298
Reputation: 151092
The message actually says it all, since the "compound" _id
value that you are generating via the $group
is not actually supported in the clientCollection
output which will be published.
The simple solution of course is to not use the resulting _id
value from $group
as the "final" _id
value in the generated output. So just as the example on the project README demonstrates, simply add a $project
that removes the _id
and renames the present "compound grouping key" as a different property name:
ReactiveAggregate(this, Questionaire,
[
{
"$match": {
"time": {$gte: fromDate, $lte: toDate},
"userId": {'$regex' : regex}
}
},
{
$group : {
"_id": {
"userId": "$userId",
"date": { $dateToString: { format: "%Y-%m-%d", date: "$time" } }
},
"total": { "$sum": 1 }
}
},
// Add the reshaping to the end of the pipeline
{
"$project": {
"_id": 0, // remove the _id, this will be automatically filled
"userDate": "$_id", // the renamed compound key
"total": 1
}
}
], { clientCollection: "Questionaire" }
);
The field order will be different because MongoDB keeps the existing fields ( i.e "total"
in this example ) and then adds any new fields to the document. You can cou[nter that by using different field names in the $group
and $project
stages rather than the 1
inclusive syntax.
Without such a plugin, this sort of reshaping is something that has been regularly done in the past, by again renaming the output _id
and supplying a new _id
value compatible with what meteor client collections expect to be present in this property.
On closer inspection of how the code is implemented, it is probably best to actually supply an _id
value in the results because the plugin actually makes no effort to create an _id
value.
So simply extracting one of the existing document _id
values in the grouping should be sufficient. So I would add a $max
to do this, and then replace the _id
in the $project
:
ReactiveAggregate(this, Questionaire,
[
{
"$match": {
"time": {$gte: fromDate, $lte: toDate},
"userId": {'$regex' : regex}
}
},
{
$group : {
"_id": {
"userId": "$userId",
"date": { $dateToString: { format: "%Y-%m-%d", date: "$time" } }
},
"maxId": { "$max": "$_id" },
"total": { "$sum": 1 }
}
},
// Add the reshaping to the end of the pipeline
{
"$project": {
"_id": "$maxId", // replaced _id
"userDate": "$_id", // the renamed compound key
"total": 1
}
}
], { clientCollection: "Questionaire" }
);
This could be easily patched in the plugin by replacing the lines
if (!sub._ids[doc._id]) {
sub.added(options.clientCollection, doc._id, doc);
} else {
sub.changed(options.clientCollection, doc._id, doc);
}
With using Random.id()
when the document(s) output from the pipeline did not already have an _id
value present:
if (!sub._ids[doc._id]) {
sub.added(options.clientCollection, doc._id || Random.id(), doc);
} else {
sub.changed(options.clientCollection, doc._id || Random.id(), doc);
}
But that might be a note to the author to consider updating the package.
Upvotes: 3