Powershell Append PSCustomObject to nested PSCustomObject - powershell

My problem is that I have a remote API giving me a JSON with 10 levels deep nesting and I need to add another array/hashtable/PSCustomObject (whatever you want to call it) to one of them.
The JSON looks like this:
{
"result": [
"modules": [
"data": {
"segments": [
{
"routes": {
"static": [
{
"destination": "10.0.0.0",
"netmask": "255.0.0.0"
},
{
"destination": "172.16.0.0",
"netmask": "255.240.0.0"
}
]
}
}
]
}
]
]
}
The return object is a Hashtable, however when I try to access the values in dot notation, it turns into PSCustomObject:
$statics = $result.result.modules.data.segments.routes.static
It won't let me use the other notation:
$statics = $result['result']['modules']['data']['segments']['routes']['static']
Error: Cannot index into a null array
The real tricky part (for me) is to to append a new hastable to the "static" hashtable in such a way that the rest of the hashtable remains intact.
$newroute = #{
destination = "1.1.1.1."
netmask = "255.255.255.255"
}
I would have used PHP or Python but for this project I must use Powershell and I'm running into all sorts of things where PS behaves different from what I expect.
Any help is greatly appreciated!

static is an array, you can add new items to it:
$newStaticRoute = [pscustomobject]#{
destination = '192.168.0.0'
netmask = '255.255.0.0'
}
$result.result.modules.data.segments.routes.static += $newStaticRoute

Related

mongo forEach unable to update array in document

I'm trying write a mongo script to run in RoboMongo that will loop through all documents in a collection. Each document contains an array myArray. The documents look like this:
{
"name": "myApp",
"myArray": [
{ "env": "dev", "dbHost": "db2dev.local" },
{ "env": "prod", "dbHost": "db1prod.local" }
]
I want to copy the dbHost field that is defined in dev to prod. So the above result would be:
{
"name": "myApp",
"myArray": [
{ "env": "dev", "dbHost": "db2dev.local" },
{ "env": "prod", "dbHost": "db2dev.local" }
]
When I try to access the field myArray[0] I get a syntax error that says:
TypeError: myDoc.myArray[0] is undefined
The function is something like this:
db.myCollection.find().forEach( function(myDoc) {
var devIdx = 0;
var prodIdx = 1;
if (myDoc.myArray[0].env !== 'dev')}
devIdx = 1;
prodIdx = 0;
}
myDoc.myArray[prodIdx].dbHost = myDoc.myArray[devIdx].dbHost;
print(myDoc);
});
I've examined the collection (it is very small) and each document has a myArray field as it should with exactly two values (one for dev and one for prod) in the array.
What am I doing wrong? What is the correct syntax to use inside a mongo script? Is updating arrays in a document not supported?
Searching for solution
I've searched and found forEach examples but most are trivial and none include an array being accessed or changed.
The mongo docs are also very simplistic: https://docs.mongodb.com/v3.6/reference/method/cursor.forEach/
Mongo javascript does not allow you to access arrays directly like you are trying to do (unless you are in a for loop). So a solution is shown below:
db.myCollection.find({}).forEach( function(myDoc) {
var foundDevEntry = null;
var updatedProdEntry = false;
// First time loop to get a copy of the dev entry
for (var idx in myDoc.myArray) {
if (myDoc.myArray[idx].env === 'dev') {
foundDevEntry = myDoc.myArray[idx];
}
}
// 2nd time loop to update the value
for (var idx in myDoc.myArray) {
if (myDoc.myArray[idx].env === 'prod') {
myDoc.myArray[idx].dbHost = foundDevEntry.dbHost;
}
}
// Now update the database with this change
db.myCollection.update({_id: myDoc._id}, {$set: {"myArray": myDoc.myArray}});
print(myDoc); // So results are also returned when query is run
});
I've stripped out error checking to focus on the change required. What (to me) is odd is that the syntax myDoc.myArray[idx] is actually valid but only inside a loop!
The following references helped me come to a solution:
Update in forEach on mongodb shell
https://www.mysoftkey.com/mongodb/how-to-use-foreach-loop-in-mongodb-to-manipulate-document/
I should add that some solutions I read said that to update an array you had to re-build the array (https://stackoverflow.com/a/22657219/3281336). I did not do that in my solution and it did work but wanted to share it.

Trouble adding to hash in perl

I am reading a file replacing data and returning output to json. When I try to add a new item to the hash I get the following error. Not a HASH reference When I use ref() I am getting HASH as the type.
I have tried.
my $json_data = decode_json($template);
$json_data->{CommandCenters}{NewItem} = ["haha","moredata"];
Gives the not a hash reference error
The $json_data is below.
{
"Location":"Arkansas",
"CommandCenters": [
{
"secretary": "jill",
"janitor": "mike"
}
],
}
I am looking for the following output after I add the element.
{
"Location":"Arkansas",
"city": "little rock"
"CommandCenters": [
{
"secretary": "jill",
"janitor": "mike"
},
{
"NewItem":["whatever","more data"]
}
],
}
If I use $json_data->{CommandCenters}[0]{NewItem} = ['whatever','sure']; I do not get an error but I get unexpected results.
The data is added but in the incorrect slot.
"commandcenters":
[
"secretary":"jill",
"janitor": "mike",
"newitem":
[
"whatever","sure"
],
]
To add a new element to an array, use push. As we
're dealing with an array reference, we need to dereference it first.
push #{ $json_data->{CommandCenters} }, { NewItem => ["haha", "moredata"] };
When I try to add a new item to the hash I get the following error. Not a HASH reference When I use ref() I am getting HASH as the type.
Attention to detail is a vital skill for a successful programmer. And you're missing something subtle here.
When you use ref(), I assume you're passing it your $json_data variable. And that is, indeed, a hash reference. But the line that generates your Not a HASH reference is this line:
$json_data->{CommandCenters}{NewItem} = ["haha","moredata"];
And that's not just treating $json_data as a hash reference ($json_data->{...}) it's also treating $json_data->{CommandCenters} as a hash reference. And that's where your problem is. $json_data->{CommandCenters} is an array reference, not a hash reference. It's generated from the bit of your JSON that looks like this:
"CommandCenters": [
{
"secretary": "jill",
"janitor": "mike"
}
]
And those [ .. ] mark it as an array not a hash. You can't add a new key/value pair to an array; you need to push() new data to the end of the array. Something like:
push #{ $json_data->{CommandCenters} }, { NewItem => ["haha", "moredata"] };
That will leave you with this data structure:
$VAR1 = {
'CommandCenters' => [
{
'janitor' => 'mike',
'secretary' => 'jill'
},
{
'NewItem' => [
'haha',
'moredata'
]
}
],
'Location' => 'Arkansas'
};
And encode_json() will turn that into the JSON that you want.

Multiple values in query

I am trying to get stats for categories, where I want to specify multiple categories. I have query params like this
$query_params = json_decode('{
"aggregated_by": "day",
"limit": 500,
"categories": "category1 category2",
"start_date": "2018-08-12",
"end_date": "2018-08-13"}');
Obviously the way that I am defining multiple categories is wrong, but what is the correct way ? I tried array, comma/space separated values but with no luck.
You need to just name each category in a distinct argument. In your example, It'd probably look like:
$query_params = json_decode('{
"aggregated_by": "day",
"limit": 500,
"categories": "category1",
"categories": "category2",
"start_date": "2018-08-12",
"end_date": "2018-08-13"}');
So I solved this by passing the parameters as array:
$query = array('categories' => ['cat1','cat2']);
And then I modified sendgrid client buildUrl method like this:
private function buildUrl($queryParams = null)
{
$path = '/' . implode('/', $this->path);
if (isset($queryParams)) {
$queryParams = \http_build_query($queryParams);
$path .= '?' . preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $queryParams);
}
return sprintf('%s%s%s', $this->host, $this->version ?: '', $path);
}
I added the preg_replace part so the url is correctly built without including array key from nested array.

Can I parameterize labels and properties on CREATE or SET? (REST and transaction)

I have a query
1. CREATE (a:%1$s {props}), (b:%2$s {props2}), (b)-[:%3$s {relProps}]->(a)
2. MATCH (a:%1$s { value:{value} })-[:%2$s]->(b) WHERE (b:%3$s) SET (b {props})
I'm using underscore.string to allow string format but would love to just stick with parameters.
Is it possible to parameterize labels such as
{
"query": CREATE (a:{label} {props}),
"params": {
"label":"SomeLabel",
"props":{....}
}
}
and is it also possible to parameterize properties on a SET?
{
"query": "MATCH ..... SET (node {props})"
"params": {
"props":{
"prop1:":"Property Name",
....
}
}
}
Also is there a way to parameterize on a "MERGE"? it gives me 'Parameter maps cannot be used in MERGE patterns (use a literal map instead, eg. "{id: {param}.id}")'
EDIT: what about parameterizing where clause?
MATCH (:Identity%1$s {nodeId:{nodeId})-[r*2..3]-(node1)-[b:%2$s]->(node2) %4$s return *
I have %4$s there for me to put whatever clauses I need to. If I want to have it as
WHERE node1.nodeId= {someNodeId} SET b= {props}
is that possible??
Also when I'm doing a transaction SET node={props} does not seem to work. I tried
statements:[
{
"statement":"..... SET node={props}",
"parameters":{
"props": {
"description":"some description"
}
}
}
]
Any suggestions?? Thank you!
You cannot parameterize labels since the query plan might look different for a different label.
Parameterizing multiple properties using a map is possible, note the slight difference in the SET syntax:
{
"query": "MATCH ..... SET node = {props}"
"params": {
"props":{
"prop1:":"Property Name",
....
}
}
}
Not 100% about MERGE but I guess this should work:
{
"query": "MERGE (n:Label {identifier: {idValue}) ON CREATE SET n = {props}"
"params": {
"identifier": 123,
"props":{
"identifier": 123,
"prop1:":"Property Name",
....
}
}
}
I found out!
CREATE ... SET node = {props}
does the trick to set multiple properties with parameters
doc: http://docs.neo4j.org/chunked/snapshot/cypher-parameters.html

How to append a key:value to a MongoDB cursor?

Is it possible to append a key:value to a MongoDB cursor?
I tried this:
cursor = collection.find(query,projection)
cursor['my_message'] = "value here" # trying to add key:value here
But it doesn't seem to work (500).
In more context, this works:
dbname = 'my_db'
db = connection[dbname]
collection = db.my_collection
query = {'key_1': my_var}
projection = {'key_2':1}
cursor = collection.find(query,projection)
response.content_type = 'application/json'
return dumps(cursor)
This doesn't:
dbname = 'my_db'
db = connection[dbname]
collection = db.my_collection
query = {'key_1': my_var}
projection = {'key_2':1}
cursor = collection.find(query,projection)
cursor['my_message'] = "value here" # trying to add key:value here
response.content_type = 'application/json'
return dumps(cursor)
Edit: And just to visualise what is being returned successfully (without the appended value), it is something like:
[{ document_1 },{ document_2 },{ document_3 }]
And I expect it to look something like:
["my_message":"value here",{ document_1 },{ document_2 },{ document_3 }]
Edit: I tried the following as an alternative and also got a 500.
entries = []
cursor = collection.find(query,projection)
for entry in cursor:
entries.append(entry)
entries['my_message'] = "value here"
response.content_type = 'application/json'
return dumps(entries)
Really, WiredPrarie answered this for you right at the beginning, and everyone is saying the same thing.
We know what you want to do. You want your serialized response to be sent back with some information you want to put in and then the resultset. I also presume that you want to use these results and your other data, likely loaded into some JavaScript processing store.
I have never seen anything that didn't expect some sort of structure like:
{
"result": "ok",
"meta": [{ akey: "avalue"}, {bkey: "bvalue"}],
"results:[ // this is your 'entries' value here
{ document_1 },
{ document_2 },
{ document_3 },
....
So what everyone is saying is embed your entries into another structure that you are going to serialize and return. By trying to push your other keys into the entries list you are doing it the wrong way around.