Skip to content

Graph Modeling

Once your data sources are connected, the next step is to model your data as a graph: defining how tables become nodes and edges, and where each graph element draws its rows from.

PuppyGraph follows the labeled property graph model. A graph consists of:

  • Nodes (also called vertices), the typed entities such as Person or Software.
  • Edges, the typed, directional relationships between nodes, such as Person -[:KNOWS]-> Person.
  • Properties, the key-value attributes attached to nodes and edges.

A PuppyGraph schema is a single JSON document that maps your tables onto this model. You can author it through the Schema Builder on the Graph page of the Web UI, let PuppyGraph AI inspect your connected tables and propose the graph for you, or upload a schema.json file directly. Once active, the schema can be edited in place from the UI without re-uploading.

Anatomy of a schema

A schema has up to five top-level sections:

Section Purpose
catalog[] Connections to external data sources. Per-source configuration is documented in Connecting.
node[] Node types. See Building a Graph.
edge[] Edge types. See Building a Graph.
localTable[] Locally cached tables. Optional. See Data Sources and Local Tables.
rowLevelSecurity Per-user query filters. Optional. See Row-Level Security.

A minimal schema with one catalog, one node type, and one edge type:

{
  "catalog": [
    {
      "name": "postgres_data",
      "type": "postgresql",
      "jdbc": {
        "username": "user",
        "password": "pass",
        "jdbcUri": "jdbc:postgresql://host:5432/postgres"
      }
    }
  ],
  "node": [
    {
      "label": "Person",
      "dataSourceGroup": {
        "externalDataSource": {
          "enabled": true,
          "catalog": "postgres_data",
          "schema": "modern",
          "table": "person",
          "mappedField": [
            { "sourceFieldName": "id",   "targetFieldName": "id"   },
            { "sourceFieldName": "name", "targetFieldName": "name" },
            { "sourceFieldName": "age",  "targetFieldName": "age"  }
          ]
        }
      },
      "id": [{ "name": "id", "type": "STRING" }],
      "attribute": [
        { "name": "name", "type": "STRING" },
        { "name": "age",  "type": "INT" }
      ]
    }
  ],
  "edge": [
    {
      "label":         "KNOWS",
      "fromNodeLabel": "Person",
      "toNodeLabel":   "Person",
      "dataSourceGroup": {
        "externalDataSource": {
          "enabled": true,
          "catalog": "postgres_data",
          "schema": "modern",
          "table": "knows",
          "mappedField": [
            { "sourceFieldName": "id",      "targetFieldName": "id"      },
            { "sourceFieldName": "from_id", "targetFieldName": "from_id" },
            { "sourceFieldName": "to_id",   "targetFieldName": "to_id"   },
            { "sourceFieldName": "weight",  "targetFieldName": "weight"  }
          ]
        }
      },
      "id":        [{ "name": "id",      "type": "STRING" }],
      "fromKey":   [{ "name": "from_id", "type": "STRING" }],
      "toKey":     [{ "name": "to_id",   "type": "STRING" }],
      "attribute": [{ "name": "weight",  "type": "DOUBLE" }]
    }
  ]
}

Two things about this shape are easy to get wrong when writing a schema by hand:

  • mappedField[] is required on every data source. It lists each graph field the source supplies, including columns that keep their source name, and an upload whose data source maps no fields is rejected. See Mapping source columns to graph fields.
  • ID columns (id[]) are the node or edge identity, not properties. attribute[] holds the queryable properties, so an identifier that users also query as a property must appear in both arrays, with a mappedField[] entry. In Cypher, read an identifier with id(n) (for example Person[v1]), not n.id, which returns null. See Reading node and edge IDs.

If you upload a schema in the legacy 0.x format (catalogs with graph.vertices), PuppyGraph detects it and converts it to this 1.x shape on upload, so you do not have to rewrite it by hand. See Migrating from v0.

What's in this section

  • Building a Graph, mapping tables to nodes and edges, identifiers and keys, the Schema Builder UI, and modeling denormalized tables.
  • Data Sources and Local Tables, where the rows behind a node or edge come from: external catalogs, locally cached tables, or unions.
  • Managing the Graph, inspecting and modifying the active schema in the Web UI, importing or exporting it as JSON, and managing local tables.
  • Migrating from v0, for users coming from PuppyGraph 0.x.

Two schema features are documented outside this section:

To have PuppyGraph design the graph for you instead of modeling it by hand, see PuppyGraph AI and the Building a Graph with PuppyGraph AI tutorial.

Next step

Once a schema is active, the graph is queryable. Continue to Querying for Cypher and Gremlin, Query UI to run queries in the browser, or PuppyGraph AI to ask questions in natural language.