Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ EMBEDDING_BINDING_HOST=http://localhost:11434
### Graph Storage (Recommended for production deployment)
# LIGHTRAG_GRAPH_STORAGE=Neo4JStorage
# LIGHTRAG_GRAPH_STORAGE=MemgraphStorage
# LIGHTRAG_GRAPH_STORAGE=FalkorDBStorage

### PostgreSQL
# LIGHTRAG_KV_STORAGE=PGKVStorage
Expand Down Expand Up @@ -253,6 +254,12 @@ NEO4J_CONNECTION_ACQUISITION_TIMEOUT=30
MAX_TRANSACTION_RETRY_TIME=30
# NEO4J_WORKSPACE=forced_workspace_name

### FalkorDB Configuration
FALKORDB_URI=falkordb://xxxxxxxx.falkordb.cloud
FALKORDB_GRAPH_NAME=lightrag_graph
# FALKORDB_HOST=localhost
# FALKORDB_PORT=6379

### MongoDB Configuration
MONGO_URI=mongodb://root:root@localhost:27017/
#MONGO_URI=mongodb+srv://xxxx
Expand Down
60 changes: 60 additions & 0 deletions examples/falkordb_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python
"""
Example of using LightRAG with FalkorDB
"""

import asyncio
import os
from lightrag import LightRAG, QueryParam
from lightrag.llm import openai_complete_if_cache, openai_embedding
from lightrag.utils import EmbeddingFunc


async def main():
"""Example usage of LightRAG with FalkorDB"""

# Set up environment for FalkorDB
os.environ["LIGHTRAG_GRAPH_STORAGE"] = "FalkorDBStorage"
os.environ["FALKORDB_HOST"] = "localhost"
os.environ["FALKORDB_PORT"] = "6379"
os.environ["FALKORDB_GRAPH_NAME"] = "lightrag_example"

# Initialize LightRAG with FalkorDB
rag = LightRAG(
working_dir="./falkordb_example",
llm_model_func=openai_complete_if_cache, # Use your preferred LLM
embedding_func=EmbeddingFunc(
embedding_dim=1536,
max_token_size=8192,
func=openai_embedding # Use your preferred embedding
),
)

# Example text to process
sample_text = """
FalkorDB is a high-performance graph database built on Redis.
It supports Cypher queries and provides excellent performance for graph operations.
LightRAG can now use FalkorDB as its graph storage backend, enabling scalable
knowledge graph operations with Redis-based persistence.
"""

print("Inserting text into LightRAG with FalkorDB backend...")
await rag.ainsert(sample_text)

print("Querying the knowledge graph...")
response = await rag.aquery(
"What is FalkorDB and how does it relate to LightRAG?",
param=QueryParam(mode="hybrid")
)

print("Response:", response)


if __name__ == "__main__":
print("LightRAG with FalkorDB Example")
print("==============================")
print("Note: This requires FalkorDB running on localhost:6379")
print("You can start FalkorDB with: docker run -p 6379:6379 falkordb/falkordb")
print()

asyncio.run(main())
266 changes: 266 additions & 0 deletions examples/graph_visual_with_falkordb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
import os
import json
import xml.etree.ElementTree as ET
import falkordb

# Constants
WORKING_DIR = "./dickens"
BATCH_SIZE_NODES = 500
BATCH_SIZE_EDGES = 100

# FalkorDB connection credentials
FALKORDB_HOST = "localhost"
FALKORDB_PORT = 6379
FALKORDB_GRAPH_NAME = "dickens_graph"


def xml_to_json(xml_file):
try:
tree = ET.parse(xml_file)
root = tree.getroot()

# Print the root element's tag and attributes to confirm the file has been correctly loaded
print(f"Root element: {root.tag}")
print(f"Root attributes: {root.attrib}")

data = {"nodes": [], "edges": []}

# Use namespace
namespace = {"": "http://graphml.graphdrawing.org/xmlns"}

for node in root.findall(".//node", namespace):
node_data = {
"id": node.get("id").strip('"'),
"entity_type": node.find("./data[@key='d1']", namespace).text.strip('"')
if node.find("./data[@key='d1']", namespace) is not None
else "",
"description": node.find("./data[@key='d2']", namespace).text
if node.find("./data[@key='d2']", namespace) is not None
else "",
"source_id": node.find("./data[@key='d3']", namespace).text
if node.find("./data[@key='d3']", namespace) is not None
else "",
}
data["nodes"].append(node_data)

for edge in root.findall(".//edge", namespace):
edge_data = {
"source": edge.get("source").strip('"'),
"target": edge.get("target").strip('"'),
"weight": float(edge.find("./data[@key='d5']", namespace).text)
if edge.find("./data[@key='d5']", namespace) is not None
else 1.0,
"description": edge.find("./data[@key='d6']", namespace).text
if edge.find("./data[@key='d6']", namespace) is not None
else "",
"keywords": edge.find("./data[@key='d7']", namespace).text
if edge.find("./data[@key='d7']", namespace) is not None
else "",
"source_id": edge.find("./data[@key='d8']", namespace).text
if edge.find("./data[@key='d8']", namespace) is not None
else "",
}
data["edges"].append(edge_data)

return data

except ET.ParseError as e:
print(f"Error parsing XML: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None


def insert_nodes_and_edges_to_falkordb(data):
"""Insert graph data into FalkorDB"""
try:
# Connect to FalkorDB
db = falkordb.FalkorDB(host=FALKORDB_HOST, port=FALKORDB_PORT)
graph = db.select_graph(FALKORDB_GRAPH_NAME)

print(f"Connected to FalkorDB at {FALKORDB_HOST}:{FALKORDB_PORT}")
print(f"Using graph: {FALKORDB_GRAPH_NAME}")

nodes = data["nodes"]
edges = data["edges"]

print(f"Total nodes to insert: {len(nodes)}")
print(f"Total edges to insert: {len(edges)}")

# Insert nodes in batches
for i in range(0, len(nodes), BATCH_SIZE_NODES):
batch_nodes = nodes[i : i + BATCH_SIZE_NODES]

# Build UNWIND query for batch insert
query = """
UNWIND $nodes AS node
CREATE (n:Entity {
entity_id: node.id,
entity_type: node.entity_type,
description: node.description,
source_id: node.source_id
})
"""

graph.query(query, {"nodes": batch_nodes})
print(f"Inserted nodes {i+1} to {min(i + BATCH_SIZE_NODES, len(nodes))}")

# Insert edges in batches
for i in range(0, len(edges), BATCH_SIZE_EDGES):
batch_edges = edges[i : i + BATCH_SIZE_EDGES]

# Build UNWIND query for batch insert
query = """
UNWIND $edges AS edge
MATCH (source:Entity {entity_id: edge.source})
MATCH (target:Entity {entity_id: edge.target})
CREATE (source)-[r:DIRECTED {
weight: edge.weight,
description: edge.description,
keywords: edge.keywords,
source_id: edge.source_id
}]-(target)
"""

graph.query(query, {"edges": batch_edges})
print(f"Inserted edges {i+1} to {min(i + BATCH_SIZE_EDGES, len(edges))}")

print("Data insertion completed successfully!")

# Print some statistics
node_count_result = graph.query("MATCH (n:Entity) RETURN count(n) AS count")
edge_count_result = graph.query("MATCH ()-[r:DIRECTED]-() RETURN count(r) AS count")

node_count = node_count_result.result_set[0][0] if node_count_result.result_set else 0
edge_count = edge_count_result.result_set[0][0] if edge_count_result.result_set else 0

print(f"Final statistics:")
print(f"- Nodes in database: {node_count}")
print(f"- Edges in database: {edge_count}")

except Exception as e:
print(f"Error inserting data into FalkorDB: {e}")


def query_graph_data():
"""Query and display some sample data from FalkorDB"""
try:
# Connect to FalkorDB
db = falkordb.FalkorDB(host=FALKORDB_HOST, port=FALKORDB_PORT)
graph = db.select_graph(FALKORDB_GRAPH_NAME)

print("\n=== Sample Graph Data ===")

# Get some sample nodes
query = "MATCH (n:Entity) RETURN n.entity_id, n.entity_type, n.description LIMIT 5"
result = graph.query(query)

print("\nSample Nodes:")
if result.result_set:
for record in result.result_set:
print(f"- {record[0]} ({record[1]}): {record[2][:100]}...")

# Get some sample edges
query = """
MATCH (a:Entity)-[r:DIRECTED]-(b:Entity)
RETURN a.entity_id, b.entity_id, r.weight, r.description
LIMIT 5
"""
result = graph.query(query)

print("\nSample Edges:")
if result.result_set:
for record in result.result_set:
print(f"- {record[0]} -> {record[1]} (weight: {record[2]}): {record[3][:100]}...")

# Get node degree statistics
query = """
MATCH (n:Entity)
OPTIONAL MATCH (n)-[r]-()
WITH n, count(r) AS degree
RETURN min(degree) AS min_degree, max(degree) AS max_degree, avg(degree) AS avg_degree
"""
result = graph.query(query)

print("\nNode Degree Statistics:")
if result.result_set:
record = result.result_set[0]
print(f"- Min degree: {record[0]}")
print(f"- Max degree: {record[1]}")
print(f"- Avg degree: {record[2]:.2f}")

except Exception as e:
print(f"Error querying FalkorDB: {e}")


def clear_graph():
"""Clear all data from the FalkorDB graph"""
try:
db = falkordb.FalkorDB(host=FALKORDB_HOST, port=FALKORDB_PORT)
graph = db.select_graph(FALKORDB_GRAPH_NAME)

# Delete all nodes and relationships
graph.query("MATCH (n) DETACH DELETE n")
print("Graph cleared successfully!")

except Exception as e:
print(f"Error clearing graph: {e}")


def main():
xml_file = os.path.join(WORKING_DIR, "graph_chunk_entity_relation.graphml")

if not os.path.exists(xml_file):
print(f"Error: File {xml_file} not found. Please ensure the GraphML file exists.")
print("This file is typically generated by LightRAG after processing documents.")
return

print("FalkorDB Graph Visualization Example")
print("====================================")
print(f"Processing file: {xml_file}")
print(f"FalkorDB connection: {FALKORDB_HOST}:{FALKORDB_PORT}")
print(f"Graph name: {FALKORDB_GRAPH_NAME}")
print()

# Parse XML to JSON
print("1. Parsing GraphML file...")
data = xml_to_json(xml_file)
if data is None:
print("Failed to parse XML file.")
return

print(f" Found {len(data['nodes'])} nodes and {len(data['edges'])} edges")

# Ask user what to do
while True:
print("\nOptions:")
print("1. Clear existing graph data")
print("2. Insert data into FalkorDB")
print("3. Query sample data")
print("4. Exit")

choice = input("\nSelect an option (1-4): ").strip()

if choice == "1":
print("\n2. Clearing existing graph data...")
clear_graph()

elif choice == "2":
print("\n2. Inserting data into FalkorDB...")
insert_nodes_and_edges_to_falkordb(data)

elif choice == "3":
print("\n3. Querying sample data...")
query_graph_data()

elif choice == "4":
print("Goodbye!")
break

else:
print("Invalid choice. Please try again.")


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions lightrag/kg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"implementations": [
"NetworkXStorage",
"Neo4JStorage",
"FalkorDBStorage",
"PGGraphStorage",
"MongoGraphStorage",
"MemgraphStorage",
Expand Down Expand Up @@ -51,6 +52,7 @@
# Graph Storage Implementations
"NetworkXStorage": [],
"Neo4JStorage": ["NEO4J_URI", "NEO4J_USERNAME", "NEO4J_PASSWORD"],
"FalkorDBStorage": ["FALKORDB_URI"],
"MongoGraphStorage": [],
"MemgraphStorage": ["MEMGRAPH_URI"],
"AGEStorage": [
Expand Down Expand Up @@ -85,6 +87,7 @@
"NanoVectorDBStorage": ".kg.nano_vector_db_impl",
"JsonDocStatusStorage": ".kg.json_doc_status_impl",
"Neo4JStorage": ".kg.neo4j_impl",
"FalkorDBStorage": ".kg.falkordb_impl",
"MilvusVectorDBStorage": ".kg.milvus_impl",
"MongoKVStorage": ".kg.mongo_impl",
"MongoDocStatusStorage": ".kg.mongo_impl",
Expand Down
Loading
Loading