I have JSON data in snowflake table as follows
{
"audit": "ss",
"gdapi": "ww",
"lock": "aa",
"messageBody": {
"id": 111,
"policycontainer": {
"policyTerms": [
{
"Billing": {
"checkpayment": {
"bankroutingnumber": "value1"
}
}
},
{
"Billing": {
"checkpayment": {
"bankroutingnumber": "value2"
}
}
}
]
}
}
}
I want to change the value of bankroutingnumber to null for that I've written the following query
update test
set RECORD_CONTENT =
object_insert(RECORD_CONTENT, 'messageBody',
object_insert(parse_json(RECORD_CONTENT:messageBody), 'policycontainer',
object_insert(parse_json(RECORD_CONTENT:messageBody):policycontainer, 'policyTerms',
object_insert(parse_json(RECORD_CONTENT:messageBody):policycontainer:policyTerms[0], 'Billing',
object_insert(parse_json(RECORD_CONTENT:messageBody):policycontainer:policyTerms[0]:Billing, 'checkpayment',
object_insert(parse_json(RECORD_CONTENT:messageBody):policycontainer:policyTerms[0]:Billing:checkpayment,'bankroutingnumber','null',true), true),true), true), true),true)
After running this the result looks like follow
{
"audit": "ss",
"gdapi": "ww",
"lock": "aa",
"messageBody": {
"id": 111,
"policycontainer": {
"policyTerms": {
"Billing": {
"checkpayment": {
"bankroutingnumber": "null"
}
}
}
}
}
}
now the array is gone. I want to keep the array and loop through policyTerms and edit all bankroutingnumber to null
In this case, you may use LATERAL FLATTEN for "RECORD_CONTENT:messageBody.policycontainer.policyTerms" and then OBJECT_AGG to make them one value again, and update the column but I think you could just use a simple regexp_replace for a "faster" query:
select parse_json( regexp_replace( RECORD_CONTENT::STRING, '"bankroutingnumber":"[^"]*"','"bankroutingnumber":"null"')) from test;
update version:
update test
set RECORD_CONTENT = parse_json( regexp_replace( RECORD_CONTENT::STRING, '"bankroutingnumber":"[^"]*"','"bankroutingnumber":"null"'));
Related
I have the following collection
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
"myarray" : [
{
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
},
{
userId : ObjectId("613ca5e48dbe673802c2d521"),
point : 2
},
]
}
These are my questions
I want to push into myarray if userId doesn't exist, it should be appended to myarray. If userId exists, it should be updated to point.
I found this
db.collection.update({
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId" : ObjectId("570ca5e48dbe673802c2d035")
}, {
$set: { "myarray.$.point": 10 }
})
But if userId doesn't exist, nothing happens.
and
db.collection.update({
_id : ObjectId("57315ba4846dd82425ca2408")
}, {
$push: {
"myarray": {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
})
But if userId object already exists, it will push again.
What is the best way to do this in MongoDB?
Try this
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $push: {"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}}
)
Explination:
in the first statment $pull removes the element with userId= ObjectId("570ca5e48dbe673802c2d035") from the array on the document where _id = ObjectId("57315ba4846dd82425ca2408")
In the second one $push inserts
this object { userId:ObjectId("570ca5e48dbe673802c2d035"), point: 10 } in the same array.
The accepted answer by Flying Fisher is that the existing record will first be deleted, and then it will be pushed again.
A safer approach (common sense) would be to try to update the record first, and if that did not find a match, insert it, like so:
// first try to overwrite existing value
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
// you probably need to modify the following if-statement to some async callback
// checking depending on your server-side code and mongodb-driver
if(!result.nMatched)
{
// record not found, so create a new entry
// this can be done using $addToSet:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408")
},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
// OR (the equivalent) using $push:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": {$ne: ObjectId("570ca5e48dbe673802c2d035"}}
},
{
$push: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
}
This should also give (common sense, untested) an increase in performance, if in most cases the record already exists, only the first query will be executed.
There is a option called update documents with aggregation pipeline starting from MongoDB v4.2,
check condition $cond if userId in myarray.userId or not
if yes then $map to iterate loop of myarray array and check condition if userId match then merge with new document using $mergeObjects
if no then $concatArrays to concat new object and myarray
let _id = ObjectId("57315ba4846dd82425ca2408");
let updateDoc = {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
};
db.collection.update(
{ _id: _id },
[{
$set: {
myarray: {
$cond: [
{ $in: [updateDoc.userId, "$myarray.userId"] },
{
$map: {
input: "$myarray",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{ $eq: ["$$this.userId", updateDoc.userId] },
updateDoc,
{}
]
}
]
}
}
},
{ $concatArrays: ["$myarray", [updateDoc]] }
]
}
}
}]
)
Playground
Unfortunately "upsert" operation is not possible on embedded array. Operators simply do not exist so that this is not possible in a single statement.Hence you must perform two update operations in order to do what you want. Also the order of application for these two updates is important to get desired result.
I haven't found any solutions based on a one atomic query. Instead there are 3 ways based on a sequence of two queries:
always $pull (to remove the item from array), then $push (to add the updated item to array)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{
$push: {
"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
)
try to $set (to update the item in array if exists), then get the result and check if the updating operation successed or if a $push needs (to insert the item)
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
if(!result.nMatched){
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
);
always $addToSet (to add the item if not exists), then always $set to update the item in array
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
{
$addToSet : {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
},
{ multi: false, upsert: false});
db.collection.update({
_id: ObjectId("57315ba4846dd82425ca2408"),
"myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{ $set : { myArray.$.point: 10 } },
{ multi: false, upsert: false});
1st and 2nd way are unsafe, so transaction must be established to avoid two concurrent requests could push the same item generating a duplicate.
3rd way is safer. the $addToSet adds only if the item doesn't exist, otherwise nothing happens. In case of two concurrent requests, only one of them adds the missing item to the array.
Possible solution with aggregation pipeline:
db.collection.update(
{ _id },
[
{
$set: {
myarray: { $filter: {
input: '$myarray',
as: 'myarray',
cond: { $ne: ['$$myarray.userId', ObjectId('570ca5e48dbe673802c2d035')] },
} },
},
},
{
$set: {
myarray: {
$concatArrays: [
'$myarray',
[{ userId: ObjectId('570ca5e48dbe673802c2d035'), point: 10 },
],
],
},
},
},
],
);
We use 2 stages:
filter myarray (= remove element if userId exist)
concat filtered myarray with new element;
When you want update or insert value in array try it
Object in db
key:name,
key1:name1,
arr:[
{
val:1,
val2:1
}
]
Query
var query = {
$inc:{
"arr.0.val": 2,
"arr.0.val2": 2
}
}
.updateOne( { "key": name }, query, { upsert: true }
key:name,
key1:name1,
arr:[
{
val:3,
val2:3
}
]
In MongoDB 3.6 it is now possible to upsert elements in an array.
array update and create don't mix in under one query, if you care much about atomicity then there's this solution:
normalise your schema to,
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
}
You could use a variation of the .forEach/.updateOne method I currently use in mongosh CLI to do things like that. In the .forEach, you might be able to set all of your if/then conditions that you mentioned.
Example of .forEach/.updateOne:
let medications = db.medications.aggregate([
{$match: {patient_id: {$exists: true}}}
]).toArray();
medications.forEach(med => {
try {
db.patients.updateOne({patient_id: med.patient_id},
{$push: {medications: med}}
)
} catch {
console.log("Didn't find match for patient_id. Could not add this med to a patient.")
}
})
This may not be the most "MongoDB way" to do it, but it definitely works and gives you the freedom of javascript to do things within the .forEach.
I am trying to update the ordered quantity of the first article of the first order.
Insert and update instructions:
db.client.insertOne({
"noClient":1, "nomClient":"John Doe", "noTéléphone":"1234567890",
"commandes":[
{
"noCommande":1, "dateCommande":"22-11-2022",
"lignesCommande":[
{ "article":{"noArticle":1, "description":"Lenovo ThinkPad", "prixUnitaire":12000000, "quantiteEnStock":250}, "quantite":13 },
{ "article":{"noArticle":2, "description":"Iphone14", "prixUnitaire":16000000, "quantiteEnStock":123}, "quantite":2 },
{ "article":{"noArticle":3, "description":"All star shoes", "prixUnitaire":12500, "quantiteEnStock":15}, "quantite":1 },
{ "article":{"noArticle":4, "description":"Cahier 200pages", "prixUnitaire":12000, "quantiteEnStock":27}, "quantite":2 }
]
},
{
"noCommande":2, "dateCommande":"23-11-2022",
"lignesCommande":[
{ "article":{"noArticle":5, "description":"Airpods", "prixUnitaire":1300000, "quantiteEnStock":13}, "quantite":1 },
{ "article":{"noArticle":4, "description":"Cahier 200pages", "prixUnitaire":12000, "quantiteEnStock":23}, "quantite":1 }
]
}
]
});
db.client.updateOne({"commandes.noCommande":1, "commandes.lignesCommande.article.noArticle" :1},
{"$set" : {"commandes.lignesCommande.$.quantite":1}})
Screenshot of code:
Following command doesn't work:
db.client.updateOne({"commandes.noCommande":1, "commandes.lignesCommande.article.noArticle" :1},
{"$set" : {"commandes.lignesCommande.$.quantite":1}})
This is actually a pain. Mongodb does not allow multiple positional operators (meaning you cannot use $ directly within your query). Instead, you could use positional filters with arrayFilters.
Playground example - https://mongoplayground.net/p/tDGYeNIYco4
db.collection.update({
"commandes.noCommande": 1
},
{
$set: {
"commandes.$.lignesCommande.$[lig].quantite": 100
}
},
{
arrayFilters: [
{
"lig.article.noArticle": 1
}
]
})
I have a mongo collection where docs have been already stored. The structure is of a single doc is something like this:
"_id":ObjectId("55c3043ab165fa6355ec5c9b"),
"address":{
"building":"522",
"coord":[
-73.95171,
40.767461
],
"street":"East 74 Street",
"zipcode":"10021"
}
}
Now I want to update the doc by inserting a new field "persons" with value being a list of objects [{"name":"marcus", "contact":"420"}, {"name":"modiji", "contact":"111"}], so after insertion the above doc should look like this:
"_id":ObjectId("55c3043ab165fa6355ec5c9b"),
"address":{
"building":"522",
"coord":[
-73.95171,
40.767461
],
"street":"East 74 Street",
"zipcode":"10021"
},
"persons":[
{
"name":"marcus",
"contact":"420"
},
{
"name":"modiji",
"contact":"111"
}
]
}
Can anyone please help me with then correct $set syntax? Also, it would be really helpful if anyone can suggest an efficient way to update a key's value, which is a list of objects so that I can push some new objects inside the existing list.
You can use the updateOne command along with $set operator to achieve it.
db.<Collection-Name>.updateOne({
"_id":ObjectId("55c3043ab165fa6355ec5c9b")
}, {
"$set": {
"persons":[
{
"name":"marcus",
"contact":"420"
},
{
"name":"modiji",
"contact":"111"
}
]
}
})
If you want to push additional data into the array, you can use the below command.
db.<Collection-Name>.updateOne({
"_id":ObjectId("55c3043ab165fa6355ec5c9b")
}, {
"$push": {
"persons": {
"name":"sample",
"contact":"1234"
}
}
})
To push multiple arrays of objects in a single command, use the below query
db.<Collection-Name>.updateOne({
"_id":ObjectId("55c3043ab165fa6355ec5c9b")
}, {
"$push": {
"persons": {
"$each": [
{
"name":"sample1",
"contact":"5678"
},
{
"name":"sample2",
"contact":"90123"
}
]
}
}
})
I have a problem with MongoDB.
I have a collection with many documents like this:
{
key1:[
{el:"EL1"},
{el:"EL2"}
]
}
I want to update all documents in the collection col adding a new key key2 where the value is key1.0.
In particular a generic output's document will be:
{
key1:[
{el:"EL1"},
{el:"EL2"}
],
key2: {el:"EL1"}
}
How can I do that?
Thanks
You can use $addFields with $let to generate new field based on key1.0 value and then you can run $out to update existing collection with the result of aggregation:
db.col.aggregate([
{
$addFields: {
"key2.el": {
$let: {
vars: { fst: { $arrayElemAt: [ "$key1", 0 ] } },
in: "$$fst.el"
}
}
}
},
{ $out: "col" }
])
I have a data under format 'JSON' like this one :
{
"email": "john#john.fr",
"line_items": [
{
"sku": "123456789",
"price": "0.67",
"price_with_tax": "4.00",
"tax_lines": [
{
"title": "tax010",
"rate": 0.01,
"price": "1.11"
},
{
"title": "tax00200",
"rate": 0.02,
"price": "2.22"
}
]
},
{
"sku": "012345666",
"price": "1.67",
"price_with_tax": "5.00",
"tax_lines": [
{
"title": "tax0003000",
"rate": 0.03,
"price": "3.33"
}
]
}
]
}
I want put it in my database (MySql) by PDO::prepare. My following sql query works but second line_items have not good value im Mysql :
1st item :::: good value
email:john#john.fr
sku:123456789
price:0.67
price_with_tax:4.00
price_tax1:1.11
price_tax2:2.22
2nd item ::::
email:john#john.fr
sku:012345666
price:1.67
price_with_tax:5.00
wrong value ::::
price_tax1:1.11
price_tax2:2.22
How can I put it good value ?
here is my code :
$dataDecode = json_decode($jsondata);
$email = $dataDecode->email;
try
{
$dtbs = new PDO($dsn_dev, $pdo_user, $pdo_password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch (Exception $e)
{
die('Error : ' . $e->getMessage());
}
try
{
foreach ($dataDecode->line_items as $obj)
{
$var_sku = $obj->sku;
$var_price = $obj->price;
$var_price_with_tax = $obj->price_with_tax;
$taxNewArray = array();
foreach ($dataDecode->line_items[0]->tax_lines as $obj2)
{
array_push($taxNewArray , $obj2);
}
$val1st = array_shift($taxNewArray);
$val2nd = array_pop ($taxNewArray);
$var_tax1 = $val1st->price;
$var_tax2 = $val2nd->price;
$stmt = $dtbs->prepare("INSERT INTO $tabledata ($mysql_email, $mysql_sku, $mysq_price, $mysql_price_with_tax, $mysql_price__tax1___line_items, $mysql_price__tax2___line_items)
VALUES (:email, :sku, :price, :price_with_tax, :price_tax1, :price_tax2)");
$stmt->execute(array(':email'=>$email,
':sku'=>$var_sku,
':price'=>$var_price,
':price_with_tax'=>$var_price_with_tax,
':price_tax1'=>$var_tax1,
':price_tax2'=>$var_tax2
));
}
}
catch(Exception $e)
{
throw $e;
}
Do you have a idée ?
If there's only one entry in tax_lines, your code will try to set $var_tax2 from the nonexistent second entry. You need to check for this and substitute some other value:
$var_tax2 = $val2nd ? $val2nd->price : '0.0';
The other problem is this line:
foreach ($dataDecode->line_items[0]->tax_lines as $obj2)
You're using the tax lines from the first line item every time. That should be:
foreach ($obj->tax_lines as $obj2)