Mongo DB - using map reduction or aggregation

I have a series of documents in the MongoDB collection that looks like this:

{ 'time' : '2016-03-28 12:12:00', 'value' : 90 }, { 'time' : '2016-03-28 12:13:00', 'value' : 82 }, { 'time' : '2016-03-28 12:14:00', 'value' : 75 }, { 'time' : '2016-03-28 12:15:00', 'value' : 72 }, { 'time' : '2016-03-28 12:16:00', 'value' : 81 }, { 'time' : '2016-03-28 12:17:00', 'value' : 90 }, etc.... 

The tasks are: - if the basket retention value of 80 was all the time, where the entering value entering below 80 and exiting above 80

 { 'time' : '2016-03-28 12:14:00', 'result' : 'enter' }, { 'time' : '2016-03-28 12:16:00', 'result' : 'exit' }, 

Is it possible for a request to reduce a map or aggregate to produce such a result? I tried to scroll through the sorted results, but it is very expensive and expensive processing - I need to do a number of such checks.

PS. I use Django and mongoengine to make a call.

+6
source share
2 answers

I'm not sure that this is only possible with MongoDB aggregation, because, as mentioned in @BlakesSeven, there is no link between subsequent documents. And you need this connection to check if the new value has changed below or above the desired threshold, compared with the fact that the value was directly in front of it in the previous document.

Here is a naive clean-python (since it is tagged with Django and MongoEngine) that iterates over the sorted results, maintaining the threshold track variable and catching when it falls below or above 80 ( col is a link to your collection):

 THRESHOLD = 80 cursor = col.find().sort("time") first_value = next(cursor) more_than = first_value["value"] >= THRESHOLD for document in cursor: if document["value"] < THRESHOLD: if more_than: print({"time": document["time"], "result": "enter"}) more_than = False else: if not more_than: print({"time": document["time"], "result": "exit"}) more_than = True 

For the provided sample data, it prints:

 {'time': '2016-03-28 12:14:00', 'result': 'enter'} {'time': '2016-03-28 12:16:00', 'result': 'exit'} 

As an additional note and an alternative solution. If you have control over how these records were inserted when you paste the document into this collection, you can check what the last value , compare it with the threshold and set result as a separate field. Then the request to enter and exit threshold points will become as simple as:

 col.find({"result" : {$exists : true}}) 

You can call this approach "predefined thresholds." This probably only makes sense in terms of search / search performance, and if you are going to do it often.

+2
source

You can easily convert documents using the aggregation and cursor iteration structures.

Example:

 db.collection.aggregate([ {$project: { value:1, "threshold":{$let: { vars: {threshold: 80 }, in: "$$threshold" }} } }, {$match:{value:{$ne: "$threshold"}}}, {$group: { _id:"$null", low:{ $max:{ $cond:[{$lt:["$value","$threshold"]},"$value",-1] } }, high:{ $min:{ // 10000000000 is a superficial value. // need something greater than values in documents $cond:[{$gt:["$value","$threshold"]},"$value",10000000000] } }, threshold:{$first:"$threshold"} } } ]) 

The aggregation structure will return a document with two values.

 { "_id" : null, "low" : NumberInt(75), "high" : NumberInt(81), "threshold" : NumberInt(80) } 

We can easily find documents that meet your return criteria. for example, in NodeJS we can easily do this. assuming the result variable contains the result of the aggregation request.

 result.forEach(function(r){ var documents = []; db.collection.find({$or:[{"value": r.low},{"value": r.high}]}).forEach(function(doc){ var _doc = {}; _doc.time = doc.time; _doc.result = doc.value < r.threshold ? "enter" : "exit"; documents.push(_doc); }); printjson(documents); }); 

As you remember, if your input documents (sample)

 { 'time' : '2016-03-28 12:12:00', 'value' : 90 }, { 'time' : '2016-03-28 12:13:00', 'value' : 82 }, { 'time' : '2016-03-28 12:14:00', 'value' : 75 }, { 'time' : '2016-03-28 12:15:00', 'value' : 72 }, { 'time' : '2016-03-28 12:16:00', 'value' : 81 }, { 'time' : '2016-03-28 12:17:00', 'value' : 90 }, etc.... 

The request above in the solution will emit:

 { "time" : "2016-03-28 12:14:00", "result" : "enter" }, { "time" : "2016-03-28 12:16:00", "result" : "exit" } 
+1
source

All Articles