Skip to content

All posts

How To Build An Amazon-Scale Knowledge Graph for GraphRAG

How Graph.Build turned 43.9 million Amazon reviews into a governed Memgraph knowledge graph in a day, and why the model mattered more than the volume.

43.9 Million Amazon Reviews Into Memgraph in a Day

Model the business once, and every agent gets the same answer. That is the idea Graph.Build is built around, and the Amazon Reviews dataset is a good place to test whether it holds at scale.

We took the Electronics category of the Amazon Reviews 2023 corpus: 43,886,944 reviews across 21 GB of JSONL, plus a 4.9 GB catalog of 1,610,012 products. Schema, mapping, transformation and publishing were done inside a day. The graph Memgraph then ran GraphRAG against was over 10 million nodes and 16 million edges.

When we put the webinar together with Memgraph, the assumption was that the hard part would be volume. It wasn't. Streaming 21 GB is an operations problem with a known answer. Deciding what the graph should mean is not, and that is where the effort went.

What you're actually given

Flat records. Reviews look like this:

{
"rating": 5,
"title": "Excellent!",
"text": "I love these. They even come with a carry case...",
"asin": "B01G8JO5F2",
"parent_asin": "B01G8JO5F2",
"user_id": "AFKZENTNBQ7A7V7UXW5JJI6UGRYQ",
"timestamp": 1523093017534,
"helpful_vote": 0,
"verified_purchase": true
}

Catalog records carry price, store, features, description, ratings and parent_asin.

Every relationship you care about is implied by a shared string. user_id ties a review to a person. asin ties it to a specific product. parent_asin ties that product to whatever Amazon considers the same thing in a different colour or size. Nothing in either file tells you which of those strings deserves to be a node.

The model

Four entity types, three relationships:

User --MADE_REVIEW--> Review --REVIEWS--> Product --IS_VARIANT_OF--> ParentProduct

We built it in Graph.Build Studio, dragging entities and relationships onto the canvas, with the generated ontology visible in code view alongside. Version control comes as standard, so the model has a history you can diff and roll back rather than a filename ending in _v3_final.

Three decisions in there are worth pulling out.

Identity is designed, not inferred. User is keyed on {user_id}, Product on {asin}, ParentProduct on {parent_asin}. Review is a composite of {user_id} and {timestamp}, so one user can own thousands of reviews without a single collision. That sounds trivial until you skip it and spend a week working out why your review count is wrong.

Variants are first-class. Amazon issues a separate ASIN per colour or size. Say the Aurex T90 earbuds ship in black, white and navy: that's three ASINs, and reviews attach to whichever one the customer bought. Model asin as your only product node and you have shattered the T90's feedback across three SKUs. Ask how the T90 is doing and you get three partial answers, none of them correct. IS_VARIANT_OF is what lets a query reach the product family instead of one variant.

Review text is in the graph. text is a typed property on Review, alongside rating, title, helpfulVote and verifiedPurchase. Ratings alone give you a decent analytics graph. Text is what GenAI actually consumes. Leave it out and every downstream question collapses back to a star average.

The schema is the contract

Once the ontology is published in Studio, it constrains the mappings built against it. Labels and relationship types are selected from the model rather than typed by hand.

This is the part that decides whether a graph survives contact with a second team. Hand-written ingest scripts keep the model in a developer's head. Someone writes Review in one script and review in another, nobody notices for six weeks, and half the graph is invisible to half the queries. When the ontology governs the mapping, that class of error stops being possible.

Studio also exposes the ontology over an API and an MCP server, so an agent can ask which class a source field belongs to instead of guessing. Useful when you're mapping a catalog with a few hundred fields and no documentation.

From a hundred records to twenty-one gigabytes

We designed the mapping against a 100-record sample, checked the output, fixed it, ran it again. Fail fast on something small, then let it loose.

The mapping that came out of that loop is the mapping that processed the full 21 GB. Not a reimplementation of it, not a hand-tuned production variant. The same file. Scale became an operations problem instead of a remodelling problem, which is what you want and rarely what you get.

Amazon ships JSONL, and the transformer wants JSON arrays, so a feeder script streams the source line by line, skips malformed records rather than dying on them, and hands the transformer chunks through Kafka. Kafka carries pointers to files rather than the records themselves, so message size stays flat no matter how much data is moving.

Transformation and writing run as separate processes with Kafka between them. Neither waits on the other. Transformation ran faster than writing, which is what you'd expect when one side is stream processing and the other is doing constraint-checked inserts, and the split is exactly why that imbalance didn't matter. Mapping errors don't block writes, writes don't stall transforms, and the same mapping runs from 100 records to tens of millions.

Joining the catalog

Reviews and catalog are separate files with separate shapes, and we only wanted catalog rows that reviews could actually attach to.

Scanning all 43.9 million reviews gave 1,609,860 unique parent_asin values. Streaming the 4.9 GB catalog against that set matched 1,609,860 of 1,609,860. A perfect join, and no catalog rows lifted that nothing points at.

A second mapping, bound to the same ontology, enriched those ParentProduct nodes with price, store, co-purchase links and aggregate ratings, exploding the description and features arrays into properties.

Two sources, two mappings, one node identity. Reviews stream in under one model while catalog attributes land on the same parent nodes from another. That pattern shows up in client work far more often than any single-source ingest we have ever built.

What Memgraph does with it

The graph is only worth building if retrieval works, and this half is Memgraph's.

They run what they call Atomic GraphRAG: analytical, local and global retrieval executed inside the database as Cypher, in a single transaction, rather than orchestrated by an external script making round trips. Analytical handles exact lookups. Local does a pivot search and expands one or two hops to pull in the neighbourhood. Global runs PageRank and Louvain community detection across subgraphs. Memgraph 3.8's single vector store also cut memory use by up to 4x, which counts for a lot when the graph lives in RAM.

The question we opened the webinar with was why ratings dropped for a given model in September 2023. Answering it needs the parent aggregation to know what the model refers to, the timestamps to bound the window, the review text rather than the star count, and a neighbourhood walk to find what else moved at the same time.

All of that traces back to the schema. Retrieval quality is a modelling outcome, and no amount of GraphRAG sophistication recovers an answer the model threw away.

The model is the asset

The database is a deployment detail. The same ontology and the same transformer would have produced RDF, and Graph Writer speaks SPARQL, Gremlin and openCypher across more than eight database vendors. Switching target is a configuration change and a re-run, not a rebuild.

Memgraph was the right target here, with GraphRAG patterns that execute where the data already sits. For a different problem we'd pick a different database, and we'd want the model to survive that decision intact.

If you're planning something similar, spend your first week on the model. The pipeline is the easy part, and it's the part that goes wrong last.


Watch the full session, including the live build: How to Build an Amazon-Scale Knowledge Graph for GraphRAG

Memgraph's write-up of the GraphRAG side: Building the Amazon Reviews Knowledge Graph


About the author

Russell Waterson is a Lead Software Engineer at Graph.Build, specialising in full-stack development with a focus on knowledge graphs, linked data and the semantic web. With experience across frontend, backend and cloud, Russell designs and implements the graph-based solutions that help organisations manage and transform their data more effectively.

All posts