如何侦听MongoDB集合的更改?

如何侦听MongoDB集合的更改?,mongodb,Mongodb,我正在创建一种后台作业队列系统,使用MongoDB作为数据存储。在生成处理作业的工作人员之前,如何“侦听”MongoDB集合的插入?我是否需要每隔几秒钟进行一次轮询,以查看是否与上次有任何更改,或者我的脚本是否有办法等待插入发生?这是一个我正在进行的PHP项目,但请随意用Ruby或语言无关的语言回答。您的想法听起来很像触发器。MongoDB对触发器没有任何支持,但是有些人使用一些技巧“自己滚”。这里的关键是oplog 在副本集中运行MongoDB时,所有MongoDB操作都会记录到操作日志(称为

我正在创建一种后台作业队列系统,使用MongoDB作为数据存储。在生成处理作业的工作人员之前,如何“侦听”MongoDB集合的插入?我是否需要每隔几秒钟进行一次轮询,以查看是否与上次有任何更改,或者我的脚本是否有办法等待插入发生?这是一个我正在进行的PHP项目,但请随意用Ruby或语言无关的语言回答。

您的想法听起来很像触发器。MongoDB对触发器没有任何支持,但是有些人使用一些技巧“自己滚”。这里的关键是oplog

在副本集中运行MongoDB时,所有MongoDB操作都会记录到操作日志(称为oplog)。oplog基本上只是对数据所做修改的运行列表。副本通过侦听此oplog上的更改,然后在本地应用更改来设置功能

这听起来熟悉吗?

我不能在这里详细说明整个过程,这是几页文档,但您需要的工具是可用的

首先是关于oplog的一些评论 - -(其中包含oplog)


你也会想要杠杆作用。这些将为您提供一种侦听更改而不是轮询更改的方法。请注意,复制使用可定制的游标,因此这是一项受支持的功能。

MongoDB具有所谓的功能,允许MongoDB将数据推送到侦听器

capped集合
本质上是一个固定大小的集合,只允许插入。下面是创建一个的外观:

db.createCollection("messages", { capped: true, size: 100000000 })
MongoDB可裁剪游标() 红宝石

coll=db.collection('my_collection'))
cursor=Mongo::cursor.new(coll,:tailable=>true)
环道
如果doc=cursor.next\u文档
放博士
其他的
睡眠1
结束
结束
PHP

$mongo=newmongo();
$db=$mongo->selectDB('my_db'))
$coll=$db->selectCollection('my_collection');
$cursor=$coll->find()->tailable(true);
while(true){
如果($cursor->hasNext()){
$doc=$cursor->getNext();
打印(doc);
}否则{
睡眠(1);
}
}
Python(由

从pymongo导入连接
导入时间
db=连接().my_db
coll=db.my_集合
cursor=coll.find(tailable=True)
cursor.alive时:
尝试:
doc=cursor.next()
打印单据
除停止迭代外:
时间。睡眠(1)
Perl(by)

使用5.010;
严格使用;
使用警告;
使用MongoDB;
my$db=MongoDB::Connection->new;
my$coll=$db->my_db->my_collection;
我的$cursor=$coll->find->tailable(1);
对于(;;)
{
如果(已定义(我的$doc=$cursor->next))
{
比如$doc;
}
其他的
{
睡眠1;
}
}
额外资源:


或者,您可以使用标准的Mongo FindAndUpdate方法,在回调中,在运行回调时触发EventEmitter事件(在节点中)


监听此事件的应用程序或体系结构的任何其他部分都将收到更新通知,并发送相关数据。这是从Mongo获得通知的一种非常简单的方法。

可以找到一个工作的java示例

这里给出了答案

如果不需要每次都加载所有数据,也可以更改find查询

BasicDBObject query= new BasicDBObject();
query.put("ts", new BasicDBObject("$gt", new BsonTimestamp(1471952088, 1))); //timestamp is within some range
query.put("op", "i"); //Only insert operation

DBCursor cur = coll.find(query).sort(BasicDBObjectBuilder.start("$natural", 1).get())
.addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);

事实上,与其观看输出,为什么不注意使用由提供的中间件插入的新内容


您可以捕获插入新文档的事件,并在插入后执行某些操作

这些答案中的许多只会给您提供新记录,而不会更新和/或极为低效

唯一可靠、高效的方法是在本地db:oplog.rs集合上创建一个可裁剪的游标,以获取对MongoDB的所有更改,并按照您的意愿使用它。(MongoDB甚至在内部或多或少地这样做以支持复制!)

解释oplog包含的内容:

Node.js库的示例,该库提供了一个API,该API围绕可使用oplog执行的操作:

自MongoDB 3.6以来,将有一个名为Change Streams的新通知API,您可以使用该API进行此操作。请参阅。示例:

cursor = client.my_db.my_collection.changes([
    {'$match': {
        'operationType': {'$in': ['insert', 'replace']}
    }},
    {'$match': {
        'newDocument.n': {'$gte': 1}
    }}
])

# Loops forever.
for change in cursor:
    print(change['newDocument'])

MongoDB版本3.6现在包括变更流,它本质上是OpLog之上的API,允许触发/通知类用例

下面是指向Java示例的链接:

NodeJS示例可能类似于:

 var MongoClient = require('mongodb').MongoClient;
    MongoClient.connect("mongodb://localhost:22000/MyStore?readConcern=majority")
     .then(function(client){
       let db = client.db('MyStore')

       let change_streams = db.collection('products').watch()
          change_streams.on('change', function(change){
            console.log(JSON.stringify(change));
          });
      });
看看这个:改变流 2018年1月10日-第3.6版 *编辑:我写了一篇关于如何做到这一点的文章


它在mongodb 3.6中是新的 2018/01/10

$ mongod --version
db version v3.6.2

要使用变更流,数据库必须是复制集

有关复制集的详细信息:

默认情况下,您的数据库将是一个“独立数据库”

如何将单机版转换为副本集:


以下示例是如何使用此功能的实际应用程序。
*专门针对节点


有用的链接:



有一组非常棒的服务,叫做。查看。注意这是一个基于云的付费服务(AWS)。在您的例子中,在插入时,您可以调用一个用javascript编写的自定义函数


在3.6之后,允许使用以下数据库触发器类型:

  • 事件驱动触发器-用于自动更新相关文档、通知下游服务、传播数据以支持混合工作负载、数据完整性和审核
  • 定时触发器-用于定时数据检索、传播、存档和分析工作负载
登录您的Atlas帐户并选择
Triggers
$ mongod --version
db version v3.6.2
/* file.js */
'use strict'


module.exports = function (
    app,
    io,
    User // Collection Name
) {
    // SET WATCH ON COLLECTION 
    const changeStream = User.watch();  

    // Socket Connection  
    io.on('connection', function (socket) {
        console.log('Connection!');

        // USERS - Change
        changeStream.on('change', function(change) {
            console.log('COLLECTION CHANGED');

            User.find({}, (err, data) => {
                if (err) throw err;

                if (data) {
                    // RESEND ALL USERS
                    socket.emit('users', data);
                }
            });
        });
    });
};
/* END - file.js */