Querying Video Relationships with Apache AGE on Postgres

Relational databases struggle with deep relationships.

At TrendVidStream, we manage a catalog of streaming titles across eight regions. We originally used SQLite with FTS5. It worked well for full-text search and simple deployments.

The problem started when users asked complex questions. They wanted to know: "What else is two or three hops away from this thriller with this specific director and these actors?"

In SQL, this requires many self-joins. Each extra hop makes the query harder to maintain and slower to run. The join fan-out becomes a nightmare.

We solved this by adding a graph layer using the Apache AGE extension on PostgreSQL.

Here is why this split works:

• You keep PostgreSQL. You use the same connections, transactions, and backup tools. You do not need a separate database like Neo4j. • You mix graph and relational data. You can run Cypher queries and join the results with standard SQL tables. • You handle variable depth easily. Finding connections across one to three hops is a simple command in Cypher. • You maintain simplicity. The graph is a projection of your data, not a separate source of truth.

Our model uses four vertex labels:

  • Video
  • Person
  • Platform
  • Region

We use specific edge types like SIMILAR_TO, WORKED_ON, and AVAILABLE_IN.

One critical lesson: always index your entry points. We index the ext_id property on Video vertices. Without this, every traversal starts with a slow sequential scan.

The result is a clean, readable query for recommendations:

SELECT * FROM cypher('discovery', $$ MATCH (seed:Video {ext_id: 'vid_8842'}) MATCH (seed)-[:SIMILAR_TO|WORKED_ON*1..3]-(rec:Video)-[:AVAILABLE_IN]->(:Region {code: 'DE'}) WHERE rec.ext_id <> 'vid_8842' RETURN DISTINCT rec.ext_id, rec.title, rec.year LIMIT 24 $$) AS (ext_id agtype, title agtype, year agtype);

This query finds related videos within three hops that are available in Germany. In SQL, this would be a massive pile of joins. In Cypher, it is a simple pattern.

We kept SQLite for text search and used AGE for connectivity. Each tool does what it does best.

Source: https://dev.to/ahmet_gedik778845/querying-video-relationships-with-apache-age-graph-extension-on-postgres-3g54