$npx -y skills add neo4j-contrib/neo4j-skills --skill neo4j-document-import-skillIngests unstructured and semi-structured documents into Neo4j as a knowledge graph.
| 1 | # Neo4j Document Import Skill |
| 2 | |
| 3 | ## When to Use |
| 4 | |
| 5 | - Ingesting PDFs, HTML, plain text, Markdown into Neo4j as a knowledge graph |
| 6 | - Chunking documents and storing `:Chunk` nodes with embeddings |
| 7 | - Extracting entities and relationships from text with an LLM |
| 8 | - Using `SimpleKGPipeline` (neo4j-graphrag) programmatically |
| 9 | - Using Neo4j LLM Graph Builder (no-code web UI) |
| 10 | - Loading semi-structured JSON via `apoc.load.json` |
| 11 | - Connecting LangChain or LlamaIndex document loaders to Neo4j |
| 12 | |
| 13 | ## When NOT to Use |
| 14 | |
| 15 | - **Structured CSV / relational data** → `neo4j-import-skill` |
| 16 | - **GraphRAG retrieval after ingestion** → `neo4j-graphrag-skill` |
| 17 | - **Vector index creation** → `neo4j-vector-search-skill` |
| 18 | - **Cypher query writing** → `neo4j-cypher-skill` |
| 19 | |
| 20 | --- |
| 21 | |
| 22 | ## Approach Decision Table |
| 23 | |
| 24 | | Situation | Approach | |
| 25 | |---|---| |
| 26 | | No code; drag-and-drop UX wanted | LLM Graph Builder web UI | |
| 27 | | Programmatic pipeline; PDFs/text | `SimpleKGPipeline` (neo4j-graphrag) | |
| 28 | | JSON / REST API responses | `apoc.load.json` or Python + UNWIND | |
| 29 | | LangChain already in stack | `Neo4jGraph` + document loader | |
| 30 | | LlamaIndex already in stack | `Neo4jQueryEngine` / `Neo4jVectorStore` | |
| 31 | | Chunk-only (no entity extraction) | Manual chunking + MERGE pattern | |
| 32 | |
| 33 | --- |
| 34 | |
| 35 | ## Install |
| 36 | |
| 37 | ```bash |
| 38 | pip install neo4j-graphrag # includes SimpleKGPipeline |
| 39 | pip install neo4j-graphrag[openai] # + OpenAI LLM/embedder |
| 40 | pip install neo4j-graphrag[anthropic] # + Anthropic Claude |
| 41 | pip install neo4j-graphrag[google] # + Vertex AI / Gemini |
| 42 | pip install neo4j-graphrag[bedrock] # + Amazon Bedrock (boto3) — added v1.15.0 |
| 43 | pip install neo4j-graphrag[ollama] # + Ollama (local) |
| 44 | pip install neo4j-graphrag[mistralai] # + MistralAI |
| 45 | pip install neo4j-graphrag[fuzzy-matching] # + FuzzyMatchResolver (rapidfuzz) |
| 46 | # spaCy entity resolver (Python <= 3.13 only — unsupported on 3.14+): |
| 47 | pip install neo4j-graphrag[nlp] |
| 48 | ``` |
| 49 | |
| 50 | Requires: `neo4j>=5.17.0` (driver 6.x supported), Python>=3.10, Neo4j>=5.18.1 (Aura>=5.18.0). |
| 51 | |
| 52 | --- |
| 53 | |
| 54 | ## Step 1 — Define Graph Schema |
| 55 | |
| 56 | Schema controls what the LLM extracts. Define before pipeline construction. |
| 57 | |
| 58 | ```python |
| 59 | # Option A — Simple string lists (LLM infers descriptions) |
| 60 | entities = ["Person", "Organization", "Location", "Product", "Event"] |
| 61 | relations = ["WORKS_AT", "LOCATED_IN", "KNOWS", "MENTIONS", "PART_OF"] |
| 62 | patterns = [ |
| 63 | ("Person", "WORKS_AT", "Organization"), |
| 64 | ("Organization", "LOCATED_IN", "Location"), |
| 65 | ("Person", "KNOWS", "Person"), |
| 66 | ("Article", "MENTIONS", "Organization"), |
| 67 | ] |
| 68 | |
| 69 | # Option B — Rich GraphSchema (production; best extraction quality) |
| 70 | from neo4j_graphrag.experimental.components.schema import ( |
| 71 | GraphSchema, NodeType, RelationshipType, PropertyType, ConstraintType |
| 72 | ) |
| 73 | schema = GraphSchema( |
| 74 | node_types=[ |
| 75 | NodeType( |
| 76 | label="Person", |
| 77 | description="A human individual", |
| 78 | properties=[ |
| 79 | PropertyType(name="name", type="STRING"), |
| 80 | PropertyType(name="role", type="STRING"), |
| 81 | ], |
| 82 | ), |
| 83 | NodeType( |
| 84 | label="Organization", |
| 85 | description="A company or institution", |
| 86 | properties=[ |
| 87 | PropertyType(name="name", type="STRING"), |
| 88 | PropertyType(name="industry", type="STRING"), |
| 89 | ], |
| 90 | ), |
| 91 | ], |
| 92 | relationship_types=[ |
| 93 | RelationshipType(label="WORKS_AT", description="Employment relationship"), |
| 94 | ], |
| 95 | patterns=[("Person", "WORKS_AT", "Organization")], |
| 96 | # Optional: constraints emitted to ParquetWriter metadata (v1.15.0+) |
| 97 | constraints=[ |
| 98 | ConstraintType(label="Person", property_name="name", type="UNIQUENESS"), |
| 99 | ConstraintType(label="Organization", property_name="name", type="KEY"), |
| 100 | ], |
| 101 | ) |
| 102 | |
| 103 | # Option C — Auto-extract schema from text (no constraints) |
| 104 | schema = "EXTRACTED" # LLM infers types; noisier output |
| 105 | schema = "FREE" # No schema guidance; most noise |
| 106 | ``` |
| 107 | |
| 108 | Use Option B for production; Option A for prototyping; `"EXTRACTED"` only for exploration. |
| 109 | |
| 110 | --- |
| 111 | |
| 112 | ## Step 2 — SimpleKGPipeline Setup |
| 113 | |
| 114 | ```python |
| 115 | import asyncio |
| 116 | from neo4j import GraphDatabase |
| 117 | from |