Property graphs let retrieval applications represent named entities and the relationships between them instead of relying only on text similarity. A LlamaIndex PropertyGraphIndex keeps those graph paths connected to their source text, which makes service dependencies and other linked facts available for graph-aware retrieval.
The default extraction path can ask an LLM to infer triples from unstructured text. This local build uses explicit EntityNode and Relation objects with ImplicitPathExtractor, so every expected relationship is known before indexing and can be checked without an external model or API key.
The completed script stores three entities and two directed relationships, persists the index, then reloads and checks both paths from the recovered graph store. MockLLM and MockEmbedding keep the smoke test offline; production applications can retain the same graph construction flow while selecting their own models and property graph store.
Steps to build a LlamaIndex property graph index:
- Create property_graph_index.py with the imports and offline model settings.
$ cat > property_graph_index.py <<'PY' from pathlib import Path from llama_index.core import ( PropertyGraphIndex, Settings, StorageContext, load_index_from_storage, ) from llama_index.core.embeddings import MockEmbedding from llama_index.core.graph_stores import EntityNode, Relation from llama_index.core.graph_stores.types import KG_NODES_KEY, KG_RELATIONS_KEY from llama_index.core.indices.property_graph import ImplicitPathExtractor from llama_index.core.llms import MockLLM from llama_index.core.schema import TextNode storage_dir = Path("property_graph_storage") Settings.llm = MockLLM(max_tokens=32) Settings.embed_model = MockEmbedding(embed_dim=8) PY
- Define the three graph entities after the model settings.
$ cat >> property_graph_index.py <<'PY' service = EntityNode(name="CheckoutService", label="service") database = EntityNode(name="PostgreSQL", label="database") cache = EntityNode(name="Redis", label="cache") PY
- Attach the known entities and relationships to one source text node.
$ cat >> property_graph_index.py <<'PY' source_node = TextNode( text="CheckoutService writes orders to PostgreSQL and reads carts from Redis.", id_="checkout-service-note", metadata={ KG_NODES_KEY: [service, database, cache], KG_RELATIONS_KEY: [ Relation(label="WRITES_TO", source_id=service.id, target_id=database.id), Relation(label="READS_FROM", source_id=service.id, target_id=cache.id), ], }, ) PY
ImplicitPathExtractor reads graph metadata already attached to the source node. SimpleLLMPathExtractor or a schema-based extractor is appropriate when relationships must be inferred from unstructured text.
- Append property graph construction to the script.
$ cat >> property_graph_index.py <<'PY' index = PropertyGraphIndex( nodes=[source_node], kg_extractors=[ImplicitPathExtractor()], embed_model=Settings.embed_model, llm=Settings.llm, show_progress=False, ) PY
- Append the storage round-trip section to the script.
$ cat >> property_graph_index.py <<'PY' index.storage_context.persist(persist_dir=str(storage_dir)) loaded_index = load_index_from_storage( StorageContext.from_defaults(persist_dir=str(storage_dir)) ) PY
- Append reloaded graph assertions and output to the script.
$ cat >> property_graph_index.py <<'PY' loaded_store = loaded_index.property_graph_store loaded_nodes = loaded_store.get() loaded_entities = sorted( graph_node.name for graph_node in loaded_nodes if graph_node.label != "text_chunk" ) loaded_service = next( graph_node for graph_node in loaded_nodes if isinstance(graph_node, EntityNode) and graph_node.name == "CheckoutService" ) loaded_paths = sorted( f"{source.name} -[{relation.label}]-> {target.name}" for source, relation, target in loaded_store.get_rel_map( [loaded_service], depth=1 ) ) assert loaded_entities == ["CheckoutService", "PostgreSQL", "Redis"] assert loaded_paths == [ "CheckoutService -[READS_FROM]-> Redis", "CheckoutService -[WRITES_TO]-> PostgreSQL", ] assert len(loaded_nodes) == 4 print(f"reloaded entity nodes: {', '.join(loaded_entities)}") print("reloaded service paths:") for path in loaded_paths: print(f"- {path}") print(f"reloaded graph nodes: {len(loaded_nodes)}") PY
- Run the completed script to verify the stored relationships and reloaded graph.
$ python3 property_graph_index.py reloaded entity nodes: CheckoutService, PostgreSQL, Redis reloaded service paths: - CheckoutService -[READS_FROM]-> Redis - CheckoutService -[WRITES_TO]-> PostgreSQL reloaded graph nodes: 4
The reloaded count includes the source text chunk and the three entity nodes. A missing entity, relationship, or persisted node raises AssertionError instead of printing the success transcript.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.