Skip to content

Commit

Permalink
add FalkorDB support
Browse files Browse the repository at this point in the history
  • Loading branch information
Naseem77 committed Dec 15, 2024
1 parent fd433fd commit 4ff411d
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 0 deletions.
28 changes: 28 additions & 0 deletions examples/src/graph_db_falkordb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { FalkorDBGraph } from "@langchain/community/graphs/falkordb_graph";
import { OpenAI } from "@langchain/llms/openai";
import { GraphCypherQAChain } from "langchain/chains/graph_qa/cypher";

/**
* This example uses FalkorDB database, which is native graph database.
* To set it up follow the instructions on https://docs.falkordb.com/.
*/

const url = "bolt://localhost:6379";

const graph = await FalkorDBGraph.initialize({ url });
const model = new OpenAI({ temperature: 0 });

// Populate the database with two nodes and a relationship
await graph.query(
"CREATE (a:Actor {name:'Bruce Willis'})" +
"-[:ACTED_IN]->(:Movie {title: 'Pulp Fiction'})"
);

const chain = GraphCypherQAChain.fromLLM({
llm: model,
graph,
});

const res = await chain.run("Who played in Pulp Fiction?");
console.log(res);
// Bruce Willis played in Pulp Fiction.
1 change: 1 addition & 0 deletions langchain/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ NEO4J_PASSWORD=ADD_YOURS_HERE
MEMGRAPH_URI=ADD_YOURS_HERE
MEMGRAPH_USERNAME=ADD_YOURS_HERE
MEMGRAPH_PASSWORD=ADD_YOURS_HERE
FALKORDB_URI=ADD_YOURS_HERE
CLOSEVECTOR_API_KEY=ADD_YOURS_HERE
CLOSEVECTOR_API_SECRET=ADD_YOURS_HERE
GPLACES_API_KEY=ADD_YOURS_HERE
Expand Down
2 changes: 2 additions & 0 deletions libs/langchain-community/langchain.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export const config = {
// graphs
"graphs/neo4j_graph": "graphs/neo4j_graph",
"graphs/memgraph_graph": "graphs/memgraph_graph",
"graphs/memgraph_graph": "graphs/falkordb_graph",
// document_compressors
"document_compressors/ibm": "document_compressors/ibm",
// document transformers
Expand Down Expand Up @@ -450,6 +451,7 @@ export const config = {
"cache/upstash_redis",
"graphs/neo4j_graph",
"graphs/memgraph_graph",
"graphs/falkordb_graph",
// document_compressors
"document_compressors/ibm",
// document_transformers
Expand Down
154 changes: 154 additions & 0 deletions libs/langchain-community/src/graphs/falkordb_graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { createClient } from "redis";
import { Graph } from "redisgraph.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any

interface FalkorDBGraphConfig {
url: string;
graph?: string;
enhancedSchema?: boolean;
}

interface StructuredSchema {
nodeProps: { [key: string]: string[] };
relProps: { [key: string]: string[] };
relationships: { start: string; type: string; end: string }[];
}

export class FalkorDBGraph {
private driver;
private graph: Graph;
private schema = "";
private structuredSchema: StructuredSchema = {
nodeProps: {},
relProps: {},
relationships: [],
};
private enhancedSchema: boolean;

constructor({ url, graph = "falkordb", enhancedSchema = false }: FalkorDBGraphConfig) {
try {
this.driver = createClient({ url });
this.graph = new Graph(graph); // Initialize the Graph instance
this.enhancedSchema = enhancedSchema;
} catch (error) {
throw new Error(
"Could not create a FalkorDB driver instance. Please check the connection details."
);
}
}

static async initialize(config: FalkorDBGraphConfig): Promise<FalkorDBGraph> {
const graph = new FalkorDBGraph(config);
await graph.verifyConnectivity();
await graph.refreshSchema();
return graph;
}

getSchema(): string {
return this.schema;
}

getStructuredSchema(): StructuredSchema {
return this.structuredSchema;
}

async query(query: string): Promise<any[]> {
const resultSet = await this.graph.query(query); // Run the query
const rows = [];

// Iterate through the ResultSet
while (resultSet.hasNext()) {
const record = resultSet.next(); // Get the next record
const keys = record.keys(); // Get column names
const values = record.values(); // Get values
const obj = Object.fromEntries(keys.map((key, i) => [key, values[i]])); // Map keys to values
rows.push(obj); // Add the object to rows
}

return rows;
}

async verifyConnectivity(): Promise<void> {
await this.driver.connect(); // Ensure the Redis client is connected
}

async refreshSchema(): Promise<void> {
const nodePropertiesQuery = `
MATCH (n)
WITH keys(n) as keys, labels(n) AS labels
UNWIND labels AS label
UNWIND keys AS key
WITH label, collect(DISTINCT key) AS properties
RETURN {label: label, properties: properties} AS output
`;

const relPropertiesQuery = `
MATCH ()-[r]->()
WITH keys(r) as keys, type(r) AS type
UNWIND keys AS key
WITH type, collect(DISTINCT key) AS properties
RETURN {type: type, properties: properties} AS output
`;

const relQuery = `
MATCH (n)-[r]->(m)
UNWIND labels(n) as src_label
UNWIND labels(m) as dst_label
RETURN DISTINCT {start: src_label, type: type(r), end: dst_label} AS output
`;

const nodeProperties = await this.query(nodePropertiesQuery);
const relationshipsProperties = await this.query(relPropertiesQuery);
const relationships = await this.query(relQuery);

this.structuredSchema = {
nodeProps: Object.fromEntries(
nodeProperties.map((el: { output: { label: string; properties: string[] } }) => [el.output.label, el.output.properties])
),
relProps: Object.fromEntries(
relationshipsProperties.map((el: { output: { type: string; properties: string[] } }) => [el.output.type, el.output.properties])
),
relationships: relationships.map((el: { output: { start: string; type: string; end: string } }) => el.output),
};

if (this.enhancedSchema) {
this.enhanceSchemaDetails();
}

this.schema = this.formatSchema();
}

private async enhanceSchemaDetails(): Promise<void> {
console.log("Enhanced schema details not yet implemented for FalkorDB.");
}

private formatSchema(): string {
const { nodeProps, relProps, relationships } = this.structuredSchema;

const formattedNodeProps = Object.entries(nodeProps)
.map(([label, props]) => `${label}: {${props.join(", ")}}`)
.join("\n");

const formattedRelProps = Object.entries(relProps)
.map(([type, props]) => `${type}: {${props.join(", ")}}`)
.join("\n");

const formattedRelationships = relationships
.map((rel) => `(:${rel.start}) -[:${rel.type}]-> (:${rel.end})`)
.join("\n");

return [
"Node properties are the following:",
formattedNodeProps,
"Relationship properties are the following:",
formattedRelProps,
"The relationships are the following:",
formattedRelationships,
].join("\n");
}

async close(): Promise<void> {
await this.driver.quit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable no-process-env */

import { test } from "@jest/globals";
import { FalkorDBGraph } from "../falkordb_graph.js";

describe("FalkorDB Graph Tests", () => {
const url = process.env.FALKORDB_URI as string;
let graph: FalkorDBGraph;

beforeEach(async () => {
graph = await FalkorDBGraph.initialize({ url });
await graph.query("MATCH (n) DETACH DELETE n");
});

afterEach(async () => {
await graph.close();
});

test("Test that FalkorDN database is correctly instantiated and connected", async () => {
expect(url).toBeDefined();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return graph.query('RETURN "test" AS output').then((output: any) => {
const expectedOutput = [{ output: "test" }];
expect(output).toEqual(expectedOutput);
});
});
});
1 change: 1 addition & 0 deletions libs/langchain-community/src/load/import_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export const optionalImportEntrypoints: string[] = [
"langchain_community/retrievers/zep_cloud",
"langchain_community/graphs/neo4j_graph",
"langchain_community/graphs/memgraph_graph",
"langchain_community/graphs/falkordb_graph",
"langchain_community/document_compressors/ibm",
"langchain_community/document_transformers/html_to_text",
"langchain_community/document_transformers/mozilla_readability",
Expand Down

0 comments on commit 4ff411d

Please sign in to comment.