10.4 Loading a Subgraph from a PGQL Property Graph

You can create a subgraph from a PGQL property graph and load it into memory in the graph server (PGX).

Instead of loading a full graph into memory, you can load a subgraph. This would consume less memory.

The following sections explain in detail on loading and expanding of subgraphs:

10.4.1 PGQL Based Subgraph Loading

You can use the PgViewSubgraphReader#fromPgPgql API to create an in-memory subgraph from a PGQL property graph using a set of PGQL queries.

These PGQL queries define the vertices and edges that are to be loaded into the subgraph. You can also use multiple PGQL queries and the resulting output graph is a union of the subgraphs, each being loaded independently by each PGQL query.

Note:

  • Only non-composite vertex and edge keys are supported.
  • Only numeric edge keys are supported.
  • PGQL queries with GROUP BY or ORDER BY clauses are not supported for loading of subgraphs from a PGQL property graph.

The following example creates a subgraph from a PGQL property graph using multiple PGQL queries:

opg4j> var graph = session.readSubgraph().
...>                    fromPgPgql("FRIENDS").
...>                    queryPgql("MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'").
...>                    queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'").
...>                    load()
graph ==> PgxGraph[name=FRIENDS,N=3,E=1,created=1646726883194]
PgxGraph graph = session.readSubgraph()
                       .fromPgPgql("FRIENDS")
                       .queryPgql("MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'")
                       .queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'")
                       .load();
>>> graph = session.read_subgraph_from_pg_pgql("FRIENDS", ["MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'",
...                           "MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'"])
>>> graph
PgxGraph(name: FRIENDS, v: 3, e: 1, directed: True, memory(Mb): 0)

The following displays the output for the preceding PGQL query using the graph visualization tool.

Figure 10-1 Subgraph Visualization

Description of Figure 10-1 follows
Description of "Figure 10-1 Subgraph Visualization"

Loading Subgraphs with Custom Names

By default, the new subgraph gets created with the same name as the PGQL property graph. Alternatively, if you want to load a subgraph with a custom name, then you can configure the subgraph name as shown:

opg4j> var graph = session.readSubgraph().
...>                  fromPgPgql("FRIENDS").
...>                  queryPgql("MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'").
...>                  queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'").
...>                  load("friends_network")
graph ==> PgxGraph[name=friends_network,N=3,E=1,created=1664458398090]
PgxGraph graph = session.readSubgraph()
                       .fromPgPgql("FRIENDS")
                       .queryPgql("MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'")
                       .queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'")
                       .load("friends_network");
>>> graph = session.read_subgraph_from_pg_pgql("FRIENDS",
...                   ["MATCH (v1:Person)-[e:FRIENDOF]->(v2:Person) WHERE id(v1) = 'PERSONS(1)'",
...                    "MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'"],
...                   graph_name="friends_network")
>>> graph
PgxGraph(name: friends_network, v: 3, e: 1, directed: True, memory(Mb): 0)

Loading a Subgraph by Explicitly Specifying the Schema Name

If you want to load a subgraph by reading a PGQL property graph from another schema, you can additionally provide the schema name as an argument to the PgViewSubgraphReader#fromPgPgql API . You must also ensure that you have READ permission on all the underlying metadata and data tables for the PGQL property graph.

For example:

opg4j> var graph = session.readSubgraph()
...> .fromPgPgql("GRAPHUSER", "FRIENDS")
...> .queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'")
...> .load()
graph ==> PgxGraph[name=FRIENDS,N=1,E=0,created=1672743755511]
PgxGraph graph = session.readSubgraph()
                       .fromPgPgql("GRAPHUSER", "FRIENDS")
                       .queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'")
                       .load();
>>> graph = session.read_subgraph_from_pg_pgql("FRIENDS",
...  ["MATCH (v:Person) WHERE id(v) = 'PERSONS(2)'"],
...  schema="GRAPHUSER")

10.4.2 Prepared PGQL Queries

You can also use prepared queries when loading a subgraph from a PGQL property graph.

You can pass bind variables using prepared PGQL queries. The PreparedPgViewPgqlQuery#preparedPgqlQuery method adds a prepared query to a list of queries that are executed to load the subgraph. The PreparedPgViewPgqlQuery API sets the bindings for the variables and continues with the loading of the subgraph.

For example:

opg4j> var pgViewSubgraphReader = session.readSubgraph().
...>                                      fromPgPgql("FRIENDS")
pgViewSubgraphReader ==> oracle.pgx.api.subgraph.PgViewSubgraphReader@33bfe6d3
opg4j> var preparedPgqlQuery = pgViewSubgraphReader.preparedPgqlQuery("MATCH (v1:Person)-[e:FriendOf]->(v2:Person) WHERE id(v2)=?")
preparedPgqlQuery ==> oracle.pgx.api.subgraph.PreparedPgViewPgqlQuery@2e6b379c
opg4j> preparedPgqlQuery = preparedPgqlQuery.withStringArg(1, "PERSONS(3)")
preparedPgqlQuery ==> oracle.pgx.api.subgraph.PreparedPgViewPgqlQuery@2e6b379c
opg4j> var graph = preparedPgqlQuery.load()
graph ==> PgxGraph[name=FRIENDS_2,N=3,E=2,created=1648566047855]

import oracle.pgx.api.subgraph.*;
…
…
PgViewSubgraphReader pgViewSubgraphReader= session.readSubgraph().fromPgPgql("FRIENDS");
PreparedPgViewPgqlQuery preparedPgqlQuery = pgViewSubgraphReader.preparedPgqlQuery("MATCH (v1:Person)-[e:FriendOf]->(v2:Person) WHERE id(v2)=?");
preparedPgqlQuery = preparedPgqlQuery.withStringArg(1, "PERSONS(3)"); 
PgxGraph graph = preparedPgqlQuery.load(); 
>>> from pypgx.api import PreparedPgqlQuery
>>> from pypgx.api import PreparedPgqlQueryStringArgument
>>> graph = session.read_subgraph_from_pg_pgql("FRIENDS",
...   [PreparedPgqlQuery("MATCH (v1:Person)-[e:FriendOf]->(v2:Person) WHERE id(v2)=?", [PreparedPgqlQueryStringArgument("PERSONS(3)")])])
>>> graph
PgxGraph(name: FRIENDS, v: 3, e: 2, directed: True, memory(Mb): 0)

10.4.3 Providing Database Connection Credentials

You can specify the database connection credentials with the PgViewSubgraphReader#fromPgPgql API instead of using the default credentials of the current user.

The following example shows loading of a subgraph for non-default database connection settings:

opg4j> var graph = session.readSubgraph().
...>                     fromPgPgql("FRIENDS").
...>                     username("graphuser").
...>                     password("<password_for_graphuser>").
...>                     keystoreAlias("database1").
...>                     schema("GRAPHUSER").
...>                     jdbcUrl("jdbc:oracle:thin:@localhost:1521/orclpdb").
...>                     connections(12).
...>                     queryPgql("MATCH (a:Person)").
...>                     load()
graph ==> PgxGraph[name=FRIENDS,N=4,E=0,created=1648541234520] 
PgxGraph graph = session.readSubgraph()
                        .fromPgPgql("FRIENDS")
                        .username("graphuser")
                        .password("<password_for_graphuser>")
                        .keystoreAlias("database1")
                        .schema("GRAPHUSER")
                        .jdbcUrl("jdbc:oracle:thin:@localhost:1521/orclpdb")
                        .connections(12)
                        .queryPgql("MATCH (a:Person)")
                        .load();

10.4.4 Dynamically Expanding a Subgraph

You can expand an in-memory subgraph by loading another subgraph into memory and merging it with the current in-memory subgraph.

The PgxGraph.expandGraph() method can be used to expand a subgraph. The following applies when merging two graphs:

  • Both the graphs can have separate sets of providers.
  • A graph can have some providers same as the other graph. In this case:
    • The providers with the same names must have the same labels.
    • The graph being merged must have the same or a common subset of properties as the base graph. However, it is possible that either of the graphs may have more number of properties.

The following example shows the expansion of the subgraph created in PGQL Based Subgraph Loading:

opg4j> graph = graph.expandGraph().
...>          withPgql().
...>          fromPgPgql("FRIENDS").
...>          queryPgql("MATCH (v1:PERSON) -[e:FRIENDOF]-> (v2:PERSON) WHERE id(v1) = 'PERSONS(2)'").
...>          preparedPgqlQuery("MATCH (v:PERSON) WHERE id(v) in ?").withStringArg(1, "PERSONS(4)").
...>          expand()
graph ==> PgxGraph[name=anonymous_graph_152,N=4,E=3,created=1647347092964]
graph = graph.expandGraph()
             .withPgql()
             .fromPgPgql("FRIENDS")
             .queryPgql("MATCH (v1:PERSON) -[e:FRIENDOF]-> (v2:PERSON) WHERE id(v1) = 'PERSONS(2)'")
             .preparedPgqlQuery("MATCH (v:PERSON) WHERE id(v) in ?").withStringArg(1, "PERSONS(4)")
             .expand();
>>> from pypgx.api import PreparedPgqlQuery
>>> from pypgx.api import PreparedPgqlQueryStringArgument
>>> graph = graph.expand_with_pgql(["MATCH (v1:PERSON) -[e:FRIENDOF]-> (v2:PERSON) WHERE id(v1) = 'PERSONS(2)'",
...               PreparedPgqlQuery("MATCH (v:Person) WHERE id(v)=?", [PreparedPgqlQueryStringArgument("PERSONS(4)")])],
...               pg_view_name="FRIENDS")
>>> graph
PgxGraph(name: anonymous_graph_66, v: 4, e: 3, directed: True, memory(Mb): 0)

The following displays the output for the preceding PGQL query using the graph visualization tool. The subgraph is now expanded to include the friendOf relationship for PERSONS(2) in addition to PERSONS(1) which was already existing in the subgraph.

Figure 10-2 Expanding a Subgraph

Description of Figure 10-2 follows
Description of "Figure 10-2 Expanding a Subgraph"

Expanding a Subgraph by Explicitly Specifying the Schema Name

When expanding a graph, you can load another subgraph by reading a PGQL property graph from a different schema. For this, you must provide the schema name as an argument to the PgqlViewGraphExpander#fromPgPgql API. You must also ensure that you have READ permission on all the underlying metadata and data tables for the PGQL property graph.

For example:

opg4j> graph = graph.expandGraph().
...>           withPgql().
...>           fromPgPgql("GRAPHUSER", "FRIENDS").
...>           queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(1)'").
...>           expand()
graph ==> PgxGraph[name=anonymous_graph_18,N=1,E=0,created=1672848726308]
graph = graph.expandGraph()
             .withPgql()
             .fromPgPgql("GRAPHUSER", "FRIENDS")
             .queryPgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(1)'")
             .expand();
>>> graph = graph.expand_with_pgql("MATCH (v:Person) WHERE id(v) = 'PERSONS(1)'",
...  pg_view_name="FRIENDS", schema="GRAPHUSER")
>>> graph
PgxGraph(name: anonymous_graph_6, v: 2, e: 0, directed: True, memory(Mb): 0)

Using Merging Strategy

When expanding a graph, some vertices and edges that are in the new graph data may have already been loaded in the base graph. In such cases, if the vertex and edge property values differ for all vertices and edges that are both in the base graph and in the new graph to be merged, then the following applies:

  • If the merging strategy is KEEP_CURRENT_VALUES, then the vertex and edge property values coming from the new graph are ignored.
  • If the merging strategy is UPDATE_WITH_NEW_VALUES, then the vertex and edge property values are updated with the ones found in the new graph.

For example:

opg4j> import oracle.pgx.api.expansion.PropertyMergeStrategy
opg4j> graph = graph.expandGraph().
...>          withPgql().
...>          fromPgPgql("FRIENDS").
...>          queryPgql("MATCH (v1:PERSON) -[e:FRIENDOF]-> (v2:PERSON) WHERE id(v1) = 'PERSONS(2)'").
...>          preparedPgqlQuery("MATCH (v:PERSON) WHERE id(v) in ?").withStringArg(1, "PERSONS(4)").
...>          vertexPropertiesMergingStrategy(PropertyMergeStrategy.UPDATE_WITH_NEW_VALUES).
...>          expand()
import oracle.pgx.api.expansion.PropertyMergeStrategy;
graph = graph.expandGraph()
             .withPgql()
             .fromPgPgql("FRIENDS")
             .queryPgql("MATCH (v1:PERSON) -[e:FRIENDOF]-> (v2:PERSON) WHERE id(v1) = 'PERSONS(2)'")
             .preparedPgqlQuery("MATCH (v:PERSON) WHERE id(v) in ?").withStringArg(1, "PERSONS(4)")
             .vertexPropertiesMergingStrategy(PropertyMergeStrategy.UPDATE_WITH_NEW_VALUES)
             .expand();