Reputation: 127
I have the complex formula p / a^5
I want to sort a collection with, where p
and a
are attributes of the collection Items
.
Is there any possible way to do it in Meteor (MongoDB)?
Upvotes: 1
Views: 675
Reputation: 103455
You can use the aggregation framework for this. You can structure your aggregation pipeline so that it performs the exponential function by using the arithmetic operator $multiply
, calling it n
number of times where n
is the exponent.
For example, the following aggregation pipeline will add an extra field r
which is the result of the operation p / a^5
.
For a start, in mongo shell create some sample documents in the exponents
collection:
for(x=0; x < 10; x++){ db.exponents.insert({p: 5*x, a: x }); }
Generate the pipeline:
var pipeline = [
{
"$match" : {
"a" : {
"$gt" : 0
}
}
},
{
"$project" : {
"p" : 1,
"a" : 1,
"r" : {
"$divide" : [
"$p",
{
"$multiply" : [
"$a",
"$a",
"$a",
"$a",
"$a"
]
}
]
}
}
},
{
"$sort" : {
"r" : 1
}
}
];
Running the pipeline with the sample documents above :
db.exponents.aggregate(pipeline);
Output:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("561d1ced3d8f561c1548d393"),
"p" : 45,
"a" : 9,
"r" : 0.0007620789513793629
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d392"),
"p" : 40,
"a" : 8,
"r" : 0.001220703125
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d391"),
"p" : 35,
"a" : 7,
"r" : 0.002082465639316951
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d390"),
"p" : 30,
"a" : 6,
"r" : 0.003858024691358025
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d38f"),
"p" : 25,
"a" : 5,
"r" : 0.008
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d38e"),
"p" : 20,
"a" : 4,
"r" : 0.01953125
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d38d"),
"p" : 15,
"a" : 3,
"r" : 0.06172839506172839
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d38c"),
"p" : 10,
"a" : 2,
"r" : 0.3125
},
{
"_id" : ObjectId("561d1ced3d8f561c1548d38b"),
"p" : 5,
"a" : 1,
"r" : 5
}
],
"ok" : 1
}
You can set up a generic helper function for generating the pipeline code to extract $project
operators with the arithmetic operators from the document representation of a power function. As aggregate isn't supported yet in Meteor, use the meteorhacks:aggregate package that adds proper aggregation support for Meteor. This package exposes .aggregate
method on Mongo.Collection
instances.
Add to your app with
meteor add meteorhacks:aggregate
Then simply use .aggregate()
function together with the generic function to generate the pipeline to use as the .aggregate()
argument below:
function power(exponent) {
var multiply = [];
for (var count = 0; count < exponent; count++){
multiply.push("$a");
}
return [
{ $match: { a: { $gt: 0} } },
{
$project: {
p: 1, a: 1, r : { $divide: [ "$p", { $multiply: multiply } ] }
}
},
{
"$sort": { r: 1 }
}
];
}
var pipeline = power(5),
exponents = new Mongo.Collection('exponents'),
result = exponents.aggregate(pipeline);
Upvotes: 0
Reputation: 4091
No, you can't do this. I'd suggest adding another field that stores the value of p / a^5
. E.g. let's say you called this field pResult
, then you could just do:
collection.find(selector, {sort: {pResult: 1}});
Upvotes: 1
Reputation: 4101
You can just fetch()
the collection, then do the sort using the regular Array.prototype.sort
or _.sortBy
. However, this destroys fine-grained reactivity, so if your collection is large or the elements being rendered are complex enough, and the collection changes a lot over time, then you might have performance problems.
Template.whatever.helpers({
items: function () {
return _.sortBy(Items.find().fetch(), function (item) {
return item.p / Math.pow(item.a, 5);
});
}
});
<template name="whatever">
{{#each items}}
<!-- ... -->
{{/each}}
</template>
If you need fine-grained reactivity, you could look at my package collection-view, although I don't consider it production ready.
Upvotes: 0