GraphStore wraps the backend of your choice behind a single API. Call connect() (or use it as a context manager) before running any queries:
from semantica.graph_store import GraphStorestore = GraphStore( backend="neo4j", uri="bolt://localhost:7687", user="neo4j", password="password",)store.connect()# Create a nodenode = store.create_node( labels=["Person"], properties={"name": "Alice", "role": "Engineer"},)print(node["id"]) # Neo4j internal integer ID# Create a relationshipstore.create_relationship( start_node_id=node1_id, end_node_id=node2_id, rel_type="WORKS_FOR", properties={"since": 2022},)# Execute a Cypher queryresults = store.query( "MATCH (p:Person)-[:WORKS_FOR]->(o:Organization) WHERE o.name = $org RETURN p", parameters={"org": "Acme Corp"},)store.close()
Use as a context manager to close the connection automatically:
with GraphStore(backend="neo4j", uri="bolt://localhost:7687", user="neo4j", password="password") as store: store.create_node(labels=["Person"], properties={"name": "Bob"})
Call connect() before any operations.GraphStore does not connect automatically on construction. Either call store.connect() explicitly or use the context manager form with GraphStore(...) as store:.
Best for: production workloads, complex Cypher queries, Bloom visualization.
pip install falkordb
store = GraphStore( backend="falkordb", host="localhost", port=6379, graph_name="semantica",)store.connect()
Best for: ultra-low latency queries over Redis protocol, edge deployments.
pip install psycopg2-binary
store = GraphStore( backend="age", # or "apache_age" connection_string="host=localhost dbname=agedb user=postgres password=secret", graph_name="semantica",)store.connect()
Best for: teams already running PostgreSQL who want graph queries without a separate service.
Apache AGE requires the PostgreSQL extension installed.backend="age" calls the AGE extension functions. If AGE is not installed in your PostgreSQL instance, you’ll get a ProgrammingError. See the Apache AGE docs for setup.
pip install neo4j boto3
store = GraphStore( backend="neptune", # or "amazon_neptune" endpoint="your-cluster.cluster-xxxx.us-east-1.neptune.amazonaws.com", port=8182, region="us-east-1", iam_auth=True, # uses boto3 default credential chain)store.connect()# OpenCypher queries via Bolt protocolresults = store.query( "MATCH (p:Person)-[:WORKS_FOR]->(o:Organization) RETURN p, o")
Best for: managed AWS deployments. Neptune uses the Bolt protocol for OpenCypher queries: the same query API used for Neo4j.
Amazon Neptune uses iam_auth=, not use_iam_auth=. The AmazonNeptuneStore and the GraphStore Neptune backend both use iam_auth: bool = True as the parameter name.
Use create_nodes() for bulk loading. Individual create_node() calls issue one network round-trip each. create_nodes(list) is faster for initial graph population.
Use QueryEngine caching for read-heavy workloads. Access the engine via store.query_engine. Call engine.execute(query, use_cache=True) to cache identical queries in-process. Call engine.clear_cache() after writes that invalidate results.
Use parameterized queries, never string interpolation.store.query("WHERE n.name = $name", parameters={"name": user_input}) prevents Cypher injection attacks. Never use f"WHERE n.name = '{user_input}'".
betweenness_centrality(), pagerank(), detect_communities(), and all_paths() are not implemented. Use Neo4j GDS procedures directly via store.execute_query() for those algorithms.
# Index for fast label-property lookups: use property_name= not property=store.create_index(label="Person", property_name="name")store.create_index(label="Organization", property_name="id")# Graph statisticsstats = store.get_stats()
Create indexes before bulk loading.store.create_index(label="Person", property_name="name") makes MATCH queries on name orders of magnitude faster. Without indexes, every query does a full scan. Create indexes first, then load data.
create_index parameter is property_name=, not property=.store.create_index(label="Person", property_name="name"): using property= will be silently ignored.
from semantica.graph_store import GraphStorestore = GraphStore(backend="neo4j", uri="bolt://localhost:7687", user="neo4j", password="password")store.connect()# Create indexes first for speedstore.create_index("Person", property_name="name")store.create_index("Organization", property_name="name")# Load entities as nodescreated = store.create_nodes([ {"labels": [e["type"]], "properties": {"name": e["text"], "id": e["id"]}} for e in entities])# Map entity IDs to backend node IDsid_map = {e["id"]: node["id"] for e, node in zip(entities, created)}# Load relationshipsfor rel in relationships: if rel["source_id"] in id_map and rel["target_id"] in id_map: store.create_relationship( start_node_id=id_map[rel["source_id"]], end_node_id=id_map[rel["target_id"]], rel_type=rel["type"], )store.close()
# Always use parameters, never string interpolationresults = store.query( "MATCH (p:Person)-[:WORKS_FOR]->(o:Organization) " "WHERE o.name = $org AND p.role = $role RETURN p.name", parameters={"org": user_input_org, "role": user_input_role},)
# Walk 2 hops of KNOWS relationshipsneighbors = store.get_neighbors( node_id=alice_id, rel_type="KNOWS", direction="out", depth=2,)for n in neighbors: print(n["properties"]["name"])
AGE supports one primary label per vertex. If you pass multiple labels, the first is used as the AGE label and the rest are stored in a labels property array. Parameterized queries use literal inlining internally (AGE does not support $param binding inside cypher() calls): the store handles escaping automatically.
KG Module
Build the graph before persisting it.
Triplet Store
RDF triple store for semantic web and SPARQL queries.
Visualization
Visualize graphs stored in any backend.
Context
AgentContext uses GraphStore for memory retrieval.