Knowledge Graphs: Decide What Every Edge Means

By Sergey Nosov

16 October 2026

On 16 May 2012, Google launched its Knowledge Graph with a three-word summary: “things, not strings.” Someone searching for “Taj Mahal,” the launch post explained, might mean a monument, a Grammy Award–winning musician, or a casino in Atlantic City, and matching strings cannot tell them apart. The post said Google’s new model “understands real-world entities and their relationships to one another.”

A knowledge graph stores facts the way that slogan suggests: as things joined by named relationships. “Albert Einstein was born in Ulm” becomes a triple: a subject, a predicate, and an object. In the Resource Description Framework (RDF) data published by Wikidata, a free knowledge base that anyone can edit, the simplest form of that fact is three identifiers:

wd:Q937 wdt:P19 wd:Q3012 .

Q937 is Einstein, P19 is the property “place of birth,” and Q3012 is Ulm. Statements like this one, linked through shared identifiers, form a graph you can traverse and query.

Storing triples is the easy part. The hard part is deciding what every edge means. Which way does it point? Can a subject have one value or many? Does the fact change over time? How do you know that two mentions name the same thing? Those decisions determine whether the graph returns right answers or merely plausible ones.

Large language models give application developers a new reason to care about knowledge graphs. On its own, a model remembers nothing from one session to the next, and, in the words of Google’s DataGemma announcement, models “sometimes confidently present information that is inaccurate.” Some applications now give a model a graph to read and write, as the reference memory server for the Model Context Protocol does.

The Cypher examples below were tested on Neo4j 2026.09.0 Community Edition, and the SPARQL queries against Wikidata’s public query service, in October 2026.

What Makes a Graph a Knowledge Graph

Stanford’s CS520 seminar notes from 2020 give the definition I find most useful: “A knowledge graph is a directed labeled graph in which the labels have well-defined meanings.” Each part does work:

The last part separates a knowledge graph from a graph that merely stores connections. The meanings live in an ontology, which the same notes define as a “formal specification of the conceptualization of a domain.” In practice, it lists the entity types, how they nest, and the relationship types, each with a definition precise enough to settle an argument. Wikidata’s description of “instance of” shows the precision it takes: “Different from P279 (subclass of); for example: K2 is an instance of mountain; volcano is a subclass of mountain.”

Write the ontology before you load data, but only for the questions you need to answer now. The CS520 notes call a top-down schema “extremely difficult” to design for many data integration applications; most enterprise projects of that kind, they report, favor defining the schema as needed. A short list of types with sharp definitions beats a long one that nobody reads.

Where You Already Meet One

Google. The knowledge panels beside desktop search results come from the Knowledge Graph. The launch post counted more than 500 million objects and more than 3.5 billion facts and relationships among them. In May 2020, Google said the graph had “amassed over 500 billion facts about five billion entities.” The same post admits that the graph sometimes gets facts wrong and explains how anyone can report an error. Even the largest graph needs a way to correct a fact.

Data Commons. Google’s Data Commons applies the idea to public statistics. Its documentation describes a knowledge graph built on the Schema.org vocabulary, with “over 100 datasets and 250 billion data points.” In September 2024, Google released DataGemma, research models built on its open Gemma 2 models that retrieve statistics from Data Commons instead of relying on what a model memorized. In September 2025, it added a Model Context Protocol server for AI agents and has hosted the server since February 2026.

Microsoft. Microsoft Research introduced GraphRAG in February 2024: a language model reads a document collection, builds a knowledge graph from it, and answers questions from the graph. Microsoft 365 Copilot draws on Microsoft Graph, an API whose named relationships link a user to a manager and to groups. The company says it brings “information from a user’s emails, files, meetings, calendars, teams, or organizational relationships” into the prompt.

RDF and Property Graphs

Two data models are in common use, and the choice shapes your tools.

RDF, a W3C standard, is the model behind the triple above, and SPARQL is its query language. Predicates are global identifiers called IRIs; subjects and objects can be IRIs too, while values such as dates are literals. Because IRIs are global, two datasets that use the same IRIs link up as soon as you load their triples together. Maintained open-source stores include Apache Jena, Eclipse RDF4J, and QLever, all of which published releases between July and September 2026.

A plain triple has nowhere to put a fact about the fact, such as the year a friendship began. Wikidata attaches qualifiers, such as a start time, to its statements for that purpose. RDF 1.2 Concepts, a W3C Candidate Recommendation since April 2026, proposes a direct form of the idea: the triple term, a triple used inside another triple.

Property graphs are the model many developers meet first. Nodes carry labels and properties. Relationships carry a type, a direction, and properties of their own, so the year a friendship began can sit on the edge itself. Neo4j developed Cypher, the property-graph query language used in this article. ISO and IEC published GQL, a standard query language for property graphs, in April 2024, and Neo4j calls GQL “a close reflection of Cypher.” Amazon Neptune serves both camps: it supports Gremlin and openCypher for property graphs and SPARQL for RDF.

Here are the Einstein facts as a property graph. The birth date is a property of the person, because a person has exactly one; the relationships connect things to other things.

CREATE
  (e:Person {
    name: 'Albert Einstein',
    born: date('1879-03-14')}),
  (u:City {name: 'Ulm'}),
  (g:Country {name: 'Germany'}),
  (b:Book {title: 'Relativity'}),
  (e)-[:BORN_IN]->(u),
  (u)-[:LOCATED_IN]->(g),
  (b)-[:AUTHOR]->(e)

The AUTHOR edge points from the book to the person, the same way Wikidata’s “author” property does; its description says to use it “on works, not humans.” A query can follow an edge in either direction, so the direction you store is a convention. Pick one for each relationship type and write it down. This query finds the books whose authors were born in Germany:

MATCH (b:Book)-[:AUTHOR]->(:Person)
      -[:BORN_IN]->(:City)
      -[:LOCATED_IN]->
      (:Country {name: 'Germany'})
RETURN b.title

It returns Relativity.

Which model should you choose? RDF pays off when your graph must combine with other people’s data, provided you can agree on the identifiers; that agreement is the integration work. A property graph is the shorter path when one application owns the data, because developers write traversals in a syntax that looks like the diagram on the whiteboard.

Query a Real Knowledge Graph

Wikidata’s query service is the quickest way to get a feel for a knowledge graph. Paste this query there:

SELECT ?place ?born WHERE {
  wd:Q937 wdt:P19 ?p ;
          wdt:P569 ?born .
  ?p rdfs:label ?place .
  FILTER(LANG(?place) = "en")
}

It returns Ulm and 14 March 1879. The service predeclares the prefixes wd:, wdt:, and rdfs:, as its manual explains. Standard SPARQL needs them declared, and other endpoints may reject the query without them, so elsewhere put these lines first:

PREFIX wd: <http://www.wikidata.org/entity/>
PREFIX wdt: <http://www.wikidata.org/prop/direct/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

Now a question with more than one right answer: which films did Christopher Nolan direct?

SELECT DISTINCT ?film ?title
WHERE {
  ?film wdt:P57 wd:Q25191 ;
    wdt:P31/wdt:P279* wd:Q11424 .
  OPTIONAL {
    ?film rdfs:label ?title .
    FILTER(LANG(?title) = "en")
  }
}

Three decisions hide in this query. The first is direction: the “director” property (P57) points from each film to its director, so the film is the subject and Nolan (Q25191) is the object.

The second is what counts as a film. The type line accepts an instance of film (Q11424) or of any subclass, and Wikidata records “short film” as a subclass, so Nolan’s shorts appear. Require an instance of film itself, and the items typed as short films drop out. Remove the type line, and the results, as of October 2026, also include The Dark Knight trilogy, which has a director but is not a film. Each version answers a different question; the ontology lets you say which one you mean.

The third is the label, which sits in an OPTIONAL block. One of the shorts, Tarantella, has no English label as of October 2026, and a required label would drop it without a trace.

Both queries are otherwise standard SPARQL 1.1, on purpose. In June 2026, Wikimedia decided to move Wikidata’s query service from Blazegraph to QLever. To run on QLever, the Wikidata Platform team’s migration guide says, queries must comply with SPARQL 1.1 and drop Blazegraph-specific services, functions, and syntax. With the PREFIX lines added, both queries returned the same rows on QLever’s public Wikidata endpoint.

Wikimedia’s own proposal calls Blazegraph “an open source project that has gone unmaintained since being acquired by Amazon.” Its repository was archived in March 2026, and Wikimedia aims to decommission the Blazegraph endpoint by 30 June 2027. Treat a graph store as a long-term commitment. The polyglot persistence entry in my Software Development Principles series says the same of any database you add: decide it with the rigor you would give a major system dependency.

Four Modeling Decisions

Many questions about knowledge graphs are modeling questions in disguise. Four decisions come up as soon as real data arrives.

Name predicates for meaning, not grammar. A predicate does not have to be a verb. Wikidata and Schema.org both name authorship with the noun “author,” while the memory server stores every relation in active voice. Either form works; what must hold everywhere is each name’s meaning and direction. A good predicate is unambiguous to people and to programs, used consistently, easy to query, and useful for a question someone actually asks. One word with two meanings breaks a graph the same way it breaks a canonical data model.

Decide how many values a subject can have. A person has one birthplace and may like many topics. The graph does not decide which is which; three layers do. The ontology says whether a predicate is single-valued, the database may enforce it, and the application decides whether a new value replaces the old one or joins it.

In a property graph, a single-valued attribute such as a birth date fits a node property, which holds one value. A fact that points to another thing, such as a birthplace, has to be a relationship. None of Neo4j’s constraints limits how many relationships of one type a node may have, and a single-valued relationship stays single only if the application keeps it that way.

RDF has no such distinction: a second birthplace is just another triple. The Web Ontology Language (OWL) can declare a property functional, meaning that each subject has at most one value. But OWL is built for inference, and its primer warns that “OWL 2 is not a schema language for syntax conformance.” Declare a functional birthplace property and give Einstein two birthplaces. Unless the ontology also says or implies that the two cities are different, an OWL reasoner reports no error. It concludes that Ulm and Munich are the same place, as the owlrl reasoner did in testing.

To catch the second value, use SHACL, the W3C’s language for validating RDF data. With sh:maxCount 1 on the property, the same data fails validation.

Decide whether a fact has a time. Friendships start, deadlines move, and people change jobs. If a later value simply replaces an earlier one, the graph can answer what is true now but not what was true then, or why an answer changed. When history matters, put dates such as since and until on the edges that change. Zep, a memory service for AI agents, built its Graphiti engine around this idea: its paper describes Graphiti as “a temporally-aware knowledge graph engine” that keeps the history of relationships.

Give every entity a stable identity. Text calls one person “Albert Einstein,” “Einstein,” and “he”; matching them is called entity resolution. Wikidata gives every item an identifier such as Q937 and keeps names as labels. Do the same: key nodes by an identifier and enforce it with a uniqueness constraint. Where none exists, choose the key deliberately: a canonical name for a shared concept, or the owner plus a name for something one user owns. The memory server keys each entity by “A unique name (identifier),” so namesakes become one entity unless the model names them differently.

Readable Is Not the Same as Correct

Cypher reads almost like English, which makes it easy to write a query that looks right and is not. Take friends of friends, the classic graph query. In this small network, each friendship is stored once, in whichever direction it was first recorded:

CREATE
  (a:Person {name: 'Alice'}),
  (b:Person {name: 'Bob'}),
  (c:Person {name: 'Carol'}),
  (e:Person {name: 'Erin'}),
  (a)-[:FRIEND {since: 2019}]->(b),
  (c)-[:FRIEND {since: 2021}]->(b),
  (a)-[:FRIEND {since: 2022}]->(c),
  (e)-[:FRIEND {since: 2024}]->(c)

The obvious query follows FRIEND edges two steps out from Alice:

MATCH (me:Person {name: 'Alice'})
      -[:FRIEND]->(friend)
      -[:FRIEND]->(foaf)
RETURN foaf.name

It returns Bob, who is already Alice’s friend, and it misses Erin, who is a friend of her friend Carol. The arrows are the problem. Friendship has no direction, but every stored edge does, and the query follows only the stored direction. Storing every friendship twice, once each way, trades one error for others: the query then finds Erin, but it also returns Bob and Carol, and it returns Alice herself, twice. By default, a Cypher match never uses the same relationship twice, but a path may come back to a node it has already visited.

The fix is to store each symmetric relationship once, match it without direction, and exclude Alice and her direct friends:

MATCH (me:Person {name: 'Alice'})
      -[:FRIEND]-(friend)
      -[:FRIEND]-(foaf)
WHERE foaf <> me
  AND NOT (me)-[:FRIEND]-(foaf)
RETURN DISTINCT foaf.name

It returns Erin, and only Erin, whichever way each edge was stored. DISTINCT matters as soon as two of Alice’s friends share a friend, because each path produces its own row.

Building One: Structured Data First, Text Second

I recommend four steps: define the ontology for your questions, import the structured data you already own, extract more facts from text, and refine the ontology as new questions arrive. Structured data comes first because its identifiers and relationships are already explicit: loading a table of customers and orders is mostly a mapping exercise, not an inference problem.

Text holds much of what an organization knows, and extraction is where errors creep in. The CS520 notes name three tasks: entity extraction, relation extraction, and entity resolution. A named-entity recognizer does the first. Run spaCy 3.8 with its small English model over “Albert Einstein was born in Ulm in 1879. He developed the theory of relativity,” and it tags Albert Einstein as a person, Ulm as a geopolitical entity, and 1879 as a date. It does not say that he was born there, or that “He” is Einstein; those are the other two tasks.

Language models now do much of this work. The same notes said in 2020 that most of the popular extraction techniques “rely on adapting a pre-trained language model for the task at hand.” Fluent extraction is still extraction, and its errors land in your graph. As the notes put it, “Validating the output of extraction remains an ongoing challenge.” Store a source reference and a confidence score on every extracted edge; the notes suggest validating the low-confidence ones first.

Budget for entity resolution as well. In “Apple’s stock price rose today,” spaCy tags Apple as an organization. Linking that mention to the company and not the fruit is resolution, and the graph helps: the company has a stock price, and the fruit does not. The CS520 notes call resolution “an advanced technique.” Structured sources need it too: Data Commons, which combines statistics from many publishers, lists merging data about the same entity from different sources among its tedious work.

Above all, start small. Pick one domain you understand, model a few hundred nodes, and iterate. The GraphRAG README gives the same advice about its own pipeline: indexing can be expensive, so “start small.”

Memory for a Language Model

Context windows have grown large: Google’s documentation now says that many Gemini models have windows of a million tokens or more. Size has not solved the memory problem: each session still starts empty unless the application stores something, and long inputs are not read evenly. A July 2025 report from Chroma, which makes open-source search infrastructure for AI, tested eighteen models and found that “their performance grows increasingly unreliable as input length grows.”

A knowledge graph gives the application a memory to write to and read back. The loop has four steps: extract facts from the conversation, write them to the graph, retrieve the relevant ones before the next answer, and put them in the prompt. Suppose a personal assistant has just learned that Alice likes lattes and that her web app is due on 15 March 2027. Start with a uniqueness constraint on each global key that you merge on, here the user’s ID and the drink’s name:

CREATE CONSTRAINT user_id
IF NOT EXISTS FOR (u:User)
REQUIRE u.id IS UNIQUE;

CREATE CONSTRAINT drink_name
IF NOT EXISTS FOR (d:Drink)
REQUIRE d.name IS UNIQUE;

Without these constraints, concurrent writes could create two Alices or two lattes. Neo4j’s Cypher manual explains that under concurrent loads, MERGE alone guarantees that a pattern exists, not that it is unique. Then write the facts:

MERGE (u:User {id: 'alice'})
MERGE (d:Drink {name: 'latte'})
MERGE (u)-[:PREFERS]->(d)
MERGE (u)-[:OWNS]->
      (p:Project {name: 'web app'})
SET p.deadline = date('2027-03-15')

MERGE makes the write idempotent: run it twice, and the second run creates nothing. That matters, because a write that times out is often retried.

The latte is a shared concept: every user’s preference points to one Drink node. The project is merged through Alice’s OWNS edge, so its identity is Alice plus the project name. That is why the project name gets no uniqueness constraint: another user’s project with the same name stays a separate node. Both keys are names, so normalize them during extraction; to the database, “Web app” and “web app” are different projects.

In application code, pass extracted values as query parameters, such as $drink, instead of pasting conversation text into the query string, where it could change the query. Set the source and confidence recommended above after the MERGE, not inside its pattern: there, a later mention with a different source or confidence would create a second edge.

A week later, Alice mentions that she also likes mochas and that the deadline moved to 1 April:

MATCH (u:User {id: 'alice'})
MERGE (d:Drink {name: 'mocha'})
MERGE (u)-[:PREFERS]->(d)
WITH u
MATCH (u)-[:OWNS]->
      (p:Project {name: 'web app'})
SET p.deadline = date('2027-04-01')
RETURN p.deadline

PREFERS is multi-valued, so the mocha becomes a second edge. The deadline is single-valued, so SET replaces it. To keep the old deadline as well, make each deadline a dated relationship or node instead of one property that SET overwrites. If no project matches, Neo4j raises no error: the mocha, an independent fact, is still recorded, and the deadline keeps its old value. Check for the row that the query returns: no row means the deadline did not change.

Idempotent is not the same as order-safe: a delayed retry of the first write would restore the old deadline. Stamp each fact with the time it was stated, and keep the newest. A WHERE clause that compares stamps stops a late retry, but Neo4j reads the stamp without a lock, so two overlapping writes can both pass it. In testing, the older of two simultaneous writes won about half the time. Take the node’s write lock before comparing: the Neo4j Operations Manual does it by setting a temporary property.

Before the next answer, the application loads what it knows about Alice:

MATCH (:User {id: 'alice'})
      -[r:PREFERS|OWNS]->(x)
RETURN type(r), properties(x)

That returns three rows: two preferences and one project, due 1 April 2027.

The payoff is that the memory can be inspected and corrected. When a fact is wrong, you fix one property or edge instead of hoping that the newer of two contradictory notes wins at retrieval time. Treat every extracted memory as a claim, and give people a way to see and correct what the system remembers about them.

Graph, Vector, or Both

The common alternative to a graph is vector retrieval, which most retrieval-augmented generation (RAG) systems use, as the GraphRAG announcement notes. They split documents into chunks, embed them, and fetch the chunks nearest to the question.

Microsoft’s LazyGraphRAG post of November 2024 separates the two: vector retrieval “excels for local queries where the answer resembles the query and can be found within specific text regions.” The post adds that who, what, when, and where questions typically fit that description. GraphRAG targets global questions about a whole collection, such as the main themes in a dataset.

Microsoft’s paper tested GraphRAG on “global sensemaking questions over datasets in the 1 million token range.” There, a language model judged GraphRAG’s answers more comprehensive and more diverse than those of conventional retrieval. The paper’s February 2025 revision checked those verdicts by counting and clustering the factual claims in each answer. In the 33 to 39 percent of comparisons where the judge picked a winner rather than a tie, the authors reported “moderately strong alignment” with it. Microsoft says that LazyGraphRAG, a later variant, cut indexing costs to those of vector retrieval: 0.1 percent of full GraphRAG’s.

Independent evaluations are more mixed. A systematic comparison by Haoyu Han and colleagues, last revised in March 2026, found that the two approaches “exhibit complementary behaviors rather than a consistent winner.” Plain retrieval did better “on single-hop and detail-oriented factual queries,” while GraphRAG did better “on multi-hop, reasoning-intensive questions.” The authors add that “GraphRAG is not free,” citing the higher construction cost, retrieval latency, and storage it often incurs. They also found that judge-based comparisons like Microsoft’s can be “highly sensitive to the presentation order” of the answers.

Zhishang Xiang and colleagues, who built a benchmark called GraphRAG-Bench, found the same split. Plain retrieval matched or beat GraphRAG on simple fact retrieval, while GraphRAG did better on complex reasoning, contextual summarization, and creative generation. Both studies test graphs that a model builds from documents. The memory above differs: a model extracts its facts, but the application defines its schema. It answers only the questions that schema covers, and only as accurately as the facts written to it.

Since August 2026, a notice in the GraphRAG README has described the project as “largely in maintenance mode”: no new features, but bug fixes and dependency updates as needed. The same notice observes that “the capabilities of frontier models have changed dramatically” since the first release in July 2024. The idea outlives the package.

Use a graph where the questions are about relationships: who connects to whom, what depends on what, and what changed. Use vectors to find the passages that answer the rest. The two also combine well: routing each question to the better method, or merging their evidence, improved overall question answering in the tests by Han and colleagues.

A Checklist for Your First Knowledge Graph

Further Reading