PGX 20.2.2
Documentation

Quick Start

This page gives a quick overview to get you started on the functionalities of PGX: it shows how to load or create a simple graph, execute pattern-matching queries on it with PGQL, run analytic algorithms, modify the graph, and store the updated version.

This quickstart guide assumes that you have already installed PGX. Refer to the Basic Installation section of the installation guide in case you haven't yet. You can execute the code in this quickstart either with the PGX shell or in a Java program written using the PGX API.

Load a Graph from File

The most common way to create a graph in PGX is to load it from existing data (e.g., csv files or database tables). PGX support multiple options and formats; in this quick start guide, we use CSV files, as they are easy to examine. We will load the data from two files: one file for vertices and the other for edges. Both files need to have a header that PGX uses to infer the names of the columns to be loaded as properties. The header needs to follow a specific format to provide additional information for marking special columns (e.g., for IDs) and specify the data types.

Another way to create a graph is to directly call the graph builder API; we will show this option later.

We have prepared two small CSV files representing a simple graph that we will use in this quickstart. Please download the following files and place them in the current working directory.

PGX uses the header of the csv files to determine the name and types of the properties to load, as well as the column to be used as vertex (resp. edge) ID, the columns that indicate the source and destination vertex ID for edges, and the column to be loaded as vertex / edge label.

For example, the header of edges.csv is:

:EID,:SRC,:DST,:LABEL,Strength:string

You can use PgxSession.readGraphFiles() to load the graph. This method takes three arguments:

  • The path to the vertex file;
  • The path to the edge file;
  • The name of the graph to be created.
var loadedGraph = session.readGraphFiles("vertices.csv", "edges.csv", "LoadedGraphName")
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.PgxGraph;

PgxSession session = Pgx.createSession("NewSession");
PgxGraph loadedGraph = session.readGraphFiles("vertices.csv", "edges.csv", "LoadedGraphName")
session = pypgx.get_session(session_name="NewSession")
loaded_graph = session.read_graph_files("vertices.csv", "edges.csv", "LoadedGraphName")

More Advanced Graph Loading

There are multiple ways to load graph data into PGX from CSV files or other data sources. In this quickstart, we used the configless mode, which means that all the information has to be stored in the file header, without any additional configuration file required. A more flexible (but more verbose) way is to use a graph configuration that specifies the property names and types and other settings. Refer to the Graph Loading Guide for more details.

Partitioned Graphs

This quickstart only covers non-partitioned graphs, where all vertices (resp. edges) all have the same set of properties. PGX also supports creating partitioned graphs where there can be multiple types of vertices or edges, each with their own set of properties (e.g., people, places, companies, ...). Refer to the partitioned graph configuration section for more details.

Run a PGQL Query

PGX implements a declarative graph pattern matching called PGQL. PGQL allows to express search pattern that consists of vertices and edges, as well as additional constraints on the properties of the vertices and edges.

To submit a PGQL query to PGX, you can use the PgxGraph.queryPgql() method, passing the PGQL query as an argument. The queryPgql() method returns a PgqlResultSet object that contains the result of the query. The simplest way to display the actual values is to use the PgqlResultSet.print() method.

The following code snippet finds all pairs of vertices connected by an edge and prints the first 10 results. The vertices and edges are shown as vertex and edge IDs.

loadedGraph.queryPgql("SELECT * FROM MATCH (n) -[e]- (m)").print(10).close()
loadedGraph.queryPgql("SELECT * FROM MATCH (n) -[e]- (m)").print(10).close();

The following code snippet gets the same set of pairs, but instead of vertex/edge IDs shows vertex/edge labels and selected vertex/edge properties.

Remember to Close ResultSets

Always use PgqlResultSet.close() to free the resources associated with the result set once you are done examining the results. Otherwise, the result set will be kept in memory indefinitely.

loadedGraph.queryPgql("SELECT id(n), label(n), n.Name, label(e), e.Strength, id(m), label(m), m.Name FROM MATCH (n) -[e]- (m)").print(10).close()
loadedGraph.queryPgql("SELECT id(n), label(n), n.Name, label(e), e.Strength, id(m), label(m), m.Name FROM MATCH (n) -[e]- (m)").print(10).close();

You can also iterate through the result set using the for loop construct.

var result = loadedGraph.queryPgql("SELECT n.Name, n.Height FROM MATCH (n:Person)")

for (PgxResult line : result) {
    println("Person " + line.getString(1) + " is " + line.getDouble(2) + "\" tall.");
}


result.close()
import oracle.pgx.api.PgqlResultSet;
import oracle.pgx.api.PgxResult;

PgqlResultSet result = loadedGraph.queryPgql("SELECT n.Name, n.Height FROM MATCH (n:Person)");

for (PgxResult line : result) {
    System.out.println("Person " + line.getString(1) + " is " + line.getDouble(2) + "\" tall.");
}

result.close()
result = loaded_graph.query_pgql("SELECT n.Name, n.Height FROM MATCH (n:Person)")
for line in results:
    print("Person " + line[0] + " is " + line[1] + "\" tall.")

PGQL also supports conditions, aggregations and ordering as shown in the following code snippets.

// Condition
loadedGraph.queryPgql("SELECT id(n), label(n), n.Name, label(e), id(m), label(m), m.Name FROM MATCH (n) -[e]- (m) WHERE label(n) = 'Person' AND n.Height > 6").print(10).close()

// Aggregation
loadedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close()

// Ordering
loadedGraph.queryPgql("SELECT n.Name, n.Height FROM MATCH (n:Person) ORDER BY n.Height").print(10).close()
// Condition
loadedGraph.queryPgql("SELECT id(n), label(n), n.Name, label(e), id(m), label(m), m.Name FROM MATCH (n) -[e]- (m) WHERE label(n) = 'Person' AND n.Height > 6").print(10).close();

// Aggregation
loadedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close();

// Ordering
loadedGraph.queryPgql("SELECT n.Name, n.Height FROM MATCH (n:Person) ORDER BY n.Height").print(10).close();

Further Details

You can refer to the PGQL Specification and the relevant programming with PGQL guide for further information.

Run Analytic Algorithms

PGX also offers a number of built-in advanced graph analytic algorithms, like Shortest-Path, Max-Capacity, PageRank, Graph Segmentation, Reachability, etc. The list of all available analytics algorithms is available in the built-in algorithms section.

You can run an algorithm by calling the appropriate method from the Analyst class. You can get an instance of the Analyst class by using the PgxSession.createAnalyst() method. If you are using the PGX Shell, an instance of the Analyst class is created automatically and stored in the analyst variable.

The following example shows how to execute the reachability algorithm on the example graph.

var ctyPrg = loadedGraph.getVertex("5")
var ctyZrh = loadedGraph.getVertex("6")
analyst.reachability(loadedGraph, ctyPrg, ctyZrh, 10000, true)
import oracle.pgx.api.PgxVertex;

PgxVertex<String> ctyPrg = loadedGraph.getVertex("5");
PgxVertex<String> ctyZrh = loadedGraph.getVertex("6");
analyst.reachability(loadedGraph, ctyPrg, ctyZrh, 10000, true);
cty_prg = loaded_graph.get_vertex("5")
cty_zrh = loadedGraph.get_vertex("6")
analyst.reachability(loaded_graph,cty_prg,cty_zrh,10000,True)

Custom Algorithms

You can implement and execute additional analytics algorithms, besides those included in the built-in selection, by using PGX Algorithm as described in the graph algorithms section.

Create a Graph from Scratch

Besides loading a graph from existing data, you can also create a graph from scratch programmatically using the GraphBuilder functionality of PGX.

The steps to create a graph are:

  1. Create a GraphBuilder object representing an empty graph.

  2. Add new vertices and edges, including vertex/edge properties. You may also specify vertex/edge IDs if necessary.

  3. Create the updated PGX graph by applying the changeset.

The following code snippet illustrates these steps using the PGX API.

var builder = session.createGraphBuilder(IdGenerationStrategy.USER_IDS, IdGenerationStrategy.USER_IDS)

builder.addVertex(1).setProperty("Name", "Anna" ).setProperty("Height", 5.5).addLabel("Person")
builder.addVertex(2).setProperty("Name", "Maria").setProperty("Height", 5.8).addLabel("Person")
builder.addVertex(3).setProperty("Name", "John" ).setProperty("Height", 6.4).addLabel("Person")
builder.addVertex(4).setProperty("Name", "Peter").setProperty("Height", 6.1).addLabel("Person")
builder.addVertex(5).setProperty("Name", "Prague").addLabel("City")
builder.addVertex(6).setProperty("Name", "Zurich").addLabel("City")

builder.addEdge(101, 1, 2).setProperty("Strength", "friends").setLabel("Relationship")
builder.addEdge(102, 2, 3).setProperty("Strength", "friends").setLabel("Relationship")
builder.addEdge(103, 1, 4).setProperty("Strength", "acquitance").setLabel("Relationship")
builder.addEdge(110, 1, 5).setProperty("Strength", "").setLabel("LivesIn")
builder.addEdge(111, 2, 5).setProperty("Strength", "").setLabel("LivesIn")
builder.addEdge(112, 3, 6).setProperty("Strength", "").setLabel("LivesIn")
builder.addEdge(113, 4, 6).setProperty("Strength", "").setLabel("LivesIn")

var createdGraph = builder.build("CreatedGraphName")
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.PgxGraph;
import oracle.pgx.api.GraphBuilder;

PgxSession session = Pgx.createSession("NewSession");
GraphBuilder<Integer> builder = session.createGraphBuilder();

builder.addVertex(1).setProperty("Name", "Anna" ).setProperty("Height", 5.5).addLabel("Person");
builder.addVertex(2).setProperty("Name", "Maria").setProperty("Height", 5.8).addLabel("Person");
builder.addVertex(3).setProperty("Name", "John" ).setProperty("Height", 6.4).addLabel("Person");
builder.addVertex(4).setProperty("Name", "Peter").setProperty("Height", 6.1).addLabel("Person");
builder.addVertex(5).setProperty("Name", "Prague").addLabel("City");
builder.addVertex(6).setProperty("Name", "Zurich").addLabel("City");

builder.addEdge(101, 1, 2).setProperty("Strength", "friends").setLabel("Relationship");
builder.addEdge(102, 2, 3).setProperty("Strength", "friends").setLabel("Relationship");
builder.addEdge(103, 1, 4).setProperty("Strength", "acquitance").setLabel("Relationship");
builder.addEdge(110, 1, 5).setProperty("Strength", "").setLabel("LivesIn");
builder.addEdge(111, 2, 5).setProperty("Strength", "").setLabel("LivesIn");
builder.addEdge(112, 3, 6).setProperty("Strength", "").setLabel("LivesIn");
builder.addEdge(113, 4, 6).setProperty("Strength", "").setLabel("LivesIn");

PgxGraph createdGraph = builder.build("CreatedGraphName");
session = Pypgx.get_session(session_name="NewSession");
builder = session.create_graph_builder();

builder.add_vertex(1).set_property("Name", "Anna" ).set_property("Height", 5.5).add_label("Person")
builder.add_vertex(2).set_property("Name", "Maria").set_property("Height", 5.8).add_label("Person")
builder.add_vertex(3).set_property("Name", "John" ).set_property("Height", 6.4).add_label("Person")
builder.add_vertex(4).set_property("Name", "Peter").set_property("Height", 6.1).add_label("Person")
builder.add_vertex(5).set_property("Name", "Prague").add_label("City")
builder.add_vertex(6).set_property("Name", "Zurich").add_label("City")

builder.add_edge(101, 1, 2).set_property("Strength", "friends").set_label("Relationship")
builder.add_edge(102, 2, 3).set_property("Strength", "friends").set_label("Relationship")
builder.add_edge(103, 1, 4).set_property("Strength", "acquitance").set_label("Relationship")
builder.add_edge(110, 1, 5).set_property("Strength", "").set_label("LivesIn")
builder.add_edge(111, 2, 5).set_property("Strength", "").set_label("LivesIn")
builder.add_edge(112, 3, 6).set_property("Strength", "").set_label("LivesIn")
builder.add_edge(113, 4, 6).set_property("Strength", "").set_label("LivesIn")

created_graph = builder.build("CreatedGraphName")

Further Details

For further details, refer to the graph builder and graph change set reference page.

Modify a Graph

You can also modify an existing graph using the GraphChangeSet class. The process is similar to creating a graph from scratch:

  1. Get the graph changeset object from the existing graph you want to modify.

  2. Make the changes to the graph using addEdge / addVertex / updateEdge / updateVertex / removeEdge / removeVertex methods.

  3. Finalize the change by applying the changes to the graph.

The following code snippet demonstrates the use of these methods.

var builder = createdGraph.<Integer>createChangeSet()

builder.addVertex(7).setProperty("Name", "Jack").setProperty("Height", 6.3).addLabel("Person")
builder.addVertex(8).setProperty("Name", "Nick").setProperty("Height", 6.2).addLabel("Person")
builder.addVertex(9).setProperty("Name", "London").addLabel("City")

builder.updateVertex(1).setProperty("Name", "Marianna")

builder.removeVertex(3)

builder.addEdge(210, 7, 9).setProperty("Strength", "").setLabel("LivesIn")
builder.addEdge(211, 8, 9).setProperty("Strength", "").setLabel("LivesIn")

builder.updateEdge(103).setProperty("Strength", "friends")

builder.removeEdge(102)

var updatedGraph = builder.build()
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.GraphBuilder;
import oracle.pgx.api.PgxGraph;

PgxSession session = Pgx.createSession("NewSession");
GraphBuilder<Integer> builder = createdGraph.createChangeSet();

builder.addVertex(7).setProperty("Name", "Jack").setProperty("Height", 6.3).addLabel("Person");
builder.addVertex(8).setProperty("Name", "Nick").setProperty("Height", 6.2).addLabel("Person");
builder.addVertex(9).setProperty("Name", "London").addLabel("City");

builder.updateVertex(1).setProperty("Name", "Marianna");
builder.removeVertex(3);

builder.addEdge(210, 7, 9).setProperty("Strength", "").setLabel("LivesIn");
builder.addEdge(211, 8, 9).setProperty("Strength", "").setLabel("LivesIn");

builder.updateEdge(103).setProperty("Strength", "friends");
builder.removeEdge(102);

PgxGraph updatedGraph = builder.build();

To see the difference between the two graphs, you can execute the following queries.

updatedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close()
loadedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close()
updatedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close();
loadedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close();

Further Details

For further details, refer to the graph builder and graph change set reference page.

Publish a Graph

The loaded/created graph remains private to the current session until you publish it. The published graph can be made available to other sessions using the PgxGraph.publish() method. In another session, the graph is available through the PgxSession.getGraph() method, passing the name of the published graph as an argument.

loadedGraph.publish()

var newSession = instance.createSession("NewSession")
var sharedGraph = newSession.getGraph("LoadedGraphName")
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.PgxGraph;

loadedGraph.publish();

PgxSession newSession = instance.createSession("NewSession");
PgxGraph sharedGraph = newSession.getGraph("LoadedGraphName");
loaded_graph.publish()
new_session = get_session(session_name="NewSession")
shared_graph= new_session.get_graph("LoadedGraphName")

Note that if you use the publish() method, only the graph structure and labels are published. If you want to publish also properties, you have to list them in the publish() call, as shown in the code snippet below.

var nameProp = loadedGraph.getVertexProperty("Name")
var heightProp = loadedGraph.getVertexProperty("Height")
var strengthProp = loadedGraph.getEdgeProperty("Strength")

loadedGraph.publish(List.of(nameProp, heightProp), List.of(strengthProp))

var newSession = instance.createSession("NewSession")
var sharedGraph = newSession.getGraph("LoadedGraphName")

sharedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close()
import oracle.pgx.api.VertexProperty;
import oracle.pgx.api.EdgeProperty;
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.PgxGraph;

VertexProperty<?, ?> nameProp = loadedGraph.getVertexProperty("Name");
VertexProperty<?, ?> heightProp = loadedGraph.getVertexProperty("Height");
EdgeProperty<?> strengthProp = loadedGraph.getEdgeProperty("Strength");

loadedGraph.publish([nameProp, heightProp], [strengthProp]);

PgxSession newSession = instance.createSession("NewSession");
PgxGraph sharedGraph = newSession.getGraph("LoadedGraphName");

sharedGraph.queryPgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print(10).close()
name_prop= loaded_graph.get_vertex_property("Name")
height_prop= loaded_graph.get_vertex_property("Height")
strength_prop= loaded_graph.get_edge_property("Strength")
loaded_graph.publish(vertex_properties=[name_prop,height_prop],edge_properties=[strength_prop])
new_session = get_session(session_name="NewSession")
shared_graph= new_session.get_graph("LoadedGraphName")
shared_graph.query_pgql("SELECT m.Name, COUNT(*) FROM MATCH (n:Person) -[e]- (m:City) GROUP BY m.Name").print()

Store a Graph

You can use the PgxGraph.store() method to save a graph to persistent storage (e.g., to file). In the code snippet below, we are showing the use of PGB format, which is a custom binary format optimized for storing / loading PGX graphs.

updatedGraph.store(Format.PGB, "updated.graph.pgb")
updatedGraph.store(Format.PGB, "updated.graph.pgb");
updated_graph.store("pgb","updated.graph.pgb")

The store() method returns a GraphConfig object that describes the stored graph and allows to later load it again into PGX with the PgxSession.readGraphWithProperties() method.

Further Details

For further details, refer to the relevant page about exporting graphs.