Neo4j import plan
Date: 2026-06-01
CSV source: data/graph_exports/ (regenerated by python3 scripts/refresh_all.py)
Executable templates: graph_queries/neo4j_constraints.cypher, neo4j_import.cypher, analysis_queries.cypher
Neo4j is optional for this project. The same CSVs support pandas, NetworkX, or DuckDB. This plan describes a Community Edition import path when you want Cypher exploration.
1. Recommended node labels
| Label | Source | Notes |
|---|---|---|
Person |
nodes_people.csv |
person_id key |
Resource |
nodes_resources.csv |
Facility/program/partner; 78 rows (non-course) |
Grant |
nodes_grants.csv |
grant_id key |
Publication |
nodes_publications.csv |
|
Output |
nodes_outputs.csv |
Respect node include_in_graph on output records |
Course |
Phase 2 — data/resource_courses.yaml |
Not in current CSV export |
Equipment |
Phase 2 — data/resource_equipment.yaml |
Line items; link to Resource |
Unit |
Derived from prefix on Resource |
Optional grouping node (CDM, CSH, …) |
Topic |
nodes_topics.csv |
Canonical research vocabulary; topic_id key |
2. Recommended relationship types
| Neo4j type | CSV / YAML source | Endpoints | Property highlights |
|---|---|---|---|
ABOUT_TOPIC |
edges_entity_topic.csv |
Person/Grant/Publication/Output/Course → Topic | source (registry, derived, course_theme) |
SUBTOPIC_OF |
edges_topic_parent.csv |
Topic → Topic | Parent hierarchy roll-up |
CONNECTED_TO_RESOURCE |
edges_person_resource.csv |
Person → Resource | relationship, edge_type, evidence_level, include_in_graph, include_in_public_site |
PI_ON |
edges_person_grant.csv |
Person → Grant | role, evidence_level |
FUNDED_BY |
(optional inverse) | Grant → Person | Mirror of PI_ON if bidirectional traversal needed |
SUPPORTS_RESOURCE |
edges_grant_resource.csv |
Grant → Resource | relationship, evidence_level |
AUTHORED |
edges_person_publication.csv |
Person → Publication | role |
DOCUMENTS_RESOURCE |
edges_publication_resource.csv |
Publication → Resource | High-evidence facility use |
CREATED_OUTPUT |
edges_person_output.csv |
Person → Output | role |
OUTPUT_USES_RESOURCE |
edges_output_resource.csv |
Output → Resource | |
COURSE_USES_RESOURCE |
data/course_resource_links.yaml |
Course → Resource | Phase 2 export |
RESOURCE_HAS_EQUIPMENT |
data/resource_equipment.yaml |
Resource → Equipment | Phase 2 export |
AFFILIATED_WITH |
Subset of person–resource | Person → Resource | Where relationship=affiliated_faculty or evidence_level=affiliation |
ADMINISTERED_BY |
Subset of person–resource | Person → Resource | administrator / administers; often excluded from default viz |
Mapping note: You may model all person–resource rows as CONNECTED_TO_RESOURCE and filter by relationship + evidence_level, or split weak types into AFFILIATED_WITH / ADMINISTERED_BY at import time for simpler default queries.
3. CSV file → graph mapping
| CSV file | Graph element |
|---|---|
nodes_people.csv |
(:Person) |
nodes_resources.csv |
(:Resource) |
nodes_grants.csv |
(:Grant) |
nodes_publications.csv |
(:Publication) |
nodes_outputs.csv |
(:Output) |
edges_person_resource.csv |
(Person)-[:CONNECTED_TO_RESOURCE]->(Resource) |
edges_person_grant.csv |
(Person)-[:PI_ON]->(Grant) |
edges_grant_resource.csv |
(Grant)-[:SUPPORTS_RESOURCE]->(Resource) |
edges_person_publication.csv |
(Person)-[:AUTHORED]->(Publication) |
edges_publication_resource.csv |
(Publication)-[:DOCUMENTS_RESOURCE]->(Resource) |
edges_person_output.csv |
(Person)-[:CREATED_OUTPUT]->(Output) |
edges_output_resource.csv |
(Output)-[:OUTPUT_USES_RESOURCE]->(Resource) |
Not yet exported to CSV: course links, equipment lines, facility–facility links (data/course_resource_links.yaml, resource_equipment.yaml, resource Markdown). Add nodes_courses.csv / edges_course_resource.csv in a future export_graph_tables.py extension before importing those relationship types.
4. Edge types excluded from default visualizations
Apply these filters in browser apps and default Cypher (WHERE clauses):
| Pattern | Reason |
|---|---|
include_in_graph = 'false' |
Curated exclusion (weak evidence, UNI-001 anchors, courses) |
resource_id = 'UNI-001' on any technical edge |
ORS administrative hub — not a collaboration node |
relationship = 'grant_pi' AND resource_id = 'UNI-001' |
Must stay graph-excluded per person_link_rules.py |
evidence_level IN ['excluded', 'topical', 'administrative'] |
Weak or narrative-only |
evidence_level = 'affiliation' |
Unless user opts in to “show weak ties” |
Broad administrator to non-operational resources |
Associate provost / ORS roles |
Default viz profile: include_in_graph = 'true' AND evidence_level IN ['direct'] OR (direct + high confidence affiliation after manual review).
5. Suggested constraints
CREATE CONSTRAINT person_id IF NOT EXISTS FOR (p:Person) REQUIRE p.person_id IS UNIQUE;
CREATE CONSTRAINT resource_id IF NOT EXISTS FOR (r:Resource) REQUIRE r.resource_id IS UNIQUE;
CREATE CONSTRAINT grant_id IF NOT EXISTS FOR (g:Grant) REQUIRE g.grant_id IS UNIQUE;
CREATE CONSTRAINT publication_id IF NOT EXISTS FOR (p:Publication) REQUIRE p.publication_id IS UNIQUE;
CREATE CONSTRAINT output_id IF NOT EXISTS FOR (o:Output) REQUIRE o.output_id IS UNIQUE;
See graph_queries/neo4j_constraints.cypher for the full template.
6. Suggested indexes
| Index | Purpose |
|---|---|
Person(person_id) |
Lookup, merge on import |
Resource(resource_id), Resource(prefix) |
Unit filters |
Grant(grant_id) |
Award lookup |
Publication(publication_id), Output(output_id) |
Layer joins |
Relationship include_in_graph |
Fast default subgraph |
Relationship evidence_level |
Weak-edge audits |
Relationship properties are indexed in Neo4j 5+ via relationship key syntax where supported; otherwise filter after MATCH with property predicates (templates use WHERE).
7. Starter LOAD CSV import
Prerequisites:
- Run
python3 scripts/refresh_all.pyto refreshdata/graph_exports/. - Copy CSVs to Neo4j
import/directory or setfile:///URLs inneo4j_import.cypher. - Run constraints first (
neo4j_constraints.cypher). - Run
neo4j_import.cypher.
Import templates load all rows but set properties so queries can filter include_in_graph. Alternative: pre-filter CSVs in Python before import.
8. Starter analysis queries
Included in graph_queries/analysis_queries.cypher:
- Top resource hubs excluding UNI-001
- People connected to multiple resource prefixes (cross-unit)
- Grants linking people and resources (2-hop)
- Publications/resources with strong evidence
- Outputs/resources with strong evidence
- Resources with equipment but no people (requires equipment export or external join)
- People with grants but no publications or outputs
- Resources with courses but no people (phase 2)
- Cross-unit bridges
- Weak-edge audit
- False-hub audit (UNI-001, high-degree administrative)
9. Validation after import
Compare Neo4j counts to full-refresh-report.md:
| Metric | Expected (2026-06-01 baseline) |
|---|---|
| Person nodes | 115 |
| Resource nodes | 78 |
| Grant nodes | 33 |
| Publication nodes | 34 |
| Output nodes | 18 |
CONNECTED_TO_RESOURCE (include_in_graph=true) |
174 |
| PI_ON (graph) | 33 |
| SUPPORTS_RESOURCE (graph) | 9 |
Mismatch indicates stale CSVs or import filter errors—not necessarily wrong YAML.
10. Security
- Do not load local-only grant PDFs into Neo4j without access controls.
- Graph DB with internal evidence should not be exposed on the public MkDocs site.
- Align with ragmap-and-graph-analysis-plan.md §7.