0

Short question:

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

Long question:

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

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Nice initiative, i'll give it a try someday. Accepted! But that days I didn't know about Aggregation Framework existence and about super useful $unwind stage.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.