【问题标题】:How to sort collection using complex function in Meteor (MongoDB)如何在 Meteor (MongoDB) 中使用复杂函数对集合进行排序
【发布时间】:2016-01-11 06:55:03
【问题描述】:

我有一个复杂的公式p / a^5 我想对一个集合进行排序,其中pa 是集合Items 的属性。

在 Meteor (MongoDB) 中有什么可能的方法吗?

【问题讨论】:

    标签: mongodb sorting meteor


    【解决方案1】:

    您可以为此使用 aggregation framework。您可以通过使用算术运算符 $multiply 构建聚合管道,使其执行指数函数,调用它 n 的次数,其中 n 是指数。

    例如,以下聚合管道将添加一个额外的字段r,这是操作p / a^5 的结果。

    首先,在 mongo shell 中在 exponents 集合中创建一些示例文档:

    for(x=0; x < 10; x++){ db.exponents.insert({p: 5*x, a: x }); }
    

    生成管道:

    var pipeline = [
        {
            "$match" : {
                "a" : {
                    "$gt" : 0
                }
            }
        },
        {
            "$project" : {
                "p" : 1,
                "a" : 1,
                "r" : {
                    "$divide" : [
                        "$p",
                        {
                            "$multiply" : [
                                "$a",
                                "$a",
                                "$a",
                                "$a",
                                "$a"
                            ]
                        }
                    ]
                }
            }
        },
        {
            "$sort" : {
                "r" : 1
            }
        }
    ];
    

    使用上面的示例文档运行管道:

    db.exponents.aggregate(pipeline);
    

    输出

    /* 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
    }
    

    您可以设置一个通用辅助函数来生成管道代码,以从幂函数的文档表示中提取 $project 运算符和算术运算符。由于 Meteor 尚不支持聚合,请使用 meteorhacks:aggregate 包,它为 Meteor 添加了适当的聚合支持。这个包在Mongo.Collection 实例上公开了.aggregate 方法。

    添加到您的应用中

    meteor add meteorhacks:aggregate
    

    然后只需将.aggregate() 函数与通用函数一起使用来生成管道以用作下面的.aggregate() 参数:

    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);
    

    【讨论】:

    • 如果每个实体的 p 值每小时增加 1 会怎样?
    • 不确定我是否正确地回答了您的其他问题,但我认为如果您打开一个新问题,详细说明您在p 的值发生变化时所做的尝试,您会得到更好的回答。我猜您需要根据计数器或 setTimeout 运行聚合?
    【解决方案2】:

    不,你不能这样做。我建议添加另一个存储p / a^5 值的字段。例如。假设您将此字段称为pResult,那么您可以这样做:

    collection.find(selector, {sort: {pResult: 1}});
    

    【讨论】:

    • 我考虑过这种方法,但是如果a 随时间变化怎么办,比如说,我应该每小时更新a 的值?
    【解决方案3】:

    您可以只fetch() 集合,然后使用常规的Array.prototype.sort_.sortBy 进行排序。但是,这会破坏细粒度的响应性,因此如果您的集合很大或要渲染的元素足够复杂,并且集合会随着时间的推移发生很大变化,那么您可能会遇到性能问题。

    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>
    

    如果您需要细粒度的反应性,您可以查看我的包 collection-view,尽管我不认为它已准备好生产。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-01
      • 2016-02-28
      • 2012-11-30
      • 1970-01-01
      • 2019-08-04
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      相关资源
      最近更新 更多