Update: Move field into another's field array - mongodb

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

Related

Query for sub-document when key is numeric?

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

Query to update showing 0 records updated mongo

Am using below query in db.getcolletion
db.getCollection('publickdata').find({"mediatorProf" : "ctvdl Without audieo",
"ProfileName":"p2_max_dls",ProfileDetails:
{ $elemMatch: { Name: "psMaxdetail", Value: "Mozilla 13" } }})
I need to update Value: Mozilla 13 to Value: Mozilla 12
Am using below query:
db.collection.update( // or updateMany directly, removing the flag for 'multi'
{"elemMatch.Value":"Mozilla 13"},
{$set:{"elemMatch.$[].Value":"Mozilla 12"}}, // notice the empty brackets after '$' opearor
false,
true
)
output showing: Updated 0 records provide suggestions.
document is:
/* 1 */
{
"_id" : ObjectId("5e31a24faa0f99e"),
"lastUpdateDate" : ISODate("2020-02-29T16:18:39.664Z"),
"mediatorProf" : "ctvdl Without audieo",
"ProfileName" : "p2_max_dls",
"ProfileDetails" : [
{
"Name" : "psMaxdetail",
"Value" : "Mozilla 13"
},
{
"Name" : "iptvSupported",
"Value" : "N"
}
]
}
Use $[identifier] along with arrayFilters.
db.publickdata.updateMany(
{
"mediatorProf": "ctvdl Without audieo",
"ProfileName": "p2_max_dls"
},
{
$set:{
"ProfileDetails.$[elem].Value": "Mozilla 12"
},
$unset:{
"ProfileDetails.$[elem].AttributeValue": 1
}
},
{
multi: true,
arrayFilters: [{"elem.Name": "psMaxdetail", "elem.Value": "Mozilla 13"}]
}
);

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

Individual search result in multiple values in arrays

I have following model:
{
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"serials" : [
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"320ffd42-f101-4b1d-8ff4-80bc693a29e6",
"fef5e68b-aed0-4a96-9488-7941c41d1c1f",
"2c0752ba-bf7a-4a3b-bd9f-14db4b2f8bae",
"6c5ff44d-5979-4bff-af12-9e6d282c3789",
"9c91bf91-72d7-4b71-827b-924947d6e93d",
"fb34b28e-afb1-4b6a-a3c1-5a1fe44246ee",
"91ab22ef-702f-4cbd-8919-a67a2b9a684c",
"ee1a7cb2-e088-47e6-a824-c8697df7d94c",
"0dc4c687-4db2-481e-a1a6-491320dede11",
"34612148-3e01-44ee-b262-de2035e63691",
"5ba85baf-e48a-40af-8578-55ff1a873c76",
"19fe3672-b6cb-4bb6-8d21-93412b938584",
"1d0d6f6d-1b49-461b-8661-ecbf43a6595e",
"d9a5455c-65ee-45e1-ae49-33cc15dec841",
"4a690a00-a76c-4d3e-aee3-78b2bb731b0c",
"ae331830-40b4-457c-8cc4-5d548f769c3e",
"fe3e460b-c89d-4ace-8a36-5ba2b53bf4d0",
"2cc6a2a0-e029-475f-a7fc-a46a79afb605",
"a7d07767-eada-4ce3-b083-9b048e9ae9f4"
],
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
I would like to retrive documents (multiple) from collection based on this serial numbers ("serials" field). For example i am finding:
[
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"key that doesn't exist",
]
We have to assume that one of the serial number doesn't exist, so would like to get information for individual serial, expected output:
[
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"e127av48-0697-4977-b096-5ce79c89a414",
"doc":{
....whole document where above serial is in array field "serials"
}
},
{
"serial":"key that doesn't exist",
"doc": null
}
]
I was trying the simplest solution - mongodb find by multiple array items, but unfortunately it'doesn't return info for individual serial number. I'am not sure it's possible to prepare this kind of query. I think some complex aggregation could perform it, but i don't even know this kind of pipelines.
Of course, i can get simple solution by using multiple aggregate or even find, but it could impact on performance, when application will be looking for 10000 records per request.
The following query can do the trick:
db.collection.aggregate([
{
$limit:1
},
{
$project:{
"_id":0,
"serialsToSearch":[
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"key that doesn't exist",
]
}
},
{
$unwind:"$serialsToSearch"
},
{
$lookup:{
"from":"collection",
"let":{
"serial":"$serialsToSearch"
},
"pipeline":[
{
$match:{
$expr:{
$in:["$$serial","$serials"]
}
}
},
{
$project:{
"serials":0
}
}
],
"as":"searialsLookup"
}
},
{
$unwind:{
"path":"$searialsLookup",
"preserveNullAndEmptyArrays":true
}
},
{
$project:{
"serial":"$serialsToSearch",
"doc":{
$ifNull:["$searialsLookup",null]
}
}
}
]).pretty()
Data Set:
{
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"serials" : [
"e127av48-0697-4977-b096-5ce79c89a414",
"d163f80a-55ff-40fe-90b4-331ece5bebd5",
"4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"320ffd42-f101-4b1d-8ff4-80bc693a29e6",
"fef5e68b-aed0-4a96-9488-7941c41d1c1f",
"2c0752ba-bf7a-4a3b-bd9f-14db4b2f8bae",
"6c5ff44d-5979-4bff-af12-9e6d282c3789",
"9c91bf91-72d7-4b71-827b-924947d6e93d",
"fb34b28e-afb1-4b6a-a3c1-5a1fe44246ee",
"91ab22ef-702f-4cbd-8919-a67a2b9a684c",
"ee1a7cb2-e088-47e6-a824-c8697df7d94c",
"0dc4c687-4db2-481e-a1a6-491320dede11",
"34612148-3e01-44ee-b262-de2035e63691",
"5ba85baf-e48a-40af-8578-55ff1a873c76",
"19fe3672-b6cb-4bb6-8d21-93412b938584",
"1d0d6f6d-1b49-461b-8661-ecbf43a6595e",
"d9a5455c-65ee-45e1-ae49-33cc15dec841",
"4a690a00-a76c-4d3e-aee3-78b2bb731b0c",
"ae331830-40b4-457c-8cc4-5d548f769c3e",
"fe3e460b-c89d-4ace-8a36-5ba2b53bf4d0",
"2cc6a2a0-e029-475f-a7fc-a46a79afb605",
"a7d07767-eada-4ce3-b083-9b048e9ae9f4"
],
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
Output:
{
"serial" : "e127av48-0697-4977-b096-5ce79c89a414",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{
"serial" : "d163f80a-55ff-40fe-90b4-331ece5bebd5",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{
"serial" : "4740021f-e9b5-4ca5-bf0e-8554c123bb94",
"doc" : {
"_id" : ObjectId("5d61aaf8108e185191552bbb"),
"name" : "ApiCard",
"producer" : "Farmina",
"form" : "syrop",
"__v" : 0
}
}
{ "serial" : "key that doesn't exist", "doc" : null }
Note: The query won't give expected output if the collection would be empty.
Aggregation stages details:
STAGE I: Limiting the records to 1, as initially, our motive is to inject the input array in aggregation. The injection would be done in no time.
STAGE II: Projecting the input array as serialsToSearch
STAGE III: Now we have the input array as a field, we can unwind it
STAGE IV: Lookup in the same collection with each field of the input array and check if the searched serial is present in serials array
STAGE V: unwinding the lookup output
STAGE VI: Projecting fields as per the response required.

MongoDB converting an array to dictionary

I insert:
{
...
"loc":[-124.9973,49.68829]
...
}
and it is stored as:
"loc": {
"0": -124.9973,
"1": 49.68829
}
This occurs from mongo command line or via PHP or through RockMongo forms.
Is this the expected behavior?
When I do an insert from the mongo shell I DO NOT get that behavior
dmReplSet:PRIMARY> db.aug29.insert( { "loc" : [ -124.9973, 49.68829 ] } );
WriteResult({ "nInserted" : 1 })
dmReplSet:PRIMARY> db.aug29.find();
{ "_id" : ObjectId("54010c47de66cda372635186"), "loc" : [ -124.9973, 49.68829 ] }
dmReplSet:PRIMARY> db.aug29.find().pretty()
{
"_id" : ObjectId("54010c47de66cda372635186"),
"loc" : [
-124.9973,
49.68829
]
}
dmReplSet:PRIMARY>
How exactly are you doing your insert? Is it through some OR mapping?
On the other hand, if you are trying to do geospacial stuff in MongoDB you probably should read up on it and be aware it has been evolving and is MongoDB version specific ( http://blog.mongolab.com/2014/08/a-primer-on-geospatial-data-and-mongodb/ and http://myadventuresincoding.wordpress.com/2011/10/02/mongodb-geospatial-queries/ ).