How can I remove all JSON objects that have a certain key/value pair from an array of JSON objects? - arrays

I want to filter a dataset this like:
For example here's some fake dataset:
[
{
"name": "Sakura",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Luna", "Milk"],
"type": "tabby"
}
]
},
{
"name": "Linn",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Luna"],
"type": "tabby"
}
]
},
{
"name": "Donna",
"hasChildren": false,
"livesInCity": false,
"pets": [
{
"cats": ["Luna", "Milk"],
"type": "tabby"
}
]
},
{
"name": "Tia",
"hasChildren": false,
"livesInCity": false,
"pets": [
{
"cats": ["Luna", "Milk"],
"type": "tuxedo"
}
]
},
{
"name": "Dora",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Artemis", "Milk"],
"type": "tuxedo"
}
]
}
]
I want to filter out everything that has "livesInCity": false:
[
{
"name": "Sakura",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Luna", "Milk"],
"type": "tabby"
}
]
},
{
"name": "Linn",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Luna"],
"type": "tabby"
}
]
},
{
"name": "Dora",
"hasChildren": false,
"livesInCity": true,
"pets": [
{
"cats": ["Artemis", "Milk"],
"type": "tuxedo"
}
]
}
]
How can I do this? Is this possible?
In python I believe it's something like this, but I do not know how to get started in Swift:
people = [person for person in people if person.livesInCity == True]
Also, how can I filter this data set above to look like this - I want to categorize by type in name.
{
"tabby": ["Sakura", "Linn"],
"tuxedo": ["Dora"],
}
Any help will be very appreciated!

As already mentioned in the comments, you can't work directly on JSON objects in Swift. They need to be converted first.
You can parse your JSON to a Swift object and then perform filtering, grouping etc.
struct Person: Codable {
let name: String
let hasChildren: Bool
let livesInCity: Bool
let pets: [Pet]
}
struct Pet: Codable {
let cats: [String]
let type: String
}
let jsonStr = "[{"name": "Sakura", ..." // your JSON string
let jsonData = jsonStr.data(using: .utf8)!
let parsedObjects = try! JSONDecoder().decode([Person].self, from: data)
Then you can filter your parsedObjects:
let filteredObjects = parsedObjects.filter({ person in person.livesInCity == true })
or in shorter (swiftier) version:
let filteredObjects = parsedObjects.filter { $0.livesInCity }
You can also try to group by pet type:
var groupedDict = [String:[String]]()
filteredObjects.forEach{ person in
person.pets.forEach { pet in
if let _ = groupedDict[pet.type] {
groupedDict[pet.type]!.append(person.name)
} else {
groupedDict[pet.type] = [person.name]
}
}
}
print(groupedDict)
//prints ["tabby": ["Sakura", "Linn"], "tuxedo": ["Dora"]]
If at any point you want to convert your objects back to JSON you can use:
let dictData = try! JSONEncoder().encode(groupedDict)
let dictStr = String(data: dictData, encoding: .utf8)!
print(dictStr)
//prints {"tabby":["Sakura","Linn"],"tuxedo":["Dora"]}
Note
For the sake of simplicity I used forced optional unwrapping (!) when decoding/encoding objects. You may want to use do-try-catch instead (to catch errors).

Related

How to filter JSON data based on another JSON data in typescript

I have 2 JSON Data 1. Payers 2. Rules. I need to filter Payers JSON data based on PayerId from Rules JSON data.
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}}
Rules JSON file
{
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
}]}
I need to filter Payers JSON data based on Rules JSON data if PayerID matches
I need output like below
{
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "34567",
"name": "Test Payer3"
}
}
How to filter?
You can use Array.filter like that (based on your data structure):
const filteredPayers = payersObj.Payers.filter((p) => rulesObj.Rules[0].Criteria[0].payerId.includes(p.payerId));
I can't figure out why your Rules json looks like this, I guess you have multiple rules. If so, you will need to iterate over each rule and invoke includes. Same for Criteria.
Code will check each rule and each critirias
and will return payers if payerId found in any of the given rules of any criteria
const payers = {
"Payers": [
{
"payerId": "12345",
"name": "Test Payer1"
},
{
"payerId": "23456",
"name": "Test Payer2",
},
{
"payerId": "34567",
"name": "Test Payer3"
}]}
const rules = {
"Rules": [
{
"actions": {
"canCopyRule": true
},
"RuleId": 123,
"description": "Test Rule",
"isDisabled": false,
"Criteria": [
{
"autoSecondaryCriteriaId": 8888,
"criteriaType": { "code": "primaryPayer", "value": "Primary Payer" },
"payerId": ["12345", "34567"]
}
]
}
]
}
const data = payers.Payers.filter(payer => rules.Rules.findIndex(rule => rule.Criteria.findIndex(criteria => criteria.payerId.includes(payer.payerId)) != -1) !== -1)
console.log(data)

RUBY parse nested JSON Object

I have a ruby script that works pretty well. I'm parsing JSON from an API and passing it into and array.
Most of the top elements parse fine. But I have a nested object that looks like this after its a hash...
I did and inspect on it and outputted the current data object I'm looking at.
orf1: ["displayValue", "DNAORF999-N9, DNAORF888-N9, DNAORF777-N9, DNAORF444-N9"]
orf1_inspect: ["displayValue", "DNAORF999-N9, DNAORF888-N9, DNAORF777-N9, DNAORF444-N9"]
orf1: ["isMulti", true]
orf1_inspect: ["isMulti", true]
orf1: ["textValue", "DNAORF999-N9, DNAORF888-N9, DNAORF777-N9, DNAORF444-N9"]
orf1_inspect: ["textValue", "DNAORF999-N9, DNAORF888-N9, DNAORF777-N9, DNAORF444-N9"]
orf1: ["type", "entity_link"]
orf1_inspect: ["type", "entity_link"]
orf1: ["value", ["seq_dfdfdfdfd", "seq_fdfdfd", "seq_fdfdfdd", "seq_jfdfdfd"]]
Here is the section of code that returns the above..Let me know if you need more info?
if row["fields"]["ORF"].nil? || row["fields"]["ORF"].empty?
orf = nil
else
row["fields"]["ORF"].each do |orf1|
puts 'orf1: ' + orf1.to_s
orfinspect = orf1.inspect
puts 'orf1_inspect: ' + orfinspect
end
end
I cant seem to parse oput the individual values.
I need to get the values/data from these fields... displayValue, isMulti, textValue, type, and value
Ive tried all kinds of approaches.. Some give conversion errors. I can use orf1.first and that works... but its only part of it...
Even this will get me the header for textvalue
orf = orf1[0]["textValue"]
puts 'orf: ' + orf.to_s
here is the inspect on row["fields"]["ORF"].inspect if it helps..
{
"displayValue" => "DNAORF888-N9, DNAORF999-N9, DNAORF444-N9, DNAORF321-N9, DNAORF111-N9, DNAORF777-N9, DNAORF222-N9, DNAORF425-N9, DNAORF122-N9",
"isMulti" => true,
"textValue" => "DNAORF888-N9, DNAORF999-N9, DNAORF444-N9, DNAORF321-N9, DNAORF111-N9, DNAORF777-N9, DNAORF222-N9, DNAORF425-N9, DNAORF122-N9", "type"=>"entity_link",
"value" => ["seq_jddddaA2", "seq_sfgsfff", "seq_osfsffs", "seq_fsdfsd", "seq_fsdfsd", "seq_fsfsfsfs", "seq_sfsfss", "seq_sfsfsf", "seq_sfsfs"]
}
This seems to work. Is this the best approach?
row["fields"]["ORF"].each do |key,value|
if key == 'displayValue'
unless value.nil?
orf_displayValue=value
end
end
if key == 'isMulti'
unless value.nil?
orf_isMulti=value
end
end
if key == 'textValue'
unless value.nil?
orf_textValue=value
end
end
if key == 'type'
unless value.nil?
orf_type=value
end
end
if key == 'value'
unless value.nil?
orf_value=value
end
end
end
end
But I would need to do this for each field. I feel like there is a better way..
As the error says:
can't convert Symbol into Integer
textValue is of type Symbol and when you are looping over data you end with first key and value which is displayValue and DNAORF004-N9 and inside do end block you are accessing DNAORF004-N9 index with textValue which is Symbol which is not possible it should be integer as the error states.
data = {"displayValue"=>"DNAORF004-N9", "isMulti"=>true, "textValue"=>"DNAORF001-N9", "type"=>"entity_link", "value"=>["seq_fdfdf", "seq_9fdfdfdfd"]}
datasequences = [ :displayValue, :textValue ]
datasequences.each do |textValue|
puts "textValue is #{textValue.inspect}"
data.each do |key, value|
puts "\t#{value[textValue]} at #{key}"
end
end
To resolve the issue you can change the code as below:
data = {"displayValue"=>"DNAORF004-N9", "isMulti"=>true, "textValue"=>"DNAORF001-N9", "type"=>"entity_link", "value"=>["seq_fdfdf", "seq_9fdfdfdfd"]}
datasequences = [ :displayValue, :textValue ]
# changing all keys from string to symbol
data = data.transform_keys(&:to_sym)
datasequences.each do |textValue|
puts "textValue is #{textValue.inspect}"
puts "Value: #{data[textValue]}"
end
value = {
"dnaSequences": [
{
"aliases": [],
"annotations": [],
"apiURL": "https://url",
"archiveRecord": nil,
"authors": [
{
"handle": "dsdsd",
"id": "ent_dsdsd",
"name": "dsdsd"
}
],
"bases": "",
"createdAt": "2020-07-14T21:39:26.991794+00:00",
"creator": {
"handle": "dsds",
"id": "ent_dsdsd",
"name": "dsdd Fdsdsdso"
},
"customFields": {},
"dnaAlignmentIds": [],
"entityRegistryId": "MOUSE006",
"fields": {
"Identical mouses": {
"displayValue": nil,
"isMulti": false,
"textValue": nil,
"type": "part_link",
"value": nil
},
"Library Constructed By": {
"displayValue": "dsdsd",
"isMulti": false,
"textValue": "dsdsd",
"type": "dropdown",
"value": "sfso_dsdsd"
},
"Library Construction Date": {
"displayValue": "2020-06-01",
"isMulti": false,
"textValue": "2020-06-01",
"type": "date",
"value": "2020-06-01"
},
"Library Description": {
"displayValue": "dsdsdds",
"isMulti": false,
"textValue": "dsdsdsd",
"type": "text",
"value": "dsdsdsdsd"
},
"Library Sample Source": {
"displayValue": "dsdsds",
"isMulti": false,
"textValue": "dsdsdsds",
"type": "dropdown",
"value": "sfso_dsdsdsd"
},
"ORF": {
"displayValue": "DNAORF004-N9, DNAORF005-N9, DNAORF008-N9, DNAORF001-N9",
"isMulti": true,
"textValue": "DNAORF004-N9, DNAORF005-N9, DNAORF008-N9, DNAORF001-N9",
"type": "entity_link",
"value": [
"seq_aaaaaa",
"seq_bbbbbb",
"seq_ccccc",
"seq_ddddd"
]
},
"Re-Run ORF?": {
"displayValue": nil,
"isMulti": false,
"textValue": nil,
"type": "dropdown",
"value": nil
},
"Sampling GPS Coordinates": {
"displayValue": nil,
"isMulti": false,
"textValue": nil,
"type": "text",
"value": nil
},
"Sequencing Approach": {
"displayValue": "Single Sequence",
"isMulti": false,
"textValue": "Single Sequence",
"type": "dropdown",
"value": "gfgf"
},
"Sequencing Method": {
"displayValue": "gfgf fgfgfg",
"isMulti": false,
"textValue": "gfgf gfgfg",
"type": "dropdown",
"value": "sfsogfgfg_irlx6NfZ"
}
},
"folderId": "gfgfg",
"id": "gfgfgf",
"isCircular": false,
"length": 25129,
"modifiedAt": "2022-04-05T17:06:25.491926+00:00",
"name": "COPE03-P19",
"primers": [],
"registrationOrigin": {
"originEntryId": nil,
"registeredAt": "2020-07-14T22:15:09.541243+00:00"
},
"registryId": "gfgfgfg",
"schema": {
"id": "ps_fdfdfd",
"name": "mouse"
},
"translations": [],
"url": "hyyps:///COPE/f//edit",
"webURL": "https://url"
}
]
}
datasequences = [ :displayValue, :isMulti, :textValue, :type, :value ]
result = value["dnaSequences".to_sym].map do |v|
row = {}
if v.key?(:fields) && v[:fields].key?(:ORF)
datasequences.map do |key|
row[key] = v[:fields][:ORF][key.to_sym]
end
end
row
end
puts result
Note: Tested with Ruby 3.0.0

In Mongoose, query fields based on array

I am trying to query documents from a mongodb collection, based on array of input query parameters sent from URL.
Sample Database Data
[
{
"drawings": {
"circle": [],
"square": [
{
"id": "828",
"name": "square"
}
],
"cube": []
},
{
"drawings": {
"circle": [
{
"id": "827",
"name": "circle"
}
],
"square": [],
"cube": []
},
{
"drawings": {
"circle": [],
"square": [],
"cube": [
{
"id": "829",
"name": "cube"
}
]
}
]
Input Query Parameter:
query = ["square","cube"];
Expected Output:
[
{
"drawings": {
"circle": [],
"square": [
{
"id": "828",
"name": "square"
}
],
"cube": []
},
{
"drawings": {
"circle": [],
"square": [],
"cube": [
{
"id": "829",
"name": "cube"
}
]
}
]
Best suited Mongoose Query:
Schema.find({
$or:[
{'drawings.square':{$elemMatch:{ name:'square'}}},
{'drawings.cube':{$elemMatch:{ name:'cube'}}}
]
});
Tried Below method. But, it is not correct.
let draw = ["square","cube"];
let draw_query =[];
for (let a=0; a<draw.length;a++){
draw_query.push("{\"drawings."+ draw[a] +"\':{$elemMatch:{ name:\"" + draw[a] + "\"}}}");
}
It creates array with single quoted strings. It cannot be used.
[ '{"drawings.square":{$elemMatch:{ name:"square"}}}',
'{"drawings.cube":{$elemMatch:{ name:"cube"}}}' ]
How to generate this mongoose query dynamically? or is there any better mongoose query to achieve the expected result.
You can query it directly using dot notation so the query should look like below:
db.collection.find({
$or: [
{
"drawings.square.name": "square"
},
{
"drawings.circle.name": "circle"
}
]
})
You can build it in JS using .map(), try:
var query = ["square","cube"];
var orQuery = { $or: query.map(x => ({ [x + ".name"]: x }) ) }

Update nested subdocuments in MongoDB with arrayFilters

I need to modify a document inside an array that is inside another array.
I know MongoDB doesn't support multiple '$' to iterate on multiple arrays at the same time, but they introduced arrayFilters for that.
See: https://jira.mongodb.org/browse/SERVER-831
MongoDB's sample code:
db.coll.update({}, {$set: {“a.$[i].c.$[j].d”: 2}}, {arrayFilters: [{“i.b”: 0}, {“j.d”: 0}]})
Input: {a: [{b: 0, c: [{d: 0}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
Output: {a: [{b: 0, c: [{d: 2}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
Here's how the documents are set:
{
"_id" : ObjectId("5a05a8b7e0ce3444f8ec5bd7"),
"name" : "support",
"contactTypes" : {
"nonWorkingHours" : [],
"workingHours" : []
},
"workingDays" : [],
"people" : [
{
"enabled" : true,
"level" : "1",
"name" : "Someone",
"_id" : ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
"contacts" : [
{
"_id" : ObjectId("5a05a8dee0ce3444f8ec5bda"),
"retries" : "1",
"priority" : "1",
"type" : "email",
"data" : "some.email#email.com"
}
]
}
],
"__v" : 0
}
Here's the schema:
const ContactSchema = new Schema({
data: String,
type: String,
priority: String,
retries: String
});
const PersonSchema = new Schema({
name: String,
level: String,
priority: String,
enabled: Boolean,
contacts: [ContactSchema]
});
const GroupSchema = new Schema({
name: String,
people: [PersonSchema],
workingHours: { start: String, end: String },
workingDays: [Number],
contactTypes: { workingHours: [String], nonWorkingHours: [String] }
});
I need to update a contact. This is what I tried using arrayFilters:
Group.update(
{},
{'$set': {'people.$[i].contacts.$[j].data': 'new data'}},
{arrayFilters: [
{'i._id': mongoose.Types.ObjectId(req.params.personId)},
{'j._id': mongoose.Types.ObjectId(req.params.contactId)}]},
function(err, doc) {
if (err) {
res.status(500).send(err);
}
res.send(doc);
}
);
The document is never updated and I get this response:
{
"ok": 0,
"n": 0,
"nModified": 0
}
What am I doing wrong?
So the arrayFilters option with positional filtered $[<identifier>] does actually work properly with the development release series since MongoDB 3.5.12 and also in the current release candidates for the MongoDB 3.6 series, where this will actually be officially released. The only problem is of course is that the "drivers" in use have not actually caught up to this yet.
Re-iterating the same content I have already placed on Updating a Nested Array with MongoDB:
NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.
However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.
So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.
All this means is that the current "driver" implementation of .update() actually "removes" the necessary arguments with the definition of arrayFilters. For NodeJS this will be addressed in the 3.x release series of the driver, and of course "mongoose" will then likely take some time after that release to implement it's own dependencies on the updated driver, which would then no longer "strip" such actions.
You can however still run this on a supported server instance, by dropping back to the basic "update command" syntax usage, since this bypassed the implemented driver method:
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = mongoose.Types.ObjectId;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/test',
options = { useMongoClient: true };
const contactSchema = new Schema({
data: String,
type: String,
priority: String,
retries: String
});
const personSchema = new Schema({
name: String,
level: String,
priority: String,
enabled: Boolean,
contacts: [contactSchema]
});
const groupSchema = new Schema({
name: String,
people: [personSchema],
workingHours: { start: String, end: String },
workingDays: { type: [Number], default: undefined },
contactTypes: {
workingHours: { type: [String], default: undefined },
contactTypes: { type: [String], default: undefined }
}
});
const Group = mongoose.model('Group', groupSchema);
function log(data) {
console.log(JSON.stringify(data, undefined, 2))
}
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Clean data
await Promise.all(
Object.entries(conn.models).map(([k,m]) => m.remove() )
);
// Create sample
await Group.create({
name: "support",
people: [
{
"_id": ObjectId("5a05a8c3e0ce3444f8ec5bd8"),
"enabled": true,
"level": "1",
"name": "Someone",
"contacts": [
{
"type": "email",
"data": "adifferent.email#example.com"
},
{
"_id": ObjectId("5a05a8dee0ce3444f8ec5bda"),
"retries": "1",
"priority": "1",
"type": "email",
"data": "some.email#example.com"
}
]
}
]
});
let result = await conn.db.command({
"update": Group.collection.name,
"updates": [
{
"q": {},
"u": { "$set": { "people.$[i].contacts.$[j].data": "new data" } },
"multi": true,
"arrayFilters": [
{ "i._id": ObjectId("5a05a8c3e0ce3444f8ec5bd8") },
{ "j._id": ObjectId("5a05a8dee0ce3444f8ec5bda") }
]
}
]
});
log(result);
let group = await Group.findOne();
log(group);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})()
Since that sends the "command" directly through to the server, we see the expected update does in fact take place:
Mongoose: groups.remove({}, {})
Mongoose: groups.insert({ name: 'support', _id: ObjectId("5a06557fb568aa0ad793c5e4"), people: [ { _id: ObjectId("5a05a8c3e0ce3444f8ec5bd8"), enabled: true, level: '1', name: 'Someone', contacts: [ { type: 'email', data: 'adifferent.email#example.com', _id: ObjectId("5a06557fb568aa0ad793c5e5") }, { _id: ObjectId("5a05a8dee0ce3444f8ec5bda"), retries: '1', priority: '1', type: 'email', data: 'some.email#example.com' } ] } ], __v: 0 })
{ n: 1,
nModified: 1,
opTime:
{ ts: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
t: 24 },
electionId: 7fffffff0000000000000018,
ok: 1,
operationTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
'$clusterTime':
{ clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 3, high_: 1510364543 },
signature: { hash: [Object], keyId: 0 } } }
Mongoose: groups.findOne({}, { fields: {} })
{
"_id": "5a06557fb568aa0ad793c5e4",
"name": "support",
"__v": 0,
"people": [
{
"_id": "5a05a8c3e0ce3444f8ec5bd8",
"enabled": true,
"level": "1",
"name": "Someone",
"contacts": [
{
"type": "email",
"data": "adifferent.email#example.com",
"_id": "5a06557fb568aa0ad793c5e5"
},
{
"_id": "5a05a8dee0ce3444f8ec5bda",
"retries": "1",
"priority": "1",
"type": "email",
"data": "new data" // <-- updated here
}
]
}
]
}
So right "now"[1] the drivers available "off the shelf" don't actually implement .update() or it's other implementing counterparts in a way that is compatible with actually passing through the necessary arrayFilters argument. So if you are "playing with" a development series or release candiate server, then you really should be prepared to be working with the "bleeding edge" and unreleased drivers as well.
But you can actually do this as demonstrated in any driver, in the correct form where the command being issued is not going to be altered.
[1] As of writing on November 11th 2017 there is no "official" release of MongoDB or the supported drivers that actually implement this. Production usage should be based on official releases of the server and supported drivers only.
I had a similar use case. But my second level nested array doesn't have a key. While most examples out there showcase an example with arrays having a key like this:
{
"id": 1,
"items": [
{
"name": "Product 1",
"colors": ["yellow", "blue", "black"]
}
]
}
My use case is like this, without the key:
{
"colors": [
["yellow"],
["blue"],
["black"]
]
}
I managed to use the arrayfilters by ommiting the label of the first level of the array nest. Example document:
db.createCollection('ProductFlow')
db.ProductFlow.insertOne(
{
"steps": [
[
{
"actionType": "dispatch",
"payload": {
"vehicle": {
"name": "Livestock Truck",
"type": "road",
"thirdParty": true
}
}
},
{
"actionType": "dispatch",
"payload": {
"vehicle": {
"name": "Airplane",
"type": "air",
"thirdParty": true
}
}
}
],
[
{
"actionType": "store",
"payload": {
"company": "Company A",
"is_supplier": false
}
}
],
[
{
"actionType": "sell",
"payload": {
"reseller": "Company B",
"is_supplier": false
}
}
]
]
}
)
In my case, I want to:
Find all documents that have any steps with payload.vehicle.thirdParty=true and actionType=dispatch
Update the actions set payload.vehicle.thirdParty=true only for the actions that have actionType=dispatch.
My first approach was withour arrayfilters. But it would create the property payload.vehicle.thirdParty=true inside the steps with actionType store and sell.
The final query that updated the properties only inside the steps with actionType=dispatch:
Mongo Shell:
db.ProductFlow.updateMany(
{"steps": {"$elemMatch": {"$elemMatch": {"payload.vehicle.thirdParty": true, "actionType": "dispatch"}}}},
{"$set": {"steps.$[].$[i].payload.vehicle.thirdParty": false}},
{"arrayFilters": [ { "i.actionType": "dispatch" } ], multi: true}
)
PyMongo:
query = {
"steps": {"$elemMatch": {"$elemMatch": {"payload.vehicle.thirdParty": True, "actionType": "dispatch"}}}
}
update_statement = {
"$set": {
"steps.$[].$[i].payload.vehicle.thirdParty": False
}
}
array_filters = [
{ "i.actionType": "dispatch" }
]
NOTE that I'm omitting the label on the first array at the update statement steps.$[].$[i].payload.vehicle.thirdParty. Most examples out there will use both labels because their objects have a key for the array. I took me some time to figure that out.

How to Update nested Array in RethinkDB using ReQL

I have a question on Updating the array in RethinkDB. My JSON structure looks like below.
{
"LOG_EVENT": {
"ATTRIBUTES": [
{
"ATTRIBUTE1": "TYPE",
"VALUE": "ORDER"
},
{
"ATTRIBUTE2": "NUMBER",
"VALUE": "1234567"
}
],
"EVENT_CODE": [
{
"CODE_NAME": "EVENT_SAVED",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
}
],
"MSG_HEADER": {
"BUSINESS_OBJ_TYPE": "order",
"MSG_ID": "f79a672b-f15e-459d-a29b-725486d6401f",
"DESTINATIONS": "3"
}
},
"id": "0de3117e-12dd-4d10-a464-dff391a4513f"
}
Here, I am trying to Update a new event inside my event code
{
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T12:58:12.421+08:00"
}
My final JSON will look like below,
{
"LOG_EVENT": {
"ATTRIBUTES": [
{
"ATTRIBUTE1": "TYPE",
"VALUE": "ORDER"
},
{
"ATTRIBUTE2": "NUMBER",
"VALUE": "1234567"
}
],
"EVENT_CODE": [
{
"CODE_NAME": "EVENT_SAVED",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
},
{
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T12:58:12.421+08:00"
}
],
"MSG_HEADER": {
"BUSINESS_OBJ_TYPE": "order",
"MSG_ID": "f79a672b-f15e-459d-a29b-725486d6401f",
"DESTINATIONS": "3"
}
},
"id": "0de3117e-12dd-4d10-a464-dff391a4513f"
}
Can you help on the ReQL query ?
Tried below, but not working
r.db("test").table("test1").get("0de3117e-12dd-4d10-a464-dff391a4513f")("LOG_EVENT")('EVENT_CODE').update(function(row) {
return {EVENT_CODE: row('EVENT_CODE').map(function(d) {
return r.branch(d.append({
"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2",
"EVENT_TIMESTAMP": "2015-08-18T00:58:12.421+08:00"
}), d)
})
}} )
well here is the code which updates the nested fields of object residing inside an array
r.db('DB').table('LOGS')
.get('ID')
.update({
EVENT_CODE: r.row('EVENT_CODE')
.changeAt(1, r.row('EVENT_CODE').nth(1)
.merge({"CODE_NAME": "MESSAGE_DELIVERED_TO_APP2"}))
})

Resources