Как прослушать изменения в коллекции 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;
}
}
Дополнительные Материалы:
статья про 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