MongoDB $其中查询和tailable游标 - WAS:日期数学最佳实践

时间:2011-12-05 17:09:13

标签: mongodb

我的问题:给我一份超过X时间的文件清单。

如果我创建的文档是:

db.dates.insert({date: new Date()});

现在我只想在“约会”成为30分钟时找到它:

db.dates.find({ $where: "this.date.getTime() + 30 * 60000 <= new Date()"});

这很有效,但在Mongo文档中明确指出,对于查询的$,存在显着的性能损失。

因此,问题是,还有更好的方法吗?

==========更新1 ==========

我应该补充一点,我希望让这个查询功能“动态地”创建一次查询并使用它来获取加盖的集合上的可用光标...而且我不确定它是不是实际上可能。

我将测试并重新发布。

==========更新2 ==========

因此,看起来我的“延迟”队列必须在代码中处理,无论是使用轮询还是一些“检查,然后睡眠”算法,因为这似乎是mongo的延迟复制正在进行的操作(来自db。 CPP):

if ( replSettings.slavedelay && ( unsigned( time( 0 ) ) < nextOpTime.getSecs() + replSettings.slavedelay ) ) {
    assert( justOne );
    oplogReader.putBack( op );
    _sleepAdviceTime = nextOpTime.getSecs() + replSettings.slavedelay + 1;
    dblock lk;
    if ( n > 0 ) {
        syncedTo = last;
        save();
    }
    log() << "repl:   applied " << n << " operations" << endl;
    log() << "repl:   syncedTo: " << syncedTo.toStringLong() << endl;
    log() << "waiting until: " << _sleepAdviceTime << " to continue" << endl;
    return okResultCode;
}

4 个答案:

答案 0 :(得分:4)

$lte运算符(和其他范围查询)将工作并使用索引,但它无法计算表达式。您必须查询查询时间常量(现在是“30分钟”):

var threshold = new Date(); 
threshold.setMinutes(-30);

// now, query against a constant:
db.dates.find({"date" : {$lte : threshold}});

当然,您可以对任何编程语言的驱动程序执行相同操作,例如 C#

var c = db.Collection.Find(Query.LTE("date", DateTime.UtcNow.AddMinutes(-30));

答案 1 :(得分:0)

Query Mongodb on month, day, year... of a datetime

回答了类似的问题

这个想法是查询所需时间的结束和开始范围。您的查询可以轻松转换为您需要的内容。只需在一侧使用new Date(),在另一侧使用this.date.getTime() + 30 * 60000

答案 2 :(得分:0)

所以,我应该在开始时进行测试!我的原始查询按预期工作,即使在tailable游标的情况下也是如此。

事实证明,Mongo似乎会在每次将记录插入集合时重新评估$ javascript。

我认为这实际上是完全合理的,因为你可以引用“this”这个对象 - 如果每次都没有重新回复javascript那么“this”实际上意味着“第一次”。

效率还有待观察,尤其是当越来越多的可用游标被添加到集合中时。

答案 3 :(得分:0)

所以我之前对我的问题的回答没有通过所有单元测试。除了一个,它通过了所有人:

插入一条记录,当一个tailable游标附加到集合时,该记录最终可用。

测试设置如下:

  1. 在一个单独的线程中,将一个tailable游标附加到一个集合。
  2. 在主线程中,将文档插入到应该在X时间内可用的集合中。
  3. 将主线程休眠X段时间。
  4. 查看带有tailable游标的线程是否包含文档。
  5. 我能够让这个工作的唯一方法是模仿部分延迟的oplog逻辑:

    if (cursor.hasNext()) {
        DBObject obj = cursor.next();
    
        if (config.getQueueDelay() > 0) {
            ObjectId objId = (ObjectId) obj.get("_id");
            long advisedSleep = (objId.getTime() + config.getQueueDelay() * 60000)
            - System.currentTimeMillis();
            if (advisedSleep > 0 ) {
                LOG.debug(
                    "object is not yet old enough, sleeping for: " 
                     + advisedSleep + "ms"
                );
                Thread.sleep(advisedSleep);
            }
        }
        return obj;
    }
    

    在我看来,这是一个好的算法,因为只要有问题的集合依赖于自动创建的ObjectIds,我们就可以100%确定对象的顺序正确,如果我们不能确定这一点,那么我们总是可以通过初始查询添加订单。

    另一方面,集合必须有一个ObjectId(我的代码假定它位于对象的_id字段中,但可以很容易地将其更改为可配置的值)。