Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add FalkorDB support #7367

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
30 changes: 30 additions & 0 deletions examples/src/chains/graph_db_falkordb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FalkorDBGraph } from "@langchain/community/graphs/falkordb";
import { OpenAI } from "@langchain/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'})"
);

await graph.refreshSchema();

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 @@ -227,6 +227,7 @@ export const config = {
// graphs
"graphs/neo4j_graph": "graphs/neo4j_graph",
"graphs/memgraph_graph": "graphs/memgraph_graph",
"graphs/falkordb": "graphs/falkordb",
// document_compressors
"document_compressors/ibm": "document_compressors/ibm",
// document transformers
Expand Down Expand Up @@ -463,6 +464,7 @@ export const config = {
//graphs
"graphs/neo4j_graph",
"graphs/memgraph_graph",
"graphs/falkordb",
// document_compressors
"document_compressors/ibm",
// document_transformers
Expand Down
18 changes: 18 additions & 0 deletions libs/langchain-community/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@
"mongodb": "^5.2.0",
"mysql2": "^3.9.8",
"neo4j-driver": "^5.17.0",
"falkordb": "^6.2.5",
"node-llama-cpp": "3.1.1",
"notion-to-md": "^3.1.0",
"officeparser": "^4.0.4",
Expand Down Expand Up @@ -324,6 +325,7 @@
"mongodb": ">=5.2.0",
"mysql2": "^3.9.8",
"neo4j-driver": "*",
"falkordb": "*",
"notion-to-md": "^3.1.0",
"officeparser": "^4.0.4",
"openai": "*",
Expand Down Expand Up @@ -644,6 +646,9 @@
"neo4j-driver": {
"optional": true
},
"falkordb": {
"optional": true
},
"notion-to-md": {
"optional": true
},
Expand Down Expand Up @@ -2365,6 +2370,15 @@
"import": "./graphs/memgraph_graph.js",
"require": "./graphs/memgraph_graph.cjs"
},
"./graphs/falkordb": {
"types": {
"import": "./graphs/falkordb.d.ts",
"require": "./graphs/falkordb.d.cts",
"default": "./graphs/falkordb.d.ts"
},
"import": "./graphs/falkordb.js",
"require": "./graphs/falkordb.cjs"
},
"./document_compressors/ibm": {
"types": {
"import": "./document_compressors/ibm.d.ts",
Expand Down Expand Up @@ -3929,6 +3943,10 @@
"graphs/memgraph_graph.js",
"graphs/memgraph_graph.d.ts",
"graphs/memgraph_graph.d.cts",
"graphs/falkordb.cjs",
"graphs/falkordb.js",
"graphs/falkordb.d.ts",
"graphs/falkordb.d.cts",
"document_compressors/ibm.cjs",
"document_compressors/ibm.js",
"document_compressors/ibm.d.ts",
Expand Down
158 changes: 158 additions & 0 deletions libs/langchain-community/src/graphs/falkordb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { FalkorDB, Graph } from "falkordb";
// 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: FalkorDB;

private graph: Graph;

private schema = "";

private structuredSchema: StructuredSchema = {
nodeProps: {},
relProps: {},
relationships: [],
};

private enhancedSchema: boolean;

constructor({ enhancedSchema = false }: FalkorDBGraphConfig) {
try {
this.enhancedSchema = enhancedSchema;
} catch (error) {
console.error("Error in FalkorDBGraph constructor:", error);
throw new Error("Failed to initialize FalkorDBGraph.");
}
}

static async initialize(config: FalkorDBGraphConfig): Promise<FalkorDBGraph> {
const graph = new FalkorDBGraph(config);
const driver = await FalkorDB.connect({
socket: {
host: new URL(config.url).hostname,
port: parseInt(new URL(config.url).port, 10),
},
});
graph.driver = driver;
await graph.verifyConnectivity();

return graph;
}


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

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

async selectGraph(graphName: string): Promise<void> {
this.graph = await this.driver.selectGraph(graphName);
}

async query(query: string): Promise<any> {
return await this.graph.query(query);
}

async verifyConnectivity(): Promise<void> {
await this.driver.info()
}


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 nodePropertiesResult = await this.query(nodePropertiesQuery);
const relationshipsPropertiesResult = await this.query(relPropertiesQuery);
const relationshipsResult = await this.query(relQuery);

const nodeProperties = nodePropertiesResult.data || [];
const relationshipsProperties = relationshipsPropertiesResult.data || [];
const relationships = relationshipsResult.data || [];

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) {
await 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.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable no-process-env */

import { test } from "@jest/globals";
import { FalkorDBGraph } from "../falkordb.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.selectGraph("falkordbGraph");
await graph.refreshSchema();
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.data).toEqual(expectedOutput);
});
});

test("Verify refreshSchema accurately updates the schema", async () => {
await graph.query(`
CREATE (:Person {name: 'Alice', age: 30})-[:FRIENDS_WITH]->(:Person {name: 'Bob', age: 25})
`);
await graph.refreshSchema();

const schema = graph.getSchema();
expect(schema).toContain("Person");
expect(schema).toContain("FRIENDS_WITH");
expect(schema).toContain("name");
expect(schema).toContain("age");
});
});
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 @@ -114,6 +114,7 @@ export const optionalImportEntrypoints: string[] = [
"langchain_community/retrievers/zep_cloud",
"langchain_community/graphs/neo4j_graph",
"langchain_community/graphs/memgraph_graph",
"langchain_community/graphs/falkordb",
"langchain_community/document_compressors/ibm",
"langchain_community/document_transformers/html_to_text",
"langchain_community/document_transformers/mozilla_readability",
Expand Down