Search and extract element located in various path of json structure - jsonb

I have a json in a PostgreSQL database and I need to extract an array not always located in same place.
Problem
Need to extract array choicies of a particular element name
Element name is known, but not where he's sitting in structure
Rules
All elements name are unique
choicies attribute could not be present
JSON structure
pages : [
{
name : 'page1',
elements : [
{ name : 'element1', choicies : [...]},
{ name : 'element2', choicies : [...]}
]
}, {
name : 'page2',
elements : [
{
name : 'element3',
templateElements : [
{
name : 'element4'
choicies : [...]
}, {
name : 'element5'
choicies : [...]
}
]
}, {
name : 'element6'
choicies : [...]
}
]
},{
name : 'element7',
templateElements : [
{
name : 'element8'
choicies : [...]
}
]
}
]
My try to extract elements by flatten the structure
SELECT pages::jsonb->>'name',
pageElements::jsonb ->> 'name',
pageElements::jsonb -> 'choicies',
pages.*
FROM myTable as myt,
jsonb_array_elements(myt.json -> 'pages') as pages,
jsonb_array_elements(pages -> 'elements') as pageElements
Alas column choicies is always null in my results. And that will not work when element is located somewhere else, like
page.elements.templateElements
page.templateElements
... and so on
I don't know if there is a way to search for a key (name) wherever it's sitting in json structure and extract an other key (choicies).
I wish to call a select with element name in parameter return choicies of this element.
By instance, if I call select with element name (element1 or element4 or element8), choicies array (as rows or json or text, no preference here) of this element should be return.

Wow! Solution founded goes belong expectation! JSONPath was the way to go
Amazing what we can do with this.
SQL
-- Use jsonpath to search, filter and return what's needed
SELECT jsonb_path_query(
myt.jsonb,
'$.** ? (#.name == "element_name_to_look_at")'
)->'choices' as jsonbChoices
FROM myTable as myt
Explanation of jsonpath in SQL
jsonb_path_query(jsonb_data, '$.** ? (#.name == "element_name_to_look_at")')->'choices'
jsonb_path_query : posgresql jsonpath function
jsonb_data : database column with jsonb data or jsonb expression
$.** : search everywhere from root element
? : where clause / filter
# : object return by search
#.name == "element_name_to_look_at" : every object name equals element_name_to_look_at
->'choices' : for each object returned by jsonpath, get choices attribute
Final version
After get choices jsonb array, we return a dataset with every choice.
choices arrays look like this :
[{value:'code1',text:'Code Label 1'}, {value:'code2',text:'Code Label 2'},...]
SELECT choices.*
FROM (
-- Use jsonpath to search, filter and return what's needed
SELECT jsonb_path_query(myt.jsonb, '$.** ? (#.name == "element_name_to_look_at")')->'choices' as jsonbChoices
FROM myTable as myt
) choice,
-- Explode json return array into columns
jsonb_to_recordset(choice.jsonbChoices) as choices(value text, text text);

Related

Dynamic Graph QL variable names

The following is a graph QL example:
Query:
query ABC(
$x: String!
$y: timestamp!
$z: timestamp!
) {
area
getInTime
getOutTime
}
}
Variable:
{
"x" : "22222",
"y" : "333",
"z" : "4444"
}
Now, is there any way that I could use dynamic variable names instead of the query param names(x,y,z) without changing the param names from the Query.
Thanks.

RethinkDB filter array, return only matched values

I have a table like this
{
dummy: [
"new val",
"new val 2",
"new val 3",
"other",
]
}
want to get only matched values to "new", I am using query like this:
r.db('db').table('table')('dummy').filter(function (val) {
return val.match("^new")
})
but its giving error
e: Expected type STRING but found ARRAY in
what is wrong with query, if I remove .match("^new"), it returns all values
Thanks
The reason of why you're getting Expected type STRING but found ARRAY in is that the value of the dummy field is an array itself, and you cannot apply match to arrays.
Despite the filter you tried may look confusing, you have to rethink your query a bit: just remap the array to a new ^new-keyed array, and then just filter its values out in an inner expression.
For example:
r.db('db')
.table('table')
.getField('dummy')
.map((array) => array.filter((element) => element.match("^new")))
Output:
[
"new val" ,
"new val 2" ,
"new val 3"
]

How do I dynamically name a collection?

Title: How do I dynamically name a collection?
Pseudo-code: collect(n) AS :Label
The primary purpose of this is for easy reading of the properties in the API Server (node application).
Verbose example:
MATCH (user:User)--(n)
WHERE n:Movie OR n:Actor
RETURN user,
CASE
WHEN n:Movie THEN "movies"
WHEN n:Actor THEN "actors"
END as type, collect(n) as :type
Expected output in JSON:
[{
"user": {
....
},
"movies": [
{
"_id": 1987,
"labels": [
"Movie"
],
"properties": {
....
}
}
],
"actors:" [ .... ]
}]
The closest I've gotten is:
[{
"user": {
....
},
"type": "movies",
"collect(n)": [
{
"_id": 1987,
"labels": [
"Movie"
],
"properties": {
....
}
}
]
}]
The goal is to be able to read the JSON result with ease like so:
neo4j.cypher.query(statement, function(err, results) {
for result of results
var user = result.user
var movies = result.movies
}
Edit:
I apologize for any confusion in my inability to correctly name database semantics.
I'm wondering if it's enough just to output the user and their lists of both actors and movies, rather than trying to do a more complicated means of matching and combining both.
MATCH (user:User)
OPTIONAL MATCH (user)--(m:Movie)
OPTIONAL MATCH (user)--(a:Actor)
RETURN user, COLLECT(m) as movies, COLLECT(a) as actors
This query should return each User and his/her related movies and actors (in separate collections):
MATCH (user:User)--(n)
WHERE n:Movie OR n:Actor
RETURN user,
REDUCE(s = {movies:[], actors:[]}, x IN COLLECT(n) |
CASE WHEN x:Movie
THEN {movies: s.movies + x, actors: s.actors}
ELSE {movies: s.movies, actors: s.actors + x}
END) AS types;
As far as a dynamic solution to your question, one that will work with any node connected to your user, there are a few options, but I don't believe you can get the column names to be dynamic like this, or even the names of the collections returned, though we can associate them with the type.
MATCH (user:User)--(n)
WITH user, LABELS(n) as type, COLLECT(n) as nodes
WITH user, {type:type, nodes:nodes} as connectedNodes
RETURN user, COLLECT(connectedNodes) as connectedNodes
Or, if you prefer working with multiple rows, one row each per node type:
MATCH (user:User)--(n)
WITH user, LABELS(n) as type, COLLECT(n) as collection
RETURN user, {type:type, data:collection} as connectedNodes
Note that LABELS(n) returns a list of labels, since nodes can be multi-labeled. If you are guaranteed that every interested node has exactly one label, then you can use the first element of the list rather than the list itself. Just use LABELS(n)[0] instead.
You can dynamically sort nodes by label, and then convert to the map using the apoc library:
WITH ['Actor','Movie'] as LBS
// What are the nodes we need:
MATCH (U:User)--(N) WHERE size(filter(l in labels(N) WHERE l in LBS))>0
WITH U, LBS, N, labels(N) as nls
UNWIND nls as nl
// Combine the nodes on their labels:
WITH U, LBS, N, nl WHERE nl in LBS
WITH U, nl, collect(N) as RELS
WITH U, collect( [nl, RELS] ) as pairs
// Convert pairs "label - values" to the map:
CALL apoc.map.fromPairs(pairs) YIELD value
RETURN U as user, value

Apex Code to retrieve value like vlookup but in Salesforce?

So I have two custom objects: The Workload_Unit_Score__c object is a mapping table, with a reference field (WLU_Combination_Value__c) and a value (Score__c) for each row reference. We receive requests each day to process contractual agreements. A new record is created on the Agreement_Title__c object for each request. Each record is assigned a Workload_Unit_Score__c, based on its WLU_Combination_Value__c.
I basically want to do something similar to an excel vlookup - each time we receive a request and a new Agreement_Title__c record is created, I want a trigger to take the WLU_Combination_Value__c, retreive a Score__c from the Workload_Unit_Score__c object, and populate that value in the Workload_Unit_Score__c field. The two custom objects are not related. Below is a summary of the fields.
-Workload_Unit_Score__c object(sort out like a "definition" or
"reference" table)
Name
MiscField1
MiscField2
MiscField3
WLU_Combination_Value__c (a formula field that concatenates MiscField1 + MiscField2 + MiscField3)
Score__c (a score designated for each unique WLU_Combination_Value__c)
-Agreement_Title__c object(contractual agreements)
Name
MiscField1
MiscField2
MiscField3
WLU_Combination_Value__c (a formula field that concatenates MiscField1 + MiscField2 + MiscField3)
Workload_Unit_Score__c (a score given to each unique WLU_Combination_Value__c)
I have run the code below but I get "Compile Error: expecting right curly bracket, found '' at line 22 column 0" But I think there may be other issues with the code that wont work.
Can someone assist? Is there an easier way to do this?
trigger updateWLUvalue on Agreement_Title__c (before insert) {
Map<String,Agreement_Title__c[]> relatedScores = new Map<String, Agreement_Title__c[]>();
for (Agreement_Title__c agmtt : trigger.new) {
if(!relatedScores.containsKey(agmtt.WLU_Combination_Value__c)){
relatedScores.put(agmtt.WLU_Combination_Value__c, new Agreement_Title__c[]{});
}
relatedScores.get(agmtt.WLU_Combination_Value__C).add(agmtt);
for(Workload_Unit_Score__c wus : [Select Id, Score__c, WLU_Combination_Value__c
FROM Workload_Unit_Score__c
WHERE WLU_Combination_Value__c
IN : relatedScores.keySet()]){
for(Agreement_Title__c agmtt2 : relatedScores.get(wus.WLU_Combination_Value__c)){
agmtt.Workload_Unit_Score__c = wus.Score__c;
}
}
}
First thing is the missing close-curlybracket at the end. Your first IF-statement didn't close.
Patched code:
trigger updateWLUvalue on Agreement_Title__c (before insert) {
Map<String,Agreement_Title__c[]> relatedScores = new Map<String, Agreement_Title__c[]>();
for (Agreement_Title__c agmtt : trigger.new) {
if(!relatedScores.containsKey(agmtt.WLU_Combination_Value__c)) {
relatedScores.put(agmtt.WLU_Combination_Value__c, new Agreement_Title__c[]{});
}
}
relatedScores.get(agmtt.WLU_Combination_Value__C).add(agmtt);
for(Workload_Unit_Score__c wus : [Select Id, Score__c, WLU_Combination_Value__c
FROM Workload_Unit_Score__c
WHERE WLU_Combination_Value__c
IN : relatedScores.keySet()])
{
for(Agreement_Title__c agmtt2 : relatedScores.get(wus.WLU_Combination_Value__c)) {
agmtt.Workload_Unit_Score__c = wus.Score__c;
}
}
}

Select specific field in MongoDB using ruby then pass the value to a variable

I'm working on a script that will return value in a specific field and exclude other fields i tried these codes:
name = 'bierc'
puts collection.find({"name"=> name},{:fields => { "_id" => 0}}).to_a
and
name = 'bierc'
collection.find("name" => name,"_id" => 0).each{|row| puts row.inspect}
These two returns
{"_id"=>BSON::ObjectId('55f0d965fcd4fe1c659cf472'), "name"=>"bierc", "song"=>"testsong"}
I want to select name only and exclude the song and especially the _id then will work on to pass the value of name field to a variable.
The option is not fields but projection. You do need _id => 0 but then 1 for any field or fields you do want to select should exclude the others.
collection.find("name" => name, projection: => { "_id" => 0, "name" => 1}})

Resources