Is there a new way to use the mongodb aggregate function that doesn’t require the meteorhacks:aggregate or is that still the only way to go.
I’d probably recommend using rawCollection()
and the official npm MongoDB library aggregate method. So in a Meteor method, for example:
Meteor.methods({
async aggregateMyStuff() {
const pipeline = [ ... ];
const options = { ... };
return MyCollection.rawCollection().aggregate(pipeline, options);
},
});
Slightly off topic but would you say it’s preferable to use rawCollection() everywhere (find, findOne, count, etc) to make use of promises?
Hmm. If you want to make use of Meteor’s pub/sub, you probably won’t be able to use it everywhere. The cursor Meteor uses is not the same as the npm library’s cursor.
Having said that, if you’re using methods, or you’re happy to drop back to minimongo for publications, it makes some sense. Especially if you want to use some of the many methods available with the official library.
Of course, you can’t use rawCollection()
on the client.
I ended up doing something that you might be interested in:
class SuperCollection extends Meteor.Collection {
async aggregate(pipeline = []) {
return this.rawCollection().aggregate(pipeline, { allowDiskUse: true }).toArray();
}
async pCount(query = {}, options = {}) {
return this.rawCollection().count(query, options);
}
async pFindOne(query = {}, options = {}) {
if (typeof query === 'string') query = { _id: query };
return this.rawCollection().findOne(query, options);
}
async distinct(key = '', query = {}, options = {}) {
return this.rawCollection().distinct(key, query, options);
}
async ensureIndex(fieldOrSpec, options = {}) {
return this.rawCollection().ensureIndex(fieldOrSpec, options);
}
};
Sorry, can you explain your code…what will be the use case?
It allows me to make a collection with const Collection = new SuperCollection('collectionName');
and then I have the ability to do Collection.aggregate
instead of Collection.rawCollection().aggregate
throughout the (server-side) codebase. It also adds distinct, ensureIndex, an async count, and an async findOne.
I’ve been using this one to publish aggregations in meteor: lamoglia:publish-aggregation. It also uses rawCollection to perform the aggregations.
Hi, I base on Vue
.
I don’t use async
, but it still work fine
// Vue Method
methods:{
rawCollectionTest
.callPromise({})
.then(result => {
if (result) {
this.rawData = result
}
})
.catch(error => {
console.log(error)
})
}
---
// Meteor Method
export const rawCollectionTest = new ValidatedMethod({
name: 'app.rawCollectionTest',
mixins: [CallPromiseMixin],
validate: new SimpleSchema({
selector: {
type: Object,
optional: true,
blackbox: true,
},
}).validator(),
run({ selector }) {
if (Meteor.isServer) {
selector = selector || {}
const data = Branches.rawCollection()
.aggregate([
{
$match: selector,
},
{
$project: {
_id: 1,
name: 1,
address: 1,
},
},
])
.toArray()
return data
}
},
})
And then I tried use async/await
, It work fine too.
Please advise???
But I can use forEach
data in server method
const data = Branches.rawCollection()
.aggregate([
{
$match: selector,
},
{
$project: {
_id: 1,
name: 1,
address: 1,
},
},
])
.toArray()
data.forEach(.......) // don't work
depending on the mongodb version, aggregate will return a cursor. If you call toArray()
on it, you get a Promise. You have to use async/await in this case
thanks for your explain.
@bmanturner, I would like to use Meteor.user(), Meteor.userId()
in Extends Collection Class
.
But don’t work, Pl help me
class SuperCollection extends Meteor.Collection {
async aggregate(pipeline = []) {
// I would like to get current user: Meteor.user(), Meteor.userId()
// but not work
...
return this.rawCollection().aggregate(pipeline, { allowDiskUse: true }).toArray();
}
- what’s the error?
- you could also pass it as param to your aggregate method
Get error, when I fixture data
on startup
Error: Meteor.userId can only be invoked in method calls or publications
the error does exactly describe what’s going on. You can only call Meteor.userId() from a method call or in a publication.
This makes perfect sense: you can only know the current user in these two context.
In server startup, its pretty clear, that you can’t do that as there is no user.
Here was some stuff because I was stupid!
The current MongoDB library returns a Promise to an aggregation cursor for this. Are you sure you’re looking for the right thing?
On the assumption that’s a Meteor Method, maybe something like:
async testAggregate() {
const buyer = Meteor.user();
let $match = { buyer_id: buyer._id };
$match.datetime = { $gte: moment().subtract(1, 'month').toDate() };
try {
const orderedItems = await OrdersHead.rawCollection().aggregate([
{ $match }
]).toArray();
console.log(orderedItems);
return orderedItems;
} catch (err) {
throw new Meteor.Error('E1234', err.message);
}
}
Boom! Thanks so much. Need to dig into the promises stuff more.