Query for sub-document when key is numeric? - mongodb

I am struggling to write a mongodb query that reaches into a sub-document for a key that is numeric.
My documents are like...
/* The collection m-plan contains the MfgPlan records */
rs0:SECONDARY> db['plan'].find( { $and: [ {displayId: 'MP-1'}, { version: 5 } ] } ).pretty();
{
"_id" : ObjectId("5cda661b93dbc10001903d52"),
"activities" : {
"5cda66c293dbc10001903d57" : [
"5b198d8c794c820001ee3c25",
"5b198da4794c820001ee3c2e"
],
"5c86f61f42c68d000165559d" : [
"5cda66c293dbc10001903d57"
],
"5b198d8c794c820001ee3c25" : [ ],
"5b198da4794c820001ee3c2e" : [ ]
},
"createdByUser" : "rkuhar",
"createdDate" : ISODate("2021-03-14T06:54:19.154Z"),
"displayId" : "MP-1",
"displayName" : "Obliterator 9000"
"revisionId" : ObjectId("5cda66c293dbc10001903d59"),
"version" : 5
}
But my attempts to locate, like "5c86f61f42c68d000165559d" get rejected by the shell.
rs0:SECONDARY> db['plan'].find( { activities.5c86f61f42c68d000165559d: { $exists: true } } ).pretty();
uncaught exception: SyntaxError: identifier starts immediately after numeric literal :
#(shell):1:33
rs0:SECONDARY> db['plan'].find( { activities."5c86f61f42c68d000165559d": { $exists: true } } ).pretty();
uncaught exception: SyntaxError: missing : after property id :
#(shell):1:33
How do I query for the existence of a key if the key is numeric?

If you use dot notation, you need to wrap the property in quotes. Like so:
db['plan'].find( { "activities.5c86f61f42c68d000165559d": { $exists: true } } ).pretty();

Related

Update: Move field into another's field array

I'm not 100% percent if this is possible, but i think it should be.
I want to update a document so that a field (END) is inserted into an array (INTERMISSIONS). I think im quite close to achieving it, but i either get an error or insert the string "$END".
The two queries i have are :
db.myCollection.findOneAndUpdate(
{"_id" : new ObjectId("...")},
{ '$push' : {'INTERMISSIONS' : '$END' } }
)
This one completes without errors, but instead of the $END value just "$END" is inserted
db.myCollection.findOneAndUpdate(
{"_id" : new ObjectId("...")},
{ '$push' :
{'INTERMISSIONS' : {$first:"$END" }}
}
)
Here i tried to "force" mongo to recognise $END as a field, but i instead get the error The dollar ($) prefixed field '$first' in 'INTERMISSIONS..START.$first' is not valid for storage.
The document structure is the following
{
"_id" : ObjectId("5f7dabb9c02c0000d2003ec2"),
"USUARIO" : "admin",
"START" : ISODate("2020-10-07T11:51:21Z"),
"INTERMISSIONS" : [ ],
"END" : ISODate("2020-10-08T09:39:27Z")
}
When you use an operator check if it can take as argument an expression,
like here a field reference.
{ $push: { <field1>: <value1>, ... } }
MongoDB can do much more,with mongodb >= 4.2 you can do updates using the aggregation pipeline.
I give you the command,the "q" is the query,the "u" is the update pipeline,
you can use it with any driver that supports mongodb >= 4.2
> use testdb
switched to db testdb
> db.testcoll.drop()
true
> db.testcoll.insert(
... {
... "_id" : "5f7dabb9c02c0000d2003ec2",
... "USUARIO" : "admin",
... "START" :ISODate("2020-10-07T11:51:21Z"),
... "INTERMISSIONS" : [ ],
... "END" : ISODate("2020-10-08T09:39:27Z")
... }
... )
WriteResult({ "nInserted" : 1 })
> db.runCommand(
... {
... "update": "testcoll",
... "updates": [
... {
... "q": {},
... "u": [
... {
... "$addFields": {
... "INTERMISSIONS": {
... "$concatArrays": [
... "$INTERMISSIONS",
... [
... "$END" //or use {"start" : "$END"},anything you want to add
... ]
... ]
... }
... }
... }
... ],
... "multi": false
... }
... ]
... }
... )
{ "n" : 1, "nModified" : 1, "ok" : 1 }
> db.testcoll.find({}).pretty();
{
"_id" : "5f7dabb9c02c0000d2003ec2",
"USUARIO" : "admin",
"START" : ISODate("2020-10-07T11:51:21Z"),
"INTERMISSIONS" : [
ISODate("2020-10-08T09:39:27Z")
],
"END" : ISODate("2020-10-08T09:39:27Z")
}

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"?

How to write a query to get all documents where field doesn't matches

I am having some trouble writing a query where I want to get all documents where they don't have a single subdocument where its property IsCurrentRevision is true. In other words, only getting documents that don't have a single revision marked as current.
What I have so far is:
{StartTimeUtc: {$gte: new ISODate('12/14/2019')},
EndTimeUtc: {$lte: new ISODate('01/21/2020')},
IsDeleted: false, 'MyDocument.IsCurrentRevision': false,
"MyDocument.1.IsCurrentRevision" : {$exists: false}}
This gets me somewhere, but the issue is that I have to manually query the index. In this case, the above query returns all documents with only 1 revision (so only one element in the sub document array), that doesn't have its revision marked as current. Thus if I wanted to get documents that have 2 revisions, with the second one marked as false as well I would need to update the query to be:
{StartTimeUtc: {$gte: new ISODate('12/14/2019')},
EndTimeUtc: {$lte: new ISODate('01/21/2020')},
IsDeleted: false, 'MyDocument.1.IsCurrentRevision': false,
"MyDocument.2.IsCurrentRevision" : {$exists: false}}
This is obviously tedious, is there a better way? If it helps, I am using Mongo Compass
Edit:
Here is how my document looks like
{
_id: Guid,
TemplateId: guid,
StartTimeUtc: DateTime,
EndTimeUtc: DateTime
..
..
SoapNoteRevisions: Array {
_id: Guid,
IsCurrentRevision: boolean,
..
..
}
}
I got rid of the unneeded bits as the document itself has a fair bit of fields within the revision as well (another sub array)
If you need documents where MyDocument elements/objects doesn't have IsCurrentRevision : true, try below query :
db.collection.find({'MyDocument.IsCurrentRevision': {$ne : true}})
If using aggregation :
db.collection.aggregate([{$match : {'MyDocument.IsCurrentRevision': {$ne : true}}}])
Collection Data :
/* 1 */
{
"_id" : ObjectId("5e27282ad02e05b69498b814"),
"MyDocument" : [
{
"IsCurrentRevision" : false
},
{
"IsCurrentRevision" : false
},
{
"IsCurrentRevision" : false
}
]
}
/* 2 */
{
"_id" : ObjectId("5e272839d02e05b69498b947"),
"MyDocument" : [
{
"IsCurrentRevision" : true
},
{
"IsCurrentRevision" : true
},
{
"IsCurrentRevision" : true
}
]
}
/* 3 */
{
"_id" : ObjectId("5e272846d02e05b69498ba5c"),
"MyDocument" : [
{
"IsCurrentRevision" : false
},
{
"IsCurrentRevision" : true
},
{
"IsCurrentRevision" : false
}
]
}
Result :
/* 1 */
{
"_id" : ObjectId("5e27282ad02e05b69498b814"),
"MyDocument" : [
{
"IsCurrentRevision" : false
},
{
"IsCurrentRevision" : false
},
{
"IsCurrentRevision" : false
}
]
}

mongoDB distict problems

It's one of my data as JSON format:
{
"_id" : ObjectId("5bfdb412a80939b6ed682090"),
"accounts" : [
{
"_id" : ObjectId("5bf106eee639bd0df4bd8e05"),
"accountType" : "DDA",
"productName" : "DDA1"
},
{
"_id" : ObjectId("5bf106eee639bd0df4bd8df8"),
"accountType" : "VSA",
"productName" : "VSA1"
},
{
"_id" : ObjectId("5bf106eee639bd0df4bd8df9"),
"accountType" : "VSA",
"productName" : "VSA2"
}
]
}
I want to make a query to get all productName(no duplicate) of accountType = VSA.
I write a mongo query:
db.Collection.distinct("accounts.productName", {"accounts.accountType": "VSA" })
I expect: ['VSA1', 'VSA2']
I get: ['DDA','VSA1', 'VSA2']
Anybody knows why the query doesn't work in distinct?
Second parameter of distinct method represents:
A query that specifies the documents from which to retrieve the distinct values.
But the thing is that you showed only one document with nested array of elements so whole document will be returned for your condition "accounts.accountType": "VSA".
To fix that you have to use Aggregation Framework and $unwind nested array before you apply the filtering and then you can use $group with $addToSet to get unique values. Try:
db.col.aggregate([
{
$unwind: "$accounts"
},
{
$match: {
"accounts.accountType": "VSA"
}
},
{
$group: {
_id: null,
uniqueProductNames: { $addToSet: "$accounts.productName" }
}
}
])
which prints:
{ "_id" : null, "uniqueProductNames" : [ "VSA2", "VSA1" ] }

Removing a (sub) array in MongoDB using $pull

So..I'm evaluating Mongodb for managing a bit of my JSON back end. I'm totally new to it and I had one problem that was just messy to do in code, so I thought — heck — let me check out to see if it's time to finally start using Mongo
I have a data structure that is approximately like this:
[
{
"_id" : ObjectId("526f59ee82f2e293f9833c54"),
"humans" : [
{
"serviceUsers" : [
{
"foo1" : "bar2",
"foo2" : "bar3"
},
{
"foo1" : "baz2",
"foo2" : "baz3"
}
]
}
]
}
]
And now I want to remove any serviceUsers array elements that have "foo1" equal to "baz2" so that ideally I would end up with this:
[
{
"_id" : ObjectId("526f59ee82f2e293f9833c54"),
"humans" : [
{
"serviceUsers" : [
{
"foo1" : "bar2",
"foo2" : "bar3"
},
]
}
]
}
]
I figured that $pull was the place to start. And I tried a bunch of contortions. If I'm in collection mytests, I tried
db.mytests.update({"humans.serviceUsers.foo1":"baz2"}, {$pull:{"humans.serviceUsers" : {"foo1":"baz2"}}}, {multi: true})
Which to my admittedly naive eye, seems like it should follow the $pull syntax:
db.collection.update( { field: <query> }, { $pull: { field: <query> } } );
Mongo doesn't complain. But it doesn't change the collection in any way, either.
I also tried
db.mytests.update({}, {$pull:{"humans.serviceUsers" : {"foo1":"baz2"}}}, {multi: true})
Which also failed.
Any suggestions are greatly appreciated.
Thus humans is also array, you should use positional $ operator to access serviceUsers array of matched humans element:
db.mytests.update({ "humans.serviceUsers.foo1" : "baz2" },
{ $pull: { "humans.$.serviceUsers" : { "foo1": "baz2" }}});