How can we create undirected edge between two nodes in apache age graph database - graph-databases

How do I connect undirected edge between these 2 nodes as follow :
SELECT * from cypher(
'munmud_graph',
$$
CREATE (v:Country {"name" : "Bangladesh"})
RETURN v
$$
) as (v agtype);
SELECT * from cypher(
'munmud_graph',
$$
CREATE (v:Country {"name" : "India"})
RETURN v
$$
) as (v agtype);
I want to connect these two node with an undirected edge with label Neighbour

Creating and storing of undirected edges are is supported, although you can read/query-for edges and ignore their direction.
To create the edge, you would do something like the following:
SELECT * from cypher(
'munmud_graph',
$$
MATCH
(x:Country),
(y:Country)
WHERE x.name = 'Bangladesh' AND y.name = 'India'
CREATE (x)-[r:Neighbour]->(y)
RETURN r
$$
) as (r agtype);
And then to query for the edge and ignore its direction, you would do:
SELECT * from cypher(
'munmud_graph',
$$
MATCH (x:Country)-[r]-(y:Country)
WHERE x.name = 'Bangladesh' AND y.name = 'India'
RETURN r
$$
) as (r agtype);

I believe that, as of today, the only way to do this with Apache AGE is to set the edge with a property that resembles this undirection or set it as bidirectional.
Considering that, both of the following examples doesn't work:
-- EXAMPLE 1 : UNDIRECTED EDGE (DOES NOT WORK)
SELECT * FROM cypher ('demo', $$
MATCH (a:Country), (b:Country)
WHERE a.name = "India" AND b.name = "Bangladesh"
CREATE (a)-[e:BORDERS_WITH]-(b)
RETURN e
$$) as (e agtype);
ERROR: only directed relationships are allowed in CREATE
LINE 4: CREATE (a)-[e:BORDERS_WITH]-(b)
-- EXAMPLE 2 : BIDIRECTIONAL EDGE (DOES NOT WORK)
SELECT * FROM cypher ('demo', $$
MATCH (a:Country), (b:Country)
WHERE a.name = "India" AND b.name = "Bangladesh"
CREATE (a)<-[e:BORDERS_WITH]->(b)
RETURN e
$$) as (e agtype);
ERROR: syntax error at or near ">"
LINE 4: CREATE (a)<-[e:BORDERS_WITH]->(b)
But this works:
-- EXAMPLE 3 : SET BIDIRECTIONAL EDGE AS PROPERTY (WORKS)
SELECT * FROM cypher ('demo', $$
MATCH (a:Country), (b:Country)
WHERE a.name = "India" AND b.name = "Bangladesh"
CREATE (a)-[e:BORDERS_WITH{ type:"<->" }]->(b)
RETURN e
$$) as (e agtype);
e
--------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 2533274790395905, "label": "BORDERS_WITH", "end_id": 2251799813685249, "start_id": 2251799813685250, "properties": {"type": "<->"}}::edge
(1 row)
demo=#
Then, if you want to find which edges are of type "<->" you just have to type this query:
SELECT * FROM cypher ('demo', $$
MATCH (a)-[e:BORDERS_WITH]->(b)
WHERE e.type = "<->"
RETURN e
$$) as (e agtype);
e
--------------------------------------------------------------------------------------------------------------------------------------------------
{"id": 2533274790395905, "label": "BORDERS_WITH", "end_id": 2251799813685249, "start_id": 2251799813685250, "properties": {"type": "<->"}}::edge
(1 row)

What you can do is create a directed Relationship between the nodes and you would have an edge both ways, e.g :
I run the command:
And now when you Display the edges it would show you both countries:

SELECT * from cypher(
'munmud_graph',
$$
MATCH (a:Country {name: "Bangladesh"}), (b:Country {name: "India"})
CREATE (a)-[:Neighbour]->(b)
RETURN a, b
$$
) as (a, b);
Hope this will work for you

Related

Postgresql how to select a value from multiple jsons inside a array on a jsonB column

I have this table
create table <table_name>(attr jsonb)
And this is the data inside
{
"rules": [
{
"id": "foo",
"name": "test_01",
...
},
{
"id": "bar",
"name": "test_02",
...
}
]
}
What I want is to select both names, what I have accomplished so far is this
select attr -> 'rules' -> 0 -> 'name' from <table_name>;
which returns test_01
select attr -> 'rules' -> 1 -> 'name' from <table_name>;
which returns test_02
I want to return something like this:
test_01,test_02
or if it's possible to return them in multiple lines, that would be even better
This is a sample data to show my problem, for reasons beyond my control, it's not possible to store each rule on a distinct line
You can use jsonb_array_length together with generate_series to get each name. Then use string_agg to aggregate list of names. Without plpgsql and with a single statement. (see demo)
with jl(counter) as ( select jsonb_array_length(attr->'rules') from table_name )
select string_agg(name,' ') "Rule Names"
from (select attr->'rules'-> n ->> 'name' name
from table_name
cross join ( select generate_series(0,counter-1) from jl ) gs(n)
) rn;
if anyone else get stuck on a situation like this, this is the solution the I found
create or replace function func_get_name() RETURNS text
language 'plpgsql'
AS $$
declare
len character varying(255);
names character varying(255);
res character varying(255);
begin
select jsonb_array_length(attr->'rules') into len from <table_name>;
res := '';
for counter in 0..len loop
select attr->'rules'-> counter ->> 'name'
into names
from <table_name>;
if names is not null then
res := res || ' ' || names;
end if;
end loop;
return res;
end;
$$
select func_get_name();
it's a solution: yes, it's a good solution: I have no ideia

Is it possible to UNION select from 2 tables?

Is it possible to do something like a sql UNION select with pact or select a common row from 2 separate tables returning a result?
For example we have 2 tables, and we want to select row ("Bob") from both tables (ages, favorite-food), and return a single object composed of data (age, food) from both tables .
;Table 1 schema
(defschema table-schema
age:decimal)
;Table 1
(deftable ages:{supply})
;Table 2 schema
(defschema table-schema
food:string)
;Table 2
(deftable favorite-food:{supply})
In such a case, a single function which would return an object containing Bob's age and his favorite food.
Is such a query possible in pact that serves this purpose?
Thank you kitty_kad I originally did mean to ask about a use case scenario which takes a list as input. Here is what I came up with just in case anyone else needs a use case with a list instead of just a single key:
(defschema user-schema
account:string
petId:string
balance:decimal
)
(deftable users:{user-schema})
(defschema pets-schema
petId:string
owner:string
petType:string
)
(deftable pets:{pets-schema})
(defun get-user-pet-details
( account:string )
(let ((x (select users['petId]
(and? (where 'account (= account))
(where 'balance (< 0.0))))))
(map (get-pet-details) x))
)
(defun get-pet-details
( x:object{pets-schema} )
(bind x { "petId" := id }
(with-read pets id
{ "petId" := pid
, "owner" := powner
, "petType" := ptype
}
{
"ID": pid
, "OWNER": powner
, "TYPE": ptype
} )
)
)
You can use let and read together
(let (
(age (at "age" (read ages "Bob" ["age"])) )
(food (at "food" (read favourite-food "Bob" ["food"])) )
)
{"age":age, "food" food}
)

Is any way to use more than one vertex set in a SELECT statement?

I was wondering if there is any way to use more than one vertex set in a SELECT statement.
I would think it should be possible because... why not?
For example, say we have this basic query:
CREATE QUERY coolQuery(VERTEX<Foo> foo, String bar, String biz) FOR GRAPH cool_graph SYNTAX v2 {
f = {foo};
x = SELECT i
FROM SomeVertex:i -(PathType1>)- f
y = SELECT i
FROM x:i -(<PathType2)- BarVertex:br
WHERE br.id == bar;
z = SELECT i
FROM y:i -(PathType3>.PathType4>)- BizVertex:bz
WHERE bz.id == biz;
PRINT z;
}
Now, that's all fine and dandy, but what if I know the other vertices whose ids are bar and biz?
Can I use more than one known vertex set in a SELECT statement?
The goal here is to arrive to the final SomeVertex set as quickly as possible via using the indexed vertex id values.
This is what I'm thinking:
CREATE QUERY coolQuery2(VERTEX<FooVertex> foo, VERTEX<BarVertex> bar, Vertex<BizVertex> biz) FOR GRAPH cool_graph SYNTAX v2 {
f = {foo};
br = {bar};
bz = {biz};
x = SELECT i
FROM SomeVertex:i -(PathType1>)- f
y = SELECT i
FROM x:i -(<PathType2)- br
z = SELECT i
FROM y:i -(PathType3>.PathType4>)- bz
PRINT z;
}
I get syntax errors with this and I can't find anything in the docs that does this type
of thing where more than one known vertex set is used in a SELECT statement.
in your case, it is recommended to write the query this way:
Version 1:
CREATE QUERY coolQuery2(VERTEX<FooVertex> foo, VERTEX<BarVertex> bar, Vertex<BizVertex> biz) FOR GRAPH cool_graph SYNTAX v2 {
OrAccum<BOOL> #hasFoo, #hasBar, #hasBiz;
f = {foo, bar, biz};
result = select t from f:s-((PathType1|PathType2|PathType3):e)-:t
accum case when s == foo then t.#hasFoo += true end,
case when s == bar then t.#hasBar += true end,
case when s == biz then t.#hasBiz += true end
having t.#hasFoo and t.#hasBar and t.#hasBiz;
print result;
}
Version 2:
CREATE QUERY coolQuery2(VERTEX<FooVertex> foo, VERTEX<BarVertex> bar, Vertex<BizVertex> biz) FOR GRAPH cool_graph SYNTAX v2 {
OrAccum<BOOL> #hasFoo, #hasBar, #hasBiz;
f = {foo};
br = {bar};
bz = {biz};
fooSet = select t from f-(PathType1)-:t;
barSet = select t from br-(PathType1)-:t;
bizSet = select t from bz-(PathType1)-:t;
result = fooSet intersect barSet intersect bizSet;
print result;
}
In this case version, 1 is more recommended since it has better concurrency and only does only one SELECT.

How to update JSON array with PostgreSQL

I have the following inconvenience, I want to update a key of an JSON array using only PostgreSQL. I have the following json:
[
{
"ch":"1",
"id":"12",
"area":"0",
"level":"Superficial",
"width":"",
"length":"",
"othern":"5",
"percent":"100",
"location":" 2nd finger base"
},
{
"ch":"1",
"id":"13",
"area":"0",
"level":"Skin",
"width":"",
"length":"",
"othern":"1",
"percent":"100",
"location":" Abdomen "
}
]
I need to update the "othern" to another number if the "othern" = X
(X is any number that I pass to the query. Example, update othern if othern = 5).
This JSON can be much bigger, so I need something that can iterate in the JSON array and find all the "othern" that match X number and replace with the new one. Thank you!
I have tried with these functions json of Postgresql, but I do not give with the correct result:
SELECT * FROM jsonb_to_recordset('[{"ch":"1", "id":"12", "area":"0", "level":"Superficial", "width":"", "length":"", "othern":"5", "percent":"100", "location":" 2nd finger base"}, {"ch":"1", "id":"13", "area":"0", "level":"Skin", "width":"", "length":"", "othern":"1", "percent":"100", "location":" Abdomen "}]'::jsonb)
AS t (othern text);
I found this function in SQL that is similar to what I need but honestly SQL is not my strength:
CREATE OR REPLACE FUNCTION "json_array_update_index"(
"json" json,
"index_to_update" INTEGER,
"value_to_update" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('[', string_agg("element"::text, ','), ']')::json
FROM (SELECT CASE row_number() OVER () - 1
WHEN "index_to_update" THEN to_json("value_to_update")
ELSE "element"
END "element"
FROM json_array_elements("json") AS "element") AS "elements"
$function$;
UPDATE plan_base
SET atts = json_array_update_index([{"ch":"1", "id":"12", "area":"0", "level":"Superficial", "width":"", "length":"", "othern":"5", "percent":"100", "location":" 2nd finger base"}, {"ch":"1", "id":"13", "area":"0", "level":"Skin", "width":"", "length":"", "othern":"1", "percent":"100", "location":" Abdomen "}], '{"othern"}', '{"othern":"71"}'::json)
WHERE id = 2;
The function you provided changes a JSON input, gives out the changed JSON and updates a table parallel.
For a simple update, you don't need a function:
demo:db<>fiddle
UPDATE mytable
SET myjson = s.json_array
FROM (
SELECT
jsonb_agg(
CASE WHEN elems ->> 'othern' = '5' THEN
jsonb_set(elems, '{othern}', '"7"')
ELSE elems END
) as json_array
FROM
mytable,
jsonb_array_elements(myjson) elems
) s
jsonb_array_elements() expands the array into one row per element
jsonb_set() changes the value of each othern field. The relevant JSON objects can be found with a CASE clause
jsonb_agg() reaggregates the elements into an array again.
This array can be used to update your column.
If you really need a function which gets the parameters and returns the changed JSON, then this could be a solution. Of course, this doesn't execute an update. I am not quite sure if you want to achieve this:
demo:db<>fiddle
CREATE OR REPLACE FUNCTION json_array_update_index(_myjson jsonb, _val_to_change int, _dest_val int)
RETURNS jsonb
AS $$
DECLARE
_json_output jsonb;
BEGIN
SELECT
jsonb_agg(
CASE WHEN elems ->> 'othern' = _val_to_change::text THEN
jsonb_set(elems, '{othern}', _dest_val::text::jsonb)
ELSE elems END
) as json_array
FROM
jsonb_array_elements(_myjson) elems
INTO _json_output;
RETURN _json_output;
END;
$$ LANGUAGE 'plpgsql';
If you want to combine both as you did in your question, of course, you can do this:
demo:db<>fiddle
CREATE OR REPLACE FUNCTION json_array_update_index(_myjson jsonb, _val_to_change int, _dest_val int)
RETURNS jsonb
AS $$
DECLARE
_json_output jsonb;
BEGIN
UPDATE mytable
SET myjson = s.json_array
FROM (
SELECT
jsonb_agg(
CASE WHEN elems ->> 'othern' = '5' THEN
jsonb_set(elems, '{othern}', '"7"')
ELSE elems END
) as json_array
FROM
mytable,
jsonb_array_elements(myjson) elems
) s
RETURNING myjson INTO _json_output;
RETURN _json_output;
END;
$$ LANGUAGE 'plpgsql';

Can Any one help me to convert this sql query into linq

I need to convert this SQL Query to Link :
"Select * FROM [Register]
where RegisterId IN (SELECT MyId
FROM Friends
WHERE FriendId='" + Session["CurrentProfileId"] + "'
AND Status=1
UNION
SELECT FriendId
FROM Friends
WHERE MyId='" + Session["CurrentProfileId"] + "'
AND Status=1) ";
It may be look like this??? but this is incorrect and having errors
(from u in db.Register
where RegisterId).Contains
(from f in db.Freinds
where f.MyId == Id && m.Status == 1
select new { m.MyId })
.Union(from m in db.Freinds
where f.FreindId == Id && m.Status == 1
select new { m.CreateDate } ));
You have a few problems with the linq above and here are a few:
In the query in the Union you select the CreateDate whereas in the top on you select the MyId. I assume you meant to select FreindId.
In these 2 queries you create an anonymous class instance with the field but then compare it to the RegisterId which is probably a guid/string/int - but for sure not of the type you just created.
You are using the Contains method wrong. Linq syntax can be similar to sql but it is not the same. Check here for Contains
The correct Linq way of doing it is:
var idsCollection = ((from f in db.Freinds
where f.StatusId == 1 && f.MyId == Id
select f.MyId)
.Union(from m in db.Friends
where m.StatusId == 1 && f.FreindId == Id
select m.FriendId)).ToList();
var result = (from u in db.Register
where idsCollection.Contains(u.RegisterId)
select u).ToList();
Notice that the .ToList() is not a must and is here just to ease in debugging. For more information about this .ToList() and Linq in general check MSDN

Resources