MongoDB Finding nested object value - mongodb

I'm trying to find documents within my collection that have a numeric value greater than x amount. The documentation explains how to do this for top level values however I'm struggling to retrieve the correct data for values that are within child objects.
Sample JSON
{
"_id" : ObjectId("5c32646c9f3315c3e8300673"),
"key" : "20190107",
"__v" : 0,
"chart" : [
{
"_id" : ObjectId("5c3372e5c35e924984f28e03"),
"volume" : "0",
"close" : "47.24",
"time" : "09:30 AM"
},
{
"_id" : ObjectId("5c3372e5c35e924984f28d34"),
"volume" : "50",
"close" : "44.24",
"time" : "09:50 AM"
}
]
}
I want to retrieve volumes greater than 10. I've tried
db.symbols.find({"chart.volume": { $gt: 10 } } )
db.symbols.find({"volume": { $gt: 10 } } )
Any help appreciated.

Your sample JSON has string values for the chart.volume field. If it was numeric, then your first solution:
db.symbols.find({"chart.volume": { $gt: 10 } } )
would work fine. The docs do explain how to do this.

Related

mongodb findOneAndUpdate only if a certain condition is met

Following is my mongo db entries.
my-mongo-set:PRIMARY> db.stat_collection.find({name : /s/})
{ "_id" : ObjectId("5aabf231a167b3808302b138"), "name" : "shankarmr", "email" : "abc#xyz", "rating" : 9901 }
{ "_id" : ObjectId("5aabf23da167b3808302b139"), "name" : "shankar", "email" : "abc1#xyz1", "rating" : 10011 }
{ "_id" : ObjectId("5aabf2b5a167b3808302b13a"), "name" : "shankar1", "email" : "abc2#xyz2", "rating" : 10 }
{ "_id" : ObjectId("5aabf2c2a167b3808302b13b"), "name" : "shankar2", "email" : "abc3#xyz3", "rating" : 100 }
Now i want to find an entry based on name but update a field only if a certain condition holds good.
I tried the following statement, but it gives me error at the second reference to $rating.
db.stat_collection.findOneAndUpdate({name: "shankar"}, {$set : {rating : {$cond : [ {$lt : [ "$rating", 100]}, 100, $rating]}}, $setOnInsert: fullObject}, {upsert : true} )
So in my case, it shouldnot update rating for the 2nd document as the rating is not less than 100. But for the third document, rating should be updated to 100.
How do i get it work?
$max is the operator you're looking for, try:
db.stat_collection.findOneAndUpdate( { name: "shankar1"}, { $max: { rating: 100 } }, { returnNewDocument: true } )
You'll either get old value (if is greater than 100) or modify a document and set 100
According to the documentation:
The $max operator updates the value of the field to a specified value if the specified value is greater than the current value of the field. The $max operator can compare values of different types, using the BSON comparison order.
You should put all conditions in the query part of the update:
db.stat_collections.findOneAndUpdate(
{ name: "Shankar", rating: { $lt: 100 } },
$set : { rating: 100 },
);
"If the name is Shankar and rating is less than 100, then set the rating to 100." is the above.

How to get all subfields of mongodb in a query when one field is root field of other field requested?

For this specific case, everything works fine, except when
for the fields field1,field2 requested, and field1 is a part of field2.
Example :
> db.mycoll.findOne()
{
"_id" : 1,
"data" : {
"amounts" : {
"dollar" : 20,
"euro" : 18
},
"item" : "toy",
"sale" : false
}
}
// works well
> db.mycoll.findOne({"_id":1},{ "data.amounts.dollar":1 })
{ "_id" : 1, "data" : { "amounts" : { "dollar" : 20 } } }
// here "data" is root of "data.amounts.dollar" and "data.amounts.euro"
// takes preference, how to query for "data", so
// that all subfields of data are
// returned
> db.mycoll.findOne({"_id":1},{ "data":1 , "data.amounts.dollar":1 })
{ "_id" : 1, "data" : { "amounts" : { "dollar" : 20 } } }
Expected output :
{
"_id" : 1,
"data" : {
"amounts" : {
"dollar" : 20,
"euro" : 18
},
"item" : "toy",
"sale" : false
}
}
Yes, it is possible to format the subfields on the program side, and send the root field to mongodb query, but my question is if this is feasible on the querying side without Javascript .
This is unusual behavior, a bug to be precise.
From credible/official sources :
Jira Open Bug
Jira Bug Duplicate
Seems that the bug is still open.
Please let me know if you need any further analysis.
db.mycoll.findOne({"_id":1},{"data.amounts.dollar":1,"data":1 })
This gives as expected result
db.getCollection(coll_name).find({_id:1},{data:1});
This will give output
{
"_id" : 1,
"data" : {
"amounts" : {
"dollar" : 20,
"euro" : 18
},
"item" : "toy",
"sale" : false
}
}
Once you use a projection (the second json document in the 'find()', only those fields specified in the projection will be returned by the server (The exception is '_id' which will be returned unless explicitly turned off by _id:0).
{ "data":1 , "data.amounts.dollar":1 }
By selecting data.amounts.dollar inside the sub-document, you have essentially turned off the other members of the data.amounts document.
You can turn them on like you did with dollar, but I think you want them all projected regardless of knowing or not the field names.
I could not find in the documentation anything about order of fields in the projection field.
From the Mongo Documentation here
https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/#projection-document

Positional operator and field limitation

In a find query projection, fields I specify after the positional operator are ignored and the whole document is always returned.
'myArray.$.myField' : 1 behave exactly like 'myArray.$' : 1
the positional operator selects the right document. But this document is quite big. I would like to project only 1 field from it.
Exemple:
db.getCollection('match').find({"participantsData.id" : 0001}, { 'participantsData.$.id': 1, })
here the response I have
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001,
"plenty" : "of",
"other" : "fields",
"and" : "subdocuments..."
}
]
}
This is what I want
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 0001
}
]
}
Is it possible with mongo?
Yes it can be done in mongo
Please try the below query
db.getCollection('match').find(
{"participantsData.id" : 0001},
{"participantsData.id": 1, "matchCreation": 1 })
This will give you the below result
{
"_id" : "myid",
"matchCreation" : 1463916465614,
"participantsData" : [
{
"id" : 1
}
]
}

List all values of a certain field in mongodb

How would I get an array containing all values of a certain field for all of my documents in a collection?
db.collection:
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be6"), "x" : 1 }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be7"), "x" : 2 }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be8"), "x" : 3 }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be9"), "x" : 4 }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990bea"), "x" : 5 }
"db.collection.ListAllValuesForfield(x)"
Result: [1,2,3,4,5]
Also, what if this field was an array?
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be6"), "y" : [1,2] }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be7"), "y" : [3,4] }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be8"), "y" : [5,6] }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990be9"), "y" : [1,2] }
{ "_id" : ObjectId("51a7dc7b2cacf40b79990bea"), "y" : [3,4] }
"db.collection.ListAllValuesInArrayField(y)"
Result: [1,2,3,4,5,6,1,2,3,4]
Additionally, can I make this array unique? [1,2,3,4,5,6]
db.collection.distinct('x')
should give you an array of unique values for that field.
Notice: My answer is a fork from the original answer.
Before any "thumbs up" here, "thumbs up" the accepted answer :).
db.collection.distinct("NameOfTheField")
Finds the distinct values for a specified field across a single collection or view and returns the results in an array.
Reference: https://docs.mongodb.com/manual/reference/method/db.collection.distinct/
This would return an array of docs, containing just it's x value...
db.collection.find(
{ },
{ x: 1, y: 0, _id:0 }
)
db.collection_name.distinct("key/field_name") - This will return a list of distinct values in the key name from the entire dictionary.
Just make sure you don't use the curly brackets after round brackets.

Use aggregation framework to get peaks from a pre-aggregated dataset

I have a few collections of metrics that are stored pre-aggregated into hour and minute collections like this:
"_id" : "12345CHA-2RU020130104",
"metadata" : {
"adaptor_id" : "CHA-2RU",
"processor_id" : NumberLong(0),
"date" : ISODate("2013-01-04T00:00:00Z"),
"processor_type" : "CHP",
"array_serial" : NumberLong(12345)
},
"hour" : {
"11" : 4.6665907,
"21" : 5.9431519999999995,
"7" : 0.6405864,
"17" : 4.712744,
---etc---
},
"minute" : {
"11" : {
"33" : 4.689972,
"32" : 4.7190895,
---etc---
},
"3" : {
"45" : 5.6883,
"59" : 4.792,
---etc---
}
The minute collection has a sub-document for each hour which has an entry for each minute with the value of the metric at that minute.
My question is about the aggregation framework, how should I process this collection if I wanted to find all minutes where the metric was above a certain highwater mark? Investigating the aggregation framework is showing an $unwind function but that seems to only work on arrays..
Would the map/reduce functionality be better suited for this? With that I could simply emit any entry above the highwatermark and count them.
You could build an array of "keys" using a reduce function that iterates through the objects attributes.
reduce: function(obj,prev)
{
for(var key in obj.minute) {
prev.results.push( { hour:key, minutes: obj.minute[key]});
}
}
will give you something like
{
"results" : [
{
"hour" : "11",
"minutes" : {
"33" : 4.689972,
"32" : 4.7190895
}
},
{
"hour" : "3",
"minutes" : {
"45" : 5.6883,
"59" : 4.792
}
}
]
}
I've just done a quick test using a group() - you'll need something more complex to iterate though the sub-sub documents (minutes) but hopefully points you in right direction.
db.yourcoll.group(
{
initial: { results: [] },
reduce: function(obj,prev)
{
for(var key in obj.minute) {
prev.results.push( { hour:key, minutes: obj.minute[key]});
}
}
}
);
In the finalizer you could reshape the data again. It's not going to be pretty, it might be easier to hold the minute and hour data as arrays rather than elements of the document.
hope it helps a bit