我是MongoDB的新手,我正在寻找以下方法:
我收集了许多可用的东西"要使用的。 用户可以"保存"一个"事情"并减少可用物品的数量。 但他有时间在它到期之前使用它。 如果它到期,则必须返回集合,再次递增它。
如果有办法监控"到期日期"那将是理想的选择。在蒙戈。但在我的搜索中,我只发现了一个自动删除整个文档的TTL(生存时间)。
然而,我需要的是"事件"到期...比我想知道是否有可能用Change Streams捕获这个事件。然后我可以使用这个事件增加"事情"试。
有可能吗?或者有更好的方法来做我想做的事情吗?
答案 0 :(得分:3)
我能够使用Change Streams和TTL来模拟cronjob。我发表了一篇文章,详细解释了我的所作所为,并给出了以下信息: https://www.patreon.com/posts/17697287
但是,基本上,每当我需要为文档安排“事件”时,当我创建文档时,我还会并行创建一个事件文档。此事件文档的_id与第一个文档的ID相同。
此外,对于此活动文档,我将设置一个TTL。
当TTL过期时,我将使用Change Streams捕获其“删除”更改。然后我将使用更改的documentKey(因为它与我想要触发的文档的id相同)在第一个集合中查找目标文档,并对文档执行任何我想要的操作。
我正在使用带有Express和Mongoose的Node.js来访问MongoDB。 以下是App.js中添加的相关部分:
const { ReplSet } = require('mongodb-topology-manager');
run().catch(error => console.error(error));
async function run() {
console.log(new Date(), 'start');
const bind_ip = 'localhost';
// Starts a 3-node replica set on ports 31000, 31001, 31002, replica set
// name is "rs0".
const replSet = new ReplSet('mongod', [
{ options: { port: 31000, dbpath: `${__dirname}/data/db/31000`, bind_ip } },
{ options: { port: 31001, dbpath: `${__dirname}/data/db/31001`, bind_ip } },
{ options: { port: 31002, dbpath: `${__dirname}/data/db/31002`, bind_ip } }
], { replSet: 'rs0' });
// Initialize the replica set
await replSet.purge();
await replSet.start();
console.log(new Date(), 'Replica set started...');
// Connect to the replica set
const uri = 'mongodb://localhost:31000,localhost:31001,localhost:31002/' + 'test?replicaSet=rs0';
await mongoose.connect(uri);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
console.log("Connected correctly to server");
});
// To work around "MongoError: cannot open $changeStream for non-existent database: test" for this example
await mongoose.connection.createCollection('test');
// *** we will add our scheduler here *** //
var Item = require('./models/item');
var ItemExpiredEvent = require('./models/scheduledWithin');
let deleteOps = {
$match: {
operationType: "delete"
}
};
ItemExpiredEvent.watch([deleteOps]).
on('change', data => {
// *** treat the event here *** //
console.log(new Date(), data.documentKey);
Item.findById(data.documentKey, function(err, item) {
console.log(item);
});
});
// The TTL set in ItemExpiredEvent will trigger the change stream handler above
console.log(new Date(), 'Inserting item');
Item.create({foo:"foo", bar: "bar"}, function(err, cupom) {
ItemExpiredEvent.create({_id : item._id}, function(err, event) {
if (err) console.log("error: " + err);
console.log('event inserted');
});
});
}
以下是model / ScheduledWithin的代码:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ScheduledWithin = new Schema({
_id: mongoose.Schema.Types.ObjectId,
}, {timestamps: true});
// timestamps: true will automatically create a "createdAt" Date field
ScheduledWithin.index({createdAt: 1}, {expireAfterSeconds: 90});
module.exports = mongoose.model('ScheduledWithin', ScheduledWithin);
答案 1 :(得分:1)
感谢详细的代码。
为了给出一些想法,我有两种选择。
1。 鉴于我们至少得到了 _id ,如果您只需要删除文档中的特定密钥,则可以在创建 _id 时手动指定它,并且至少有此信息。
您需要检查您的 oplog历史记录往后多远,以查看是否可以使用此方法: https://docs.mongodb.com/manual/reference/method/rs.printReplicationInfo/#rs.printReplicationInfo
注意:我使用的是mongodb库,而不是猫鼬
// https://mongodb.github.io/node-mongodb-native/api-bson-generated/timestamp.html
const { Timestamp } = require('mongodb');
const MAX_TIME_SPENT_SINCE_CREATION = 1000 * 60 * 10; // 10mn, depends on your situation
const cursor = db.collection('items')
.watch([{
$match: {
operationType: 'delete'
}
}]);
cursor.on('change', function(change) {
// create another cursor, back in time
const subCursor = db.collection('items')
.watch([{
$match: {
operationType: 'insert'
}
}], {
fullDocument : 'updateLookup',
startAtOperationTime: Timestamp.fromString(change.clusterTime - MAX_TIME_SPENT_SINCE_CREATION)
});
subCursor.on('change', function(creationChange) {
// filter the insert event, until we find the creation event for our document
if (creationChange.documentKey._id === change.documentKey._id) {
console.log('item', JSON.stringify(creationChange.fullDocument, false, 2));
subCursor.close();
}
});
});