Reputation: 546
Is there any solution from the box if log
is an array of objects to do something like this (in mongo shell or nodejs or meteor minimongo, anything)?
var log = db.getCollection('myCollection').findOne({_id: 'someId'}).log
var collection = useAsCollection(log);
collection.find(anyValidMongoSelector);
I have the document like this one:
{
"_id": "someId",
"log": [{
"operation": "trade",
"date": ISODate("2010-11-12T17:59:04.332+03:00")
}, {
"operation": "sell",
"date": ISODate("2011-11-14T08:53:22.937+03:00")
}, {
"operation": "buy",
"date": ISODate("2014-11-14T12:44:37.202+03:00")
}, {
"operation": "sell",
"date": ISODate("2012-11-15T12:32:40.910+03:00")
}, {
"operation": "buy",
"date": ISODate("2013-11-17T17:43:15.705+03:00")
}, {
"operation": "trade",
"date": ISODate("2018-11-18T08:51:42.518+03:00")
}]
}
I often have to know some specific information associated with log
array. For example, the earliest operation.
If the array itself was a collection, I'd just do a search:
db.getCollection('log').find().sort({date: 1})
But such a search can not be made in my case. I know, there is $elemMatch projection, but it returns only the first element matching the condition and have some other limitations.
My solution is to write down in myscript.js
some code:
function getEarlier() {
var h1 = db.getCollection('myCollection').findOne({_id: 'someId'}).log,
earliestDate = new Date();
for (var i in h1) {
if (h1[i].date < earliestDate) {
earliestDate = h1[i].date;}}
print('The earliest document: ' + earliestDate);}
getEarlier();
and then execute
mongo myDB myscript.js
The actual doc structure is more complex as well as standing in front of me problems, so i have a lot of such functions iterating this doc.
And the question one more time. Is there any solution from the box to do something like this (in mongo shell or nodejs or meteor minimongo, anything)?
var log = db.getCollection('myCollection').findOne({_id: 'someId'}).log
var collection = useAsCollection(log);
collection.find().sort({date: 1})
Upvotes: 0
Views: 64
Reputation: 906
I have a project micromongo which allows to execute some limited subset of Mongodb queries over arrays of JSON objects.
Cursors are not currently supported, but for your case you may do following:
var mm = require('micromongo');
function ISODate(s) { return new Date(s); }
var log = [{
"operation": "trade",
"date": ISODate("2010-11-12T17:59:04.332+03:00")
}, {
"operation": "sell",
"date": ISODate("2011-11-14T08:53:22.937+03:00")
// ......
}];
var sorted = mm.aggregate(log, [
{ $sort: { date: 1 } }
]);
console.log(sorted);
Or even:
var sorted = mm.aggregate(log, [
{ $sort: { date: 1 } },
{ $limit: 1 }
]);
...if you need to get just one latest value;
If using with Mongodb native driver, the solution to your question may look as following:
var mm = require('micromongo');
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/test';
MongoClient.connect(url, function(err, db) {
console.log("Connected succesfully to server");
db.collection('logger').findOne({}, function(err, doc) {
var log = doc.log;
var sorted = mm.aggregate(log, [
{ $sort: { date: -1 } }
]);
console.log(sorted);
db.close()
});
});
Please, be aware that it uses unsorted unindexed data, but I assume that log
array is relatively small, otherwise you'd put it into separate collection.
Upvotes: 1
Reputation: 11197
You update it and slice on the write, not the read.
Logs.update({
_id: 'someid'
}, {
$push: {
'log': {
$each: [{
operation: 'sell',
timestamp: new Date
}],
$slice: -100,
$sort: { timestamp: 1 }
}
}
});
Essentially, this maintains your array in order rather than querying on it and keeping it in order.
I have done something similar in my own project where I update a log asynchronously. Maybe that can help.
Upvotes: 0