【问题标题】:Mongoose query returning repeated resultsMongoose 查询返回重复的结果
【发布时间】:2015-12-07 09:28:19
【问题描述】:

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

例子:

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

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

查询

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

架构

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

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

【问题讨论】:

  • 您在执行$near 之后按date_created 对结果进行排序,这不是一个好主意,也可能不是您想要的,因为它“丢弃”了返回的“排序”近操作。还应尽可能避免分页的“跳过”和“限制”方法,并且仅在您“需要”编号分页以“跳转”到并且没有替代方法的情况下使用。我认为其目的至少是找到“最近”的东西,然后“然后”按创建顺序对大约相同距离的任何东西进行排序?
  • 没错!我能做什么?
  • 分页怎么样?你真的需要页码吗?或者你可以简单地要求下一页和下一页等等。
  • 这正是我的意思,获取一个页面,然后只获取“下一个”页面等等,每次需要一个新页面并且您永远不会“跳转到第 5 页”。我问是因为这样可以避免跳过和限制,因此有更好的方法。

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


【解决方案1】:

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

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

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

在这里要做的第一件事是使用可以将距离与其他信息一起“投影”到文档中的命令。 $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" 也被记录,因为我们是按降序排序的,最后的 "sure" 过滤器排除了列表中记录的任何 "_id" 值,因为这些值没有改变。

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

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

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

【讨论】:

    猜你喜欢
    • 2020-11-25
    • 1970-01-01
    • 2021-07-22
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    相关资源
    最近更新 更多