Mongoose查询返回重复的结果

时间:2015-09-10 23:03:56

标签: node.js mongodb mongoose mongodb-query aggregation-framework

查询接收一对坐标,一个最大距离半径,一个"跳过"整数和"限制"整数。该函数应根据给定的位置返回最近和最新的位置。我的代码中没有可见的错误,但是,当我再次调用查询时,它会返回重复的结果。 "跳过"变量根据返回的结果更新。

示例:

1)我使用skip = 0,limit = 10进行查询。我收到10个非重复位置。

2)现在再次调用查询,skip = 10,limit = 10.我收到另外10个位置,第一个查询重复结果。

QUERY

Locations.find({ coordinates :
                 { $near : [ x , y ],
                   $maxDistance: maxDistance }
            })
.sort('date_created')
.skip(skip)
.limit(limit)
.exec(function(err, locations) {
    console.log("[+]Found Locations");
    callback(locations);
});

SCHEMA

var locationSchema = new Schema({
        date_created: { type: Date },
        coordinates: [],
        text: { type: String }
});

我试过到处寻找解决方案。我唯一的选择是Mongo的版本?我使用mongoose 4.x.x而mongodb就像2.5.6。我相信。有任何想法吗?

1 个答案:

答案 0 :(得分:3)

在您想要的结果类型中,有几件事需要考虑,首先考虑的是您在“date_created”中有一个“辅助”排序标准来处理。

基本问题是MongoDB中的$near运算符和类似运算符目前没有“项目”任何字段来指示与查询位置的“距离”,而只是“默认排序”数据。因此,为了进行“次要”排序,需要存在具有“距离”的字段。因此,还有其他选择。

第二种情况是“跳过”和“限制”样式分页在大型数据集上表现糟糕,应该尽可能避免。因此,最好根据发生的“范围”选择数据,而不是“跳过”之前显示的所有结果。

这里要做的第一件事是使用一个命令,可以将距离投影到文档中以及其他信息。 $geoNear的聚合命令对此有好处,特别是因为我们想要进行其他排序:

var seenIds = [],
    lastDistance = null,
    lastDate = null;

Locations.aggregate(
    [
        { "$geoNear": {
            "near": [x,y],
            "maxDistance": maxDistance
            "distanceField": "dist",
            "limit": 10
        }},
        { "$sort": { "dist": 1, "date_created": -1 }
    ],
    function(err,results) {
        results.forEach(function(result) {

            if ( ( result.dist != lastDistance ) || ( result.date_created != lastDate ) ) {
                seenIds = [];
                lastDistance = result.dist;
                lastDate = result.date_created;
           }
           seenIds.push(result._id);
       });
       // save those variables to session or other persistence
       // do something with results
    }
)

这是您获取前10个结果的第一次迭代。注意循环中的逻辑,其中结果中的每个文档都要检查“date_created”或“dist”字段中的变化出现在文档中并且发生这种情况时,“seenIds”数组将擦除所有当前条目。一般操作是所有变量都经过测试,并且可能在每次迭代时更新,如果没有变化,则将项目添加到“seenIds”列表中。

正在处理的所有这三个变量需要存储在等待下一个请求的某个地方。对于Web应用程序,会话存储是理想的,但不同的方法各不相同。您只需要在我们开始下一个请求时调用这些值,就像在下一次和后续迭代中我们稍微更改一下查询一样:

Locations.aggregate(
    [
        { "$geoNear": {
            "near": [x,y],
            "maxDistance": maxDistance,
            "minDistance": lastDistance,
            "distanceField": "dist",
            "limit": 10,
            "query": {
                "_id": { "$nin": seenIds },
                "date_created": { "$lt": lastDate }
            }
        }},
        { "$sort": { "dist": 1, "date_created": -1 }
    ],
    function(err,results) {
        results.forEach(function(result) {
            if ( ( result.dist != lastDistance ) || ( result.date_created != lastDate ) ) {
                seenIds = [];
                lastDistance = result.dist;
                lastDate = result.date_created;
           }
           seenIds.push(result._id);
       });
       // save those variables to session or other persistence
       // do something with results
    }
)

因此,输入“minDistance”参数是为了排除已经看到的任何“更近”的结果,并且在查询中添加了额外的检查,其中“date_created”需要“小于”因为我们按排序的降序排列了“lastDistance”,最后的“确定”过滤器排除了列表中记录的任何“_id”值,因为值没有改变。

现在使用地理空间数据,“seenIds”列表不太可能增长,因为通常你不会在同一距离找到所有东西,但这是一个分页排序的数据列表的一般过程,所以它值得理解的概念。

因此,如果您希望能够使用辅助字段对地理空间数据进行排序并考虑“近”距离,那么这是一般方法,通过将距离值投影到文档结果中以及存储在任何不会使它们成为唯一的更改之前最后看到的值。

一般概念是“推进最小距离”,使每一页结果逐渐“远离”查询中使用的源点。