Force list type in $min update operator - mongodb

I have documents with the following structure:
{
"_id" : 0,
"mins" : {
"ts1" : {
"node1" : [
1,
2,
3
],
"node2" : [
4,
5,
6
]
}
}
}
I'd like to update documents by taking the component-wise minimum for an array. As MongoDB does not support $min on arrays (I think), I'm updating each index individually like so:
db.foo.updateOne(
{"_id" : 0},
{$min: {
"mins.ts3.node1.0": 1,
"mins.ts3.node1.1": 2
}}
)
This works fine but the problem is that if the document does not have the array before updating, MongoDB creates a nested document instead of an array:
{
"_id" : 0,
"mins" : {
"ts1" : {
"node1" : [
1,
2,
3
],
"node2" : [
4,
5,
6
]
},
"ts3" : {
"node1" : {
"0" : 1,
"1" : 2
}
}
}
}
Is there a way to tell MongoDB it is updating a list even if the list does not exist yet?
I'd like to avoid creating empty lists for each document as that would break my current program design.

Related

Insert new fields to document at given array index in MongoDB

I have the following document structure in a MongoDB collection :
{
"A" : [ {
"B" : [ { ... } ]
} ]
}
I'd like to update this to :
{
"A" : [ {
"B" : [ { ... } ],
"x" : [],
"y" : { ... }
} ]
}
In other words, I want the "x" and "y" fields to be added to the first element of the "A" array without loosing "B".
Ok as there is only one object in A array you could simply do as below :
Sample Collection Data :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
A:[
{
B: [{ abc: 1 }]
}
]
}
Query :
/** Insert new fields into 'A' array's first object by index 0 */
db.collection.updateOne(
{ "_id" : ObjectId("5e7c3f77c16b5679b4af4caf") },
{ $set: { "A.0.x": [] , "A.0.y" : {abcInY :1 }} }
)
Output :
{
"_id" : ObjectId("5e7c3cadc16b5679b4aeec26"),
"A" : [
{
"B" : [
{
"abc" : 1
}
],
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
]
}
Or Using positional operator $ :
db.collection.updateOne(
{ _id: ObjectId("5e7c3cadc16b5679b4aeec26") , 'A': {$exists : true}},
{ $set: { "A.$.x": [] , "A.$.y" : {abcInY :1 }} }
)
Note : Result will be the same, but functionally when positional operator is used fields x & y are inserted to first object of A array only when A field exists in that documents, if not this positional query would not insert anything (Optionally you can check A is an array condition as well if needed). But when you do updates using index 0 as like in first query if A doesn't exist in document then update would create an A field which is an object & insert fields inside it (Which might cause data inconsistency across docs with two types of A field) - Check below result of 1st query when A doesn't exists.
{
"_id" : ObjectId("5e7c3f77c16b5679b4af4caf"),
"noA" : 1,
"A" : {
"0" : {
"x" : [],
"y" : {
"abcInY" : 1.0
}
}
}
}
However, I think I was able to get anothe#whoami Thanks for the suggestion, I think your first solution should work. However, I think I was able to get another solution to this though I'm not sure if its better or worse (performance wise?) than what you have here. My solution is:
db.coll.update( { "_id" : ObjectId("5e7c4eb3a74cce7fd94a3fe7") }, [ { "$addFields" : { "A" : { "x" : [ 1, 2, 3 ], "y" : { "abc" } } } } ] )
The issue with this is that if "A" has more than one array entry then this will update all elements under "A" which is not something I want. Just out of curiosity is there a way of limiting this solution to only the first entry in "A"?

Mongodb- using find() method on an Array of Objects only return first match instead of all

Unlike the other question someone asked where they wanted only one item returned. I HAVE one item returned and I need ALL of the matching objects in the array return. However the second object that matches my query is being completely ignored.
This is what one of the items in the item collection looks like:
{
name: "soda",
cost: .50,
inventory: [
{ flavor: "Grape",
amount: 8 },
{ flavor: "Orange",
amount: 4 },
{ flavor: "Root Beer",
amount: 15 }
]
}
Here is the query I typed in to mongo shell:
Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.$.flavor" : 1})
And here is the result:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8,
}
]
And here is what I want it to return to me:
"_id" : ObjectId("59dbe33094b70e0b5851724c"),
"name": "soda"
"inventory" : [
{ "flavor" : "Grape",
"amount" : 8
},
{ "flavor" : "Orange",
"amount" : 4
}
]
I'm new to mongo and am dabbling to get familiar with it. I've read through the docs but couldn't find a solution to this though it's quite possible I overlooked it. I'd really love some help. Thanks in advance.
first u can get your result by this query
db.Items.find({"inventory.amount" : { $lte : 10} } , { name : 1, "inventory.flavor" : 1 , "inventory.amount" : 1})

MongoDB: querying for completion in documents containing an array of objects

I have the following documents inside a folders collection:
folders: [
{ _id : 1,
docs : [
{ foo : 1,
bar : undefined},
{ foo : 3,
bar : 3}
]
},
{ _id : 2,
docs : [
{ foo : 2,
bar : 2},
{ foo : 3,
bar : 3}
]
},
{ _id : 3,
docs : [
{ foo : 2},
{ foo : 3,
bar : 3}
]
},
{ _id : 4,
docs : [
{ foo : 1 }
]
},
{ _id : 5,
docs : [
{ foo : 1,
bar : null }
]
}
]
I need to be able to query the documents that do not have an undefined value, null value, or non-existent value for docs.bar. In the case above, the query should only return the document with _id: 2. I currently have a solution but I was wondering if there is a better way to query the documents.
My current solution:
db.folders.find({$nor: [{"docs.bar": { $exists: false }}]})
This ...
db.folder.find({"docs.bar": {$exists: true}, "docs.bar": {$ne: null}})
... will return only those entries for which at least one of the sub documents in the docs array has a populated bar attribute. Note: in this query the two predicates are ANDed, I think that matches your requirements, it certainly returns the document with _id: 2 from the set you supplied.

query document nested multiarray-array mongodb elemMatch

I have this documents:
//document 1
{
info : [
{
id : 100,
field : {
a : 1,
b : 2
}
},
{
id : 200,
field : {
a : 3,
b : 4
}
},
{
id : 300,
field : {
a : 5,
b : 6
}
}
]
},
//document 2
{
info : [
{
id : 400,
field : {
a : 7,
b : 8
}
},
{
id : 500,
field : {
a : 9,
b : 10
}
}
]
}
I need to find the id of the subdocument with the values field.a = 7 and field.b = 8 , that means the id value is 400.
What i have tried is $elemMatch but I can't get the result.
My attemps :
attemp 1:
db.mycollection.findOne({info : {$elemMatch : { 'field.$.a':7,'field.$.b':8 } } });
attemp 2:
db.mycollection.findOne({info:{$elemMatch:{$elemMatch:{'field.$.a':7,'field.$.b':8,}}}});
attemp 3:
db.mycollection.findOne({info:{$elemMatch:{$elemMatch:{'field.a.$':7,'field.b.$':8,}}}});
attemp 4:
db.mycollection.findOne({info:{$elemMatch:{'field.$.a':7,'field.$.b':8,}}});
The $elemMatch operator works like a "mini query" against the specified array element it is acting on, so arguments go inside. Also the positional $ operator here is a propery of "projection" and not the query document itself, so this is a separate element:
db.mycollection.find(
{
"info": {
"$elemMatch": { "field.a": 7 , "field.b": 8 }
}
},
{ "info.$": 1 }
)
Which both matches the document containing the matched element, and then only returns the matched element due to the projection:
{
"_id" : ObjectId("564d52979f28c6e0feabceee"),
"info" : [
{
"id" : 400,
"field" : {
"a" : 7,
"b" : 8
}
}
]
}

MongoDB, how to use document as the smallest unit to search the document in array?

Sorry for the title, but I really do not know how to make it clear. But I can show you.
Here I have insert two document
> db.test.find().pretty()
{
"_id" : ObjectId("557faa461ec825d473b21422"),
"c" : [
{
"a" : 3,
"b" : 7
}
]
}
{
"_id" : ObjectId("557faa4c1ec825d473b21423"),
"c" : [
{
"a" : 1,
"b" : 3
},
{
"a" : 5,
"b" : 9
}
]
}
>
I only want to select the first document with a value which is greater than 'a' and smaller than 'b', like '4'.
But when i search, i cannot get the result i want
> db.test.find({'c.a': {$lte: 4}, 'c.b': {$gte: 4}})
{ "_id" : ObjectId("557faa461ec825d473b21422"), "c" : [ { "a" : 3, "b" : 7 } ] }
{ "_id" : ObjectId("557faa4c1ec825d473b21423"), "c" : [ { "a" : 1, "b" : 3 }, { "a" : 5, "b" : 9 } ] }
>
Because '4' is greater than the '"a" : 1' and smaller than '"b" : 9' in the second document even it is not in the same document in the array, so the second one selected.
But I only want the first one selected.
I found this http://docs.mongodb.org/manual/reference/operator/query/elemMatch/#op._S_elemMatch, but it seems the example is not suitable for my situation.
You would want to
db.test.findOne({ c: {$elemMatch: {a: {$lte: 4}, b: {$gte: 4} } } })
With your query, you are searching for documents that have an object in the 'c' array that has a key 'a' with a value <= 4, and a key 'b' with a value >= 4.
The second record is return because c[0].a is <= 4, and c[1].b is >= 4.
Since you specified you wanted to select only the first document, you would want to do a findOne() instead of a find().
Use $elemMatch as below :
db.test.find({"c":{"$elemMatch":{"a":{"$lte":4},"b":{"$gte":4}}}})
Or
db.test.find({"c":{"$elemMatch":{"a":{"$lte":4},"b":{"$gte":4}}}},{"c.$":1})