Как прослушать изменения в коллекции MongoDB?

Я создаю своего рода фоновую систему очереди заданий с MongoDB в качестве хранилища данных. Как я могу" слушать " вставки в коллекцию MongoDB перед тем, как создавать рабочих для обработки задания? Нужно ли опрашивать каждые несколько секунд, чтобы узнать, есть ли какие-либо изменения с прошлого раза, или есть способ, которым мой скрипт может ждать вставки? Это PHP-проект, над которым я работаю, но не стесняйтесь отвечать на Ruby или языке agnostic.

9 ответов


что вы думаете звучит как триггеры. MongoDB не имеет никакой поддержки триггеров, однако некоторые люди "свернули свои собственные", используя некоторые трюки. Ключ здесь-oplog.

при запуске MongoDB в наборе реплик все действия MongoDB регистрируются в журнале операций (известном как oplog). Oplog-это в основном просто список изменений, внесенных в данные. Реплики устанавливают функцию, прослушивая изменения в этом oplog, а затем применяя изменения локально.

это звучит знакомо?

Я не могу подробно весь процесс здесь, это несколько страниц документации, но необходимые инструменты доступны.

сначала некоторые записи на oplog - краткое описание - планировка local коллекция (который содержит oplog)

вы также захотите использовать tailable курсоры. Эти способ слушать изменения, а не опрашивать их. Обратите внимание, что репликация использует tailable курсоры, так что это поддерживается.


MongoDB имеет то, что называется capped collections и tailable cursors это позволяет MongoDB передавать данные слушателям.

A capped collection по существу представляет собой коллекцию фиксированного размера и допускает только вставки. Вот как это будет выглядеть, чтобы создать одну:

db.createCollection("messages", { capped: true, size: 100000000 })

в MongoDB Tailable курсоры (оригинальный пост Джонатана Х. Заработной платы)

Рубин

coll = db.collection('my_collection')
cursor = Mongo::Cursor.new(coll, :tailable => true)
loop do
  if doc = cursor.next_document
    puts doc
  else
    sleep 1
  end
end

PHP

$mongo = new Mongo();
$db = $mongo->selectDB('my_db')
$coll = $db->selectCollection('my_collection');
$cursor = $coll->find()->tailable(true);
while (true) {
    if ($cursor->hasNext()) {
        $doc = $cursor->getNext();
        print_r($doc);
    } else {
        sleep(1);
    }
}

Python (by Роберт Стюарт)

from pymongo import Connection
import time

db = Connection().my_db
coll = db.my_collection
cursor = coll.find(tailable=True)
while cursor.alive:
    try:
        doc = cursor.next()
        print doc
    except StopIteration:
        time.sleep(1)

Perl (by Макс)

use 5.010;

use strict;
use warnings;
use MongoDB;

my $db = MongoDB::Connection->new;
my $coll = $db->my_db->my_collection;
my $cursor = $coll->find->tailable(1);
for (;;)
{
    if (defined(my $doc = $cursor->next))
    {
        say $doc;
    }
    else
    {
        sleep 1;
    }
}

Дополнительные Материалы:

Ruby / Node.учебник js, который проведет вас через создание приложения, которое слушает вставки в MongoDB с крышкой коллекция.

статья про tailable курсоры более подробно.

PHP, Руби, питон, Перл и примеры использования tailable курсоры.


С MongoDB 3.6 появится новый API уведомлений под названием Change Streams, который вы можете использовать для этого. См.это сообщение в блоге для примера. Пример из него:

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'])

проверьте это: изменить потоки

10 января 2018 -релиз 3.6

*EDIT: я написал статью о том, как это сделатьhttps://medium.com/riow/mongodb-data-collection-change-85b63d96ff76

https://docs.mongodb.com/v3.6/changeStreams/


это новое в mongodb 3.6 https://docs.mongodb.com/manual/release-notes/3.6/ 2018/01/10

$ mongod --version
db version v3.6.2

чтобы использовать changeStreams база данных должна быть Репликация Набор

подробнее о наборах репликации : https://docs.mongodb.com/manual/replication/

ваша база данных будет "Автономной" по умолчанию.

как преобразовать автономный набор в реплику: https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set/


следующее пример это практическое приложение для того, как вы могли бы использовать это.
* специально для узла.

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

полезное links:
https://docs.mongodb.com/manual/tutorial/convert-standalone-to-replica-set
https://docs.mongodb.com/manual/tutorial/change-streams-example

https://docs.mongodb.com/v3.6/tutorial/change-streams-example
http://plusnconsulting.com/post/MongoDB-Change-Streams


MongoDB версия 3.6 теперь включает потоки изменений, которые по существу являются API поверх OpLog, позволяющего использовать триггеры / уведомления.

вот ссылка на пример Java : http://mongodb.github.io/mongo-java-driver/3.6/driver/tutorials/change-streams/

пример 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));
          });
      });

кроме того, вы можете использовать стандартный метод Mongo FindAndUpdate и в обратном вызове запустить событие EventEmitter (в узле) при выполнении обратного вызова.

любые другие части приложения или архитектуры, прослушивающие это событие, будут уведомлены об обновлении, а также о любых соответствующих данных, отправленных туда. Это действительно простой способ получить уведомления от Mongo.


существует рабочий пример java, который можно найти здесь.

 MongoClient mongoClient = new MongoClient();
    DBCollection coll = mongoClient.getDatabase("local").getCollection("oplog.rs");

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

    System.out.println("== open cursor ==");

    Runnable task = () -> {
        System.out.println("\tWaiting for events");
        while (cur.hasNext()) {
            DBObject obj = cur.next();
            System.out.println( obj );

        }
    };
    new Thread(task).start();

ключ ПАРАМЕТРЫ ЗАПРОСА приведенный здесь.

Также вы можете изменить запрос поиска, Если вам не нужно загружать все данные каждый раз.

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

на самом деле, вместо просмотра вывода, почему вы не получаете уведомление, когда что-то новое вставляется с помощью middle-ware, который был предоставлен Мангуст схемы

вы можете поймать событие вставки нового документа и сделать что-то после этой вставки


многие из этих ответов дадут вам только новые записи, а не обновления и / или крайне неэффективны

единственный надежный и эффективный способ сделать это-создать хвостовой курсор на локальном db: oplog.rs collection, чтобы получить все изменения в MongoDB и сделать с ним то, что вы будете. (MongoDB даже делает это внутренне более или менее для поддержки репликации!)

объяснение того, что oplog содержит: https://www.compose.com/articles/the-mongodb-oplog-and-node-js/

пример узла.библиотека js, которая предоставляет API вокруг того, что доступно для работы с oplog: https://github.com/cayasso/mongo-oplog