-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
214 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
libs/langchain-community/src/graphs/tests/falkordb_graph.int.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters