Monday, November 20, 2023

SQL Property Graphs and SQL/PGQ in Oracle Database 23c

Oracle have had a Graph Server and Client product for some time, but in Oracle database 23c some of the property graph functionality has been built directly into the database.

This article focuses on new SQL property graph feature and ignores the previous Graph Server product and PGQL.

Setup


We create a new test user. We add a couple of extra privileges that allow us to do some tracing and display execution plans, but these are not necessary when using SQL property graphs.

conn sys/SysPassword1@//localhost:1521/freepdb1 as sysdba

drop user if exists testuser1 cascade;

create user testuser1 identified by testuser1 quota unlimited on users;
grant db_developer_role to testuser1;

-- Only used for tracing and execution plan examples.
grant alter session to testuser1;
grant select_catalog_role to testuser1;

We need to make sure our test user has the ability to create property graphs.

grant create property graph to testuser1;

This privilege is already part of the RESOURCE role, and will probably be added to the DB_DEVELOPER_ROLE role in the future.

We create a table to hold people, and a table to record the connections between those people.

conn testuser1/testuser1@//localhost:1521/freepdb1

drop table if exists sales purge;
drop table if exists products purge;
drop table if exists connections purge;
drop table if exists people purge;

create table people (
  person_id number primary key,
  name      varchar2(15)
);

insert into people (person_id, name)
values (1, 'Wonder Woman'),
       (2, 'Peter Parker'),
       (3, 'Jean Grey'),
       (4, 'Clark Kent'),
       (5, 'Bruce Banner');
commit;

create table connections (
  connection_id  number primary key,
  person_id_1    number,
  person_id_2    number,
  constraint connections_people_1_fk foreign key (person_id_1) references people (person_id),
  constraint connections_people_2_fk foreign key (person_id_2) references people (person_id)
);

create index connections_person_1_idx on connections (person_id_1);
create index connections_person_2_idx on connections (person_id_2);

insert into connections (connection_id, person_id_1, person_id_2)
values (1,  1, 2),
       (2,  1, 3),
       (3,  1, 4),
       (4,  2, 4),
       (5,  3, 1),
       (6,  3, 4),
       (7,  3, 5),
       (8,  4, 1),
       (9,  5, 1),
       (10, 5, 2),
       (11, 5, 3);
commit;

We create a table to hold products, and a table to hold sales, linked back to the people we defined earlier.

create table products (
  product_id  number primary key,
  name        varchar2(10)
);

insert into products (product_id, name)
values (1, 'apple'),
       (2, 'banana'),
       (3, 'lemon'),
       (4, 'lime');
commit;

create table sales (
  sale_id     number primary key,
  person_id   number,
  product_id  number,
  quantity    number,
  constraint sales_people_fk foreign key (person_id) references people (person_id),
  constraint sales_products_fk foreign key (product_id) references products (product_id)
);

create index sales_person_id_idx on sales (person_id);
create index sales_product_idx on sales (product_id);

insert into sales (sale_id, person_id, product_id, quantity)
values (1, 1, 2, 10),
       (2, 1, 3, 5),
       (3, 2, 2, 6),
       (4, 2, 1, 4),
       (5, 3, 2, 3),
       (6, 3, 1, 6),
       (7, 3, 3, 2),
       (8, 4, 3, 8),
       (9, 5, 2, 6),
       (10, 5, 4, 5),
       (11, 5, 3, 3);
commit;

We gather statistics on all the tables.

exec dbms_stats.gather_table_stats(null, 'people');
exec dbms_stats.gather_table_stats(null, 'connections');
exec dbms_stats.gather_table_stats(null, 'products');
exec dbms_stats.gather_table_stats(null, 'sales');

SQL Property Graphs


A property graph is a model that describes nodes (vertices), and the relationships between them (edges). In the case of SQL property graphs introduced in Oracle database 23c, the vertices and edges are schema objects such as tables, external tables, materialized views or synonyms to these objects. The property graph is stored in the database, and can be referenced by SQL/PGQ queries. There is no data materialized by the SQL property graph, it is just metadata. All the actual data comes from the references objects.

We create a property graph where the PEOPLE table represents the vertices (the points) and the CONNECTIONS table represents the edges (the lines between the points).

--drop property graph connections_pg;

create property graph connections_pg
  vertex tables (
    people
      key (person_id)
      label person
      properties all columns
  )
  edge tables (
    connections
      key (connection_id)
      source key (person_id_1) references people (person_id)
      destination key (person_id_2) references people (person_id)
      label connection
      properties all columns
  );

The LABEL keyword allows us to associate a meaningful name, which we can reference in queries. Notice the edge table has a SOURCE KEY and DESTINATION KEY describing the connection between the vertices.

We create a property graph for the sales of products to people. The vertex tables are PEOPLE and PRODUCTS, and the edge table is SALES.

--drop property graph sales_pg;

create property graph sales_pg
  vertex tables (
    people
      key (person_id)
      label person
      properties all columns,
    products
      key (product_id)
      label product
      properties all columns
  )
  edge tables (
    sales
      key (sale_id)
      source key (person_id) references people (person_id)
      destination key (product_id) references products (product_id)
      label sale
      properties all columns
  );

We display the current property graphs using the USER_PROPERTY_GRAPHS view.

column graph_name format a20
column graph_mode format a10
column allows_mixed_types format a17
column inmemory format a8

select * from user_property_graphs;

GRAPH_NAME               GRAPH_MODE ALLOWS_MIXED_TYPE INMEMORY
--------------------                ----------    ----------------- --------
CONNECTIONS_PG      TRUSTED    NO                NO
SALES_PG                       TRUSTED    NO                NO

SQL>

SQL/PGQ (GRAPH_TABLE)

With the property graphs in place we can use the GRAPH_TABLE operator to query the property graphs using SQL/PGQ. We display people who are connected to each other.

select person1, person2
from   graph_table (connections_pg
         match
         (p1 is person) -[c is connection]-> (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2)
       )
order by 1;

PERSON1         PERSON2
--------------- ---------------
Bruce Banner    Wonder Woman
Bruce Banner    Peter Parker
Bruce Banner    Jean Grey
Clark Kent      Wonder Woman
Jean Grey       Clark Kent
Jean Grey       Wonder Woman
Jean Grey       Bruce Banner
Peter Parker    Clark Kent
Wonder Woman    Clark Kent
Wonder Woman    Peter Parker
Wonder Woman    Jean Grey

11 rows selected.

SQL>

Let's break this query down.

  • We call GRAPH_TABLE passing the property graph.
  • We define the graph element pattern in the MATCH clause using the vertex and edge labels to show our relationship.
  • We define the COLUMNS that will be available in our select list.

The MATCH clause elements can include a WHERE clause to limit the rows, and of course we can include a WHERE clause in the main body of the query.

select person1, person2
from   graph_table (connections_pg
         match
         (p1 is person where p1.name = 'Jean Grey') -[c is connection]-> (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2)
       )
order by 1;

PERSON1         PERSON2
--------------- ---------------
Jean Grey       Wonder Woman
Jean Grey       Clark Kent
Jean Grey       Bruce Banner

SQL>

select person1, person2
from   graph_table (connections_pg
         match
         (p1 is person where p1.name = 'Jean Grey') -[c is connection]-> (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2)
       )
where  person2 = 'Clark Kent'
order by 1;

PERSON1               PERSON2
--------------------- ---------------------
Jean Grey             Clark Kent

SQL>

To display the graph using Oracle Graph Visualization we need to include the VERTEX_ID and EDGE_ID functions to identify the IDs of the vertices and edges. These produce a fully qualified description of the element. In the following example we return a single row containing these values.

column id_p1 format a20
column id_c format a20
column id_p2 format a20
set linesize 200 pagesize 100

select person1, person2, id_p1, id_c, id_p2
from   graph_table (connections_pg
         match
         (p1 is person) -[c is connection]-> (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  vertex_id(p1) AS id_p1,
                  edge_id(c) AS id_c,
                  vertex_id(p2) AS id_p2)
       )
where  rownum = 1
order by 1;

PERSON1         PERSON2         ID_P1                ID_C                 ID_P2
--------------- --------------- -------------------- -------------------- --------------------
Wonder Woman    Peter Parker    {"GRAPH_OWNER":"TEST {"GRAPH_OWNER":"TEST {"GRAPH_OWNER":"TEST
                                USER1","GRAPH_NAME": USER1","GRAPH_NAME": USER1","GRAPH_NAME":
                                "CONNECTIONS_PG","EL "CONNECTIONS_PG","EL "CONNECTIONS_PG","EL
                                EM_TABLE":"PEOPLE"," EM_TABLE":"CONNECTIO EM_TABLE":"PEOPLE","
                                KEY_VALUE":{"PERSON_ NS","KEY_VALUE":{"CO KEY_VALUE":{"PERSON_
                                ID":1}}              NNECTION_ID":1}}     ID":2}}


SQL>

This example uses our second property graph, and allows us to see people who are related by purchasing the same items.

select person1, product, person2
from   graph_table (sales_pg
         match
         (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is sale]- (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  pr.name as product)
       )
where  person1 != person2
order by 1;

PERSON1         PRODUCT    PERSON2
--------------- ---------- ---------------
Bruce Banner    lemon      Jean Grey
Bruce Banner    banana     Jean Grey
Bruce Banner    banana     Peter Parker
Bruce Banner    lemon      Clark Kent
Bruce Banner    banana     Wonder Woman
Bruce Banner    lemon      Wonder Woman
Clark Kent      lemon      Bruce Banner
Clark Kent      lemon      Jean Grey
Clark Kent      lemon      Wonder Woman
Jean Grey       banana     Peter Parker
Jean Grey       apple      Peter Parker
Jean Grey       lemon      Wonder Woman
Jean Grey       banana     Wonder Woman
Jean Grey       lemon      Bruce Banner
Jean Grey       banana     Bruce Banner
Jean Grey       lemon      Clark Kent
Peter Parker    banana     Jean Grey
Peter Parker    banana     Wonder Woman
Peter Parker    apple      Jean Grey
Peter Parker    banana     Bruce Banner
Wonder Woman    banana     Jean Grey
Wonder Woman    lemon      Jean Grey
Wonder Woman    lemon      Bruce Banner
Wonder Woman    lemon      Clark Kent
Wonder Woman    banana     Peter Parker
Wonder Woman    banana     Bruce Banner

26 rows selected.

SQL>

As before, we need to include the IDs of the vertices and edges if we want to visualize the graph.

select person1, product, person2, id_p1, id_s1, id_pr, id_s2, id_p2
from   graph_table (sales_pg
         match
         (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is sale]- (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  pr.name as product,
                  vertex_id(p1) AS id_p1,
                  edge_id(s1) AS id_s1,
                  vertex_id(pr) AS id_pr,
                  edge_id(s2) AS id_s2,
                  vertex_id(p2) AS id_p2)
       )
where  person1 != person2
order by 1;

Oracle Graph Visualization


There are several ways to deploy the Oracle Graph Visualization tool. In this case we deployed it using Tomcat.

Click on the "Oracle Graph Client" link here, and select the architecture you require. In this case we downloaded "Oracle Graph Webapps 23.2.0 for (Linux x86-64)". The SQL/PGQ support was added to the visualizer in version 23.1, so make sure you are not using an older version.

On the Tomcat server we did the following.

cd /tmp
unzip -oq V1033696-01-linux-webapps.zip
unzip -oq oracle-graph-webapps-23.1.0.zip
cp graphviz-23.2.0-tomcat.war $CATALINA_BASE/webapps/

We were then able to access the visualizer on the following URL.

https://localhost:8443/graphviz-23.2.0-tomcat

We log in with the following details.

Username: testuser1
Password: testuser1
Advanced > Database: jdbc:oracle:thin:@//localhost:1521/freepdb1

Oracle Graph Visualization creates tables, materialized views and views when you run a query. They all have the prefix "$GRAPHVIZ_TEMP_TABLE_". Make sure the user you are connecting to has the privilege to do that.

We click on the "SQL/PGQ" tab and place the following query into the visualizer. Notice it is missing the trailing ";". This query displays the connections between people.

select id_p1, id_c, id_p2
from   graph_table (connections_pg
         match
         (p1 is person) -[c is connection]-> (p2 is person)
         columns (vertex_id(p1) AS id_p1,
                  edge_id(c) AS id_c,
                  vertex_id(p2) AS id_p2)
       )

When we run it we get the following output.

SQL Property Graphs and SQL/PGQ in Oracle Database 23c

We click the settings (cog) button, click the "Customizations" tab, and set the "Vertex Label" to the NAME column.

SQL Property Graphs and SQL/PGQ in Oracle Database 23c

Once we click "OK" we see the graph with the labels.

SQL Property Graphs and SQL/PGQ in Oracle Database 23c

We use the following query against our second property graph to shows the people who have purchased the same products.

select id_p1, id_s1, id_pr, id_s2, id_p2
from   graph_table (sales_pg
         match
         (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is sale]- (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  vertex_id(p1) AS id_p1,
                  edge_id(s1) AS id_s1,
                  vertex_id(pr) AS id_pr,
                  edge_id(s2) AS id_s2,
                  vertex_id(p2) AS id_p2)
       )
where  person1 != person2

We run the query. In the "Customizations" tab we set the "Vertex Label" to NAME and the "Edge Label" to QUANTITY.

SQL Property Graphs and SQL/PGQ in Oracle Database 23c

Not only can we see the relationship between the people and the products, but we can also see the quantity of the products purchased on the edge labels.

Query Transformation


Let's see what happens behind the scenes when we use SQL/PGQ.

First we flush the shared pool and identify the trace file that will be created for our new session.

conn sys/SysPassword1@//localhost:1521/freepdb1 as sysdba

alter system flush shared_pool;

conn testuser1/testuser1@//localhost:1521/freepdb1

set linesize 100
column value format a65

select value
from   v$diag_info
where  name = 'Default Trace File';

VALUE
-----------------------------------------------------------------
/opt/oracle/diag/rdbms/free/FREE/trace/FREE_ora_14020.trc

SQL>

Now we do a 10053 trace of the statement.

alter session set events '10053 trace name context forever';

select person1, product, person2
from   graph_table (sales_pg
         match
         (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is sale]- (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  pr.name as product)
       )
where  person1 != person2
order by 1;

alter session set events '10053 trace name context off';

We check the resulting trace file, searching for the section beginning with "Final query after transformations", and we see the following statement.

Final query after transformations:******* UNPARSED QUERY IS *******
SELECT "P1"."NAME" "PERSON1",
       "PR"."NAME" "PRODUCT",
       "P2"."NAME" "PERSON2"
FROM   "TESTUSER1"."PEOPLE" "P1",
       "TESTUSER1"."SALES" "S1",
       "TESTUSER1"."PRODUCTS" "PR",
       "TESTUSER1"."SALES" "S2",
       "TESTUSER1"."PEOPLE" "P2"
WHERE  "P1"."NAME"<>"P2"."NAME"
AND    "P1"."PERSON_ID"="S1"."PERSON_ID"
AND    "PR"."PRODUCT_ID"="S1"."PRODUCT_ID"
AND    "P2"."PERSON_ID"="S2"."PERSON_ID"
AND    "PR"."PRODUCT_ID"="S2"."PRODUCT_ID"
ORDER BY "P1"."NAME"

The statement has been transformed to convert the SQL/PGQ into regular joins. In this example there is not a great deal of difference in complexity between the SQL/PGQ and the transformed SQL statement, but in some scenarios the SQL/PGQ can massively simplify the joins and unions necessary to return the data.

Since the SQL/PGQ is converted to regular SQL, it's possible to display the execution plan in the normal way.

select person1, product, person2
from   graph_table (sales_pg
         match
         (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is sale]- (p2 is person)
         columns (p1.name as person1,
                  p2.name as person2,
                  pr.name as product)
       )
where  person1 != person2
order by 1;

select * from table(dbms_xplan.display_cursor(format=>'ALL'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------
SQL_ID  2j4yv6j0gks9y, child number 0
-------------------------------------
select person1, product, person2 from   graph_table (sales_pg
match          (p1 is person) -[s1 is sale]-> (pr is product) <-[s2 is
sale]- (p2 is person)          columns (p1.name as person1,
      p2.name as person2,                   pr.name as product)
) where  person1 != person2 order by 1

Plan hash value: 3808001794

--------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                     |       |       |    14 (100)|          |
|   1 |  SORT ORDER BY                   |                     |    24 |  1224 |    14  (15)| 00:00:01 |
|*  2 |   HASH JOIN                      |                     |    24 |  1224 |    13   (8)| 00:00:01 |
|*  3 |    HASH JOIN                     |                     |    30 |  1080 |    10  (10)| 00:00:01 |
|*  4 |     HASH JOIN                    |                     |    11 |   330 |     8  (13)| 00:00:01 |
|   5 |      MERGE JOIN                  |                     |    11 |   165 |     5  (20)| 00:00:01 |
|   6 |       TABLE ACCESS BY INDEX ROWID| PRODUCTS            |     4 |    36 |     2   (0)| 00:00:01 |
|   7 |        INDEX FULL SCAN           | SYS_C0012556        |     4 |       |     1   (0)| 00:00:01 |
|*  8 |       SORT JOIN                  |                     |    11 |    66 |     3  (34)| 00:00:01 |
|   9 |        VIEW                      | index$_join$_003    |    11 |    66 |     2   (0)| 00:00:01 |
|* 10 |         HASH JOIN                |                     |       |       |            |          |
|  11 |          INDEX FAST FULL SCAN    | SALES_PERSON_ID_IDX |    11 |    66 |     1   (0)| 00:00:01 |
|  12 |          INDEX FAST FULL SCAN    | SALES_PRODUCT_IDX   |    11 |    66 |     1   (0)| 00:00:01 |
|  13 |      TABLE ACCESS FULL           | PEOPLE              |     5 |    75 |     3   (0)| 00:00:01 |
|  14 |     VIEW                         | index$_join$_005    |    11 |    66 |     2   (0)| 00:00:01 |
|* 15 |      HASH JOIN                   |                     |       |       |            |          |
|  16 |       INDEX FAST FULL SCAN       | SALES_PERSON_ID_IDX |    11 |    66 |     1   (0)| 00:00:01 |
|  17 |       INDEX FAST FULL SCAN       | SALES_PRODUCT_IDX   |    11 |    66 |     1   (0)| 00:00:01 |
|  18 |    TABLE ACCESS FULL             | PEOPLE              |     5 |    75 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$E6D2A9F4
   6 - SEL$E6D2A9F4 / "PR"@"SEL$88741CE7"
   7 - SEL$E6D2A9F4 / "PR"@"SEL$88741CE7"
   9 - SEL$2237A74B / "S1"@"SEL$88741CE7"
  10 - SEL$2237A74B
  11 - SEL$2237A74B / "indexjoin$_alias$_001"@"SEL$2237A74B"
  12 - SEL$2237A74B / "indexjoin$_alias$_002"@"SEL$2237A74B"
  13 - SEL$E6D2A9F4 / "P1"@"SEL$88741CE7"
  14 - SEL$37BF1F62 / "S2"@"SEL$88741CE7"
  15 - SEL$37BF1F62
  16 - SEL$37BF1F62 / "indexjoin$_alias$_001"@"SEL$37BF1F62"
  17 - SEL$37BF1F62 / "indexjoin$_alias$_002"@"SEL$37BF1F62"
  18 - SEL$E6D2A9F4 / "P2"@"SEL$88741CE7"

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("P2"."PERSON_ID"="S2"."PERSON_ID")
       filter("P1"."NAME"<>"P2"."NAME")
   3 - access("PR"."PRODUCT_ID"="S2"."PRODUCT_ID")
   4 - access("P1"."PERSON_ID"="S1"."PERSON_ID")
   8 - access("PR"."PRODUCT_ID"="S1"."PRODUCT_ID")
       filter("PR"."PRODUCT_ID"="S1"."PRODUCT_ID")
  10 - access(ROWID=ROWID)
  15 - access(ROWID=ROWID)

Column Projection Information (identified by operation id):
-----------------------------------------------------------

   1 - (#keys=1) "P1"."NAME"[VARCHAR2,15], "PR"."NAME"[VARCHAR2,10], "P2"."NAME"[VARCHAR2,15]
   2 - (#keys=1) "P1"."NAME"[VARCHAR2,15], "PR"."NAME"[VARCHAR2,10], "P1"."NAME"[VARCHAR2,15],
       "P2"."NAME"[VARCHAR2,15], "P2"."NAME"[VARCHAR2,15]
   3 - (#keys=1) "P1"."NAME"[VARCHAR2,15], "PR"."NAME"[VARCHAR2,10], "P1"."NAME"[VARCHAR2,15],
       "S2"."PERSON_ID"[NUMBER,22]
   4 - (#keys=1) "PR"."PRODUCT_ID"[NUMBER,22], "PR"."NAME"[VARCHAR2,10],
       "P1"."NAME"[VARCHAR2,15], "P1"."NAME"[VARCHAR2,15]
   5 - "PR"."PRODUCT_ID"[NUMBER,22], "PR"."NAME"[VARCHAR2,10], "S1"."PERSON_ID"[NUMBER,22]
   6 - "PR"."PRODUCT_ID"[NUMBER,22], "PR"."NAME"[VARCHAR2,10]
   7 - "PR".ROWID[ROWID,10], "PR"."PRODUCT_ID"[NUMBER,22]
   8 - (#keys=1) "S1"."PRODUCT_ID"[NUMBER,22], "S1"."PERSON_ID"[NUMBER,22]
   9 - "S1"."PRODUCT_ID"[NUMBER,22], "S1"."PERSON_ID"[NUMBER,22]
  10 - (#keys=1) "S1"."PERSON_ID"[NUMBER,22], "S1"."PRODUCT_ID"[NUMBER,22]
  11 - ROWID[ROWID,10], "S1"."PERSON_ID"[NUMBER,22]
  12 - ROWID[ROWID,10], "S1"."PRODUCT_ID"[NUMBER,22]
  13 - "P1"."PERSON_ID"[NUMBER,22], "P1"."NAME"[VARCHAR2,15]
  14 - "S2"."PRODUCT_ID"[NUMBER,22], "S2"."PERSON_ID"[NUMBER,22]
  15 - (#keys=1) "S2"."PERSON_ID"[NUMBER,22], "S2"."PRODUCT_ID"[NUMBER,22]
  16 - ROWID[ROWID,10], "S2"."PERSON_ID"[NUMBER,22]
  17 - ROWID[ROWID,10], "S2"."PRODUCT_ID"[NUMBER,22]
  18 - "P2"."PERSON_ID"[NUMBER,22], "P2"."NAME"[VARCHAR2,15]

Note
-----
   - this is an adaptive plan

92 rows selected.

SQL>

JSON Support


In the previous examples we used PROPERTIES ALL COLUMNS to keep things simple. We could have limited the properties to specific table columns using a comma-separated list, as shown below.

--drop property graph connections_pg;

create property graph connections_pg
  vertex tables (
    people
      key (person_id)
      label person
      properties (name)
  )
  edge tables (
    connections
      key (connection_id)
      source key (person_id_1) references people (person_id)
      destination key (person_id_2) references people (person_id)
      label connection
      properties (person_id_1, person_id_2)
  );

Where we have columns containing JSON data, we can drill down into the JSON data using dot notation, or SQL/JSON function calls to define properties.

We add a JSON column to the PEOPLE table and populate it with some JSON data.

alter table people add (json_data json);

update people
set    json_data = json('{"gender":"female", "universe":"DC"}')
where  person_id = 1;

update people
set    json_data = json('{"gender":"male", "universe":"Marvel"}')
where  person_id = 2;

update people
set    json_data = json('{"gender":"female", "universe":"DC"}')
where  person_id = 3;

update people
set    json_data = json('{"gender":"male", "universe":"Marvel"}')
where  person_id = 4;

update people
set    json_data = json('{"gender":"male", "universe":"Marvel"}')
where  person_id = 5;

commit;

We recreate the property graph including the two new properties using an example of dot notation and a JSON_VALUE call.

drop property graph connections_pg;

create property graph connections_pg
  vertex tables (
    people
      key (person_id)
      label person
      properties (name,
                  people.json_data.gender.string() as gender,
                  json_value(people.json_data, '$.universe') as universe)
  )
  edge tables (
    connections
      key (connection_id)
      source key (person_id_1) references people (person_id)
      destination key (person_id_2) references people (person_id)
      label connection
      properties (person_id_1, person_id_2)
  );

We project the new properties as columns and add them into our select list.

column gender1 format a10
column gender2 format a10
column universe1 format a10
column universe2 format a10

select person1, gender1, universe1, person2, gender2, universe2
from   graph_table (connections_pg
         match
         (p1 is person) -[c is connection]-> (p2 is person)
         columns (p1.name as person1,
                  p1.gender as gender1,
                  p1.universe as universe1,
                  p2.name as person2,
                  p2.gender as gender2,
                  p2.universe as universe2)
       )
order by 1;

PERSON1         GENDER1    UNIVERSE1  PERSON2         GENDER2    UNIVERSE2
--------------- ---------- ---------- --------------- ---------- ----------
Bruce Banner    male       Marvel     Peter Parker    male       Marvel
Bruce Banner    male       Marvel     Wonder Woman    female     DC
Bruce Banner    male       Marvel     Jean Grey       female     DC
Clark Kent      male       Marvel     Wonder Woman    female     DC
Jean Grey       female     DC         Wonder Woman    female     DC
Jean Grey       female     DC         Clark Kent      male       Marvel
Jean Grey       female     DC         Bruce Banner    male       Marvel
Peter Parker    male       Marvel     Clark Kent      male       Marvel
Wonder Woman    female     DC         Clark Kent      male       Marvel
Wonder Woman    female     DC         Jean Grey       female     DC
Wonder Woman    female     DC         Peter Parker    male       Marvel

11 rows selected.

SQL>

Related Posts

0 comments:

Post a Comment