@langchain/neo4j 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chains/graph_qa/cypher.cjs.map +1 -1
- package/dist/chains/graph_qa/cypher.js.map +1 -1
- package/dist/chains/graph_qa/prompts.cjs.map +1 -1
- package/dist/chains/graph_qa/prompts.js.map +1 -1
- package/dist/graphs/memgraph_graph.cjs.map +1 -1
- package/dist/graphs/memgraph_graph.js.map +1 -1
- package/dist/graphs/neo4j_graph.cjs.map +1 -1
- package/dist/graphs/neo4j_graph.js.map +1 -1
- package/dist/stores/message/neo4j.cjs.map +1 -1
- package/dist/stores/message/neo4j.js.map +1 -1
- package/dist/vectorstores/neo4j_vector.cjs.map +1 -1
- package/dist/vectorstores/neo4j_vector.js.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cypher.cjs","names":["BaseChain","CYPHER_QA_PROMPT","CYPHER_GENERATION_PROMPT","LLMChain"],"sources":["../../../src/chains/graph_qa/cypher.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport { ChainValues } from \"@langchain/core/utils/types\";\nimport { BasePromptTemplate } from \"@langchain/core/prompts\";\nimport { CallbackManagerForChainRun } from \"@langchain/core/callbacks/manager\";\nimport { LLMChain, BaseChain, ChainInputs } from \"@langchain/classic/chains\";\nimport { Neo4jGraph } from \"../../graphs/neo4j_graph.js\";\nimport { CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT } from \"./prompts.js\";\n\nexport const INTERMEDIATE_STEPS_KEY = \"intermediateSteps\";\n\nexport interface GraphCypherQAChainInput extends ChainInputs {\n graph: Neo4jGraph;\n cypherGenerationChain: LLMChain;\n qaChain: LLMChain;\n inputKey?: string;\n outputKey?: string;\n topK?: number;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\nexport interface FromLLMInput {\n graph: Neo4jGraph;\n llm?: BaseLanguageModelInterface;\n cypherLLM?: BaseLanguageModelInterface;\n qaLLM?: BaseLanguageModelInterface;\n qaPrompt?: BasePromptTemplate;\n cypherPrompt?: BasePromptTemplate;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\n/**\n * Chain for question-answering against a graph by generating Cypher statements.\n *\n * @example\n * ```typescript\n * const chain = new GraphCypherQAChain({\n * llm: new ChatOpenAI({ model: \"gpt-4o-mini\", temperature: 0 }),\n * graph: new Neo4jGraph(),\n * });\n * const res = await chain.invoke(\"Who played in Pulp Fiction?\");\n * ```\n *\n * @security\n * This chain will execute Cypher statements against the provided database.\n * Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling code\n * may attempt commands that would result in deletion, mutation of data\n * if appropriately prompted or reading sensitive data if such data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate) limit the\n * permissions granted to the credentials used with this tool.\n *\n * See https://js.langchain.com/docs/security for more information.\n */\nexport class GraphCypherQAChain extends BaseChain {\n private graph: Neo4jGraph;\n\n private cypherGenerationChain: LLMChain;\n\n private qaChain: LLMChain;\n\n private inputKey = \"query\";\n\n private outputKey = \"result\";\n\n private topK = 10;\n\n private returnDirect = false;\n\n private returnIntermediateSteps = false;\n\n constructor(props: GraphCypherQAChainInput) {\n super(props);\n const {\n graph,\n cypherGenerationChain,\n qaChain,\n inputKey,\n outputKey,\n topK,\n returnIntermediateSteps,\n returnDirect,\n } = props;\n\n this.graph = graph;\n this.cypherGenerationChain = cypherGenerationChain;\n this.qaChain = qaChain;\n\n if (inputKey) {\n this.inputKey = inputKey;\n }\n if (outputKey) {\n this.outputKey = outputKey;\n }\n if (topK) {\n this.topK = topK;\n }\n if (returnIntermediateSteps) {\n this.returnIntermediateSteps = returnIntermediateSteps;\n }\n if (returnDirect) {\n this.returnDirect = returnDirect;\n }\n }\n\n _chainType() {\n return \"graph_cypher_chain\" as const;\n }\n\n get inputKeys(): string[] {\n return [this.inputKey];\n }\n\n get outputKeys(): string[] {\n return [this.outputKey];\n }\n\n static fromLLM(props: FromLLMInput): GraphCypherQAChain {\n const {\n graph,\n qaPrompt = CYPHER_QA_PROMPT,\n cypherPrompt = CYPHER_GENERATION_PROMPT,\n llm,\n cypherLLM,\n qaLLM,\n returnIntermediateSteps = false,\n returnDirect = false,\n } = props;\n\n if (!cypherLLM && !llm) {\n throw new Error(\n \"Either 'llm' or 'cypherLLM' parameters must be provided\"\n );\n }\n\n if (!qaLLM && !llm) {\n throw new Error(\"Either 'llm' or 'qaLLM' parameters must be provided\");\n }\n\n if (cypherLLM && qaLLM && llm) {\n throw new Error(\n \"You can specify up to two of 'cypherLLM', 'qaLLM', and 'llm', but not all three simultaneously.\"\n );\n }\n\n const qaChain = new LLMChain({\n llm: (qaLLM || llm) as BaseLanguageModelInterface,\n prompt: qaPrompt,\n });\n\n const cypherGenerationChain = new LLMChain({\n llm: (cypherLLM || llm) as BaseLanguageModelInterface,\n prompt: cypherPrompt,\n });\n\n return new GraphCypherQAChain({\n cypherGenerationChain,\n qaChain,\n graph,\n returnIntermediateSteps,\n returnDirect,\n });\n }\n\n private extractCypher(text: string): string {\n const pattern = /```(.*?)```/s;\n const matches = text.match(pattern);\n return matches ? matches[1] : text;\n }\n\n async _call(\n values: ChainValues,\n runManager?: CallbackManagerForChainRun\n ): Promise<ChainValues> {\n const callbacks = runManager?.getChild();\n const question = values[this.inputKey];\n\n const intermediateSteps = [];\n\n const generatedCypher = await this.cypherGenerationChain.call(\n { question, schema: this.graph.getSchema() },\n callbacks\n );\n\n const extractedCypher = this.extractCypher(generatedCypher.text);\n\n await runManager?.handleText(`Generated Cypher:\\n`);\n await runManager?.handleText(`${extractedCypher} green\\n`);\n\n intermediateSteps.push({ query: extractedCypher });\n\n let chainResult: ChainValues;\n const context = await this.graph.query(extractedCypher, {\n topK: this.topK,\n });\n\n if (this.returnDirect) {\n chainResult = { [this.outputKey]: context };\n } else {\n await runManager?.handleText(\"Full Context:\\n\");\n await runManager?.handleText(`${context} green\\n`);\n\n intermediateSteps.push({ context });\n\n const result = await this.qaChain.call(\n { question, context: JSON.stringify(context) },\n callbacks\n );\n\n chainResult = {\n [this.outputKey]: result[this.qaChain.outputKey],\n };\n }\n\n if (this.returnIntermediateSteps) {\n chainResult[INTERMEDIATE_STEPS_KEY] = intermediateSteps;\n }\n\n return chainResult;\n }\n}\n"],"mappings":";;;;AAQA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;AAgDtC,IAAa,qBAAb,MAAa,2BAA2BA,0BAAAA,UAAU;CAChD;CAEA;CAEA;CAEA,WAAmB;CAEnB,YAAoB;CAEpB,OAAe;CAEf,eAAuB;CAEvB,0BAAkC;CAElC,YAAY,OAAgC;AAC1C,QAAM,MAAM;EACZ,MAAM,EACJ,OACA,uBACA,SACA,UACA,WACA,MACA,yBACA,iBACE;AAEJ,OAAK,QAAQ;AACb,OAAK,wBAAwB;AAC7B,OAAK,UAAU;AAEf,MAAI,SACF,MAAK,WAAW;AAElB,MAAI,UACF,MAAK,YAAY;AAEnB,MAAI,KACF,MAAK,OAAO;AAEd,MAAI,wBACF,MAAK,0BAA0B;AAEjC,MAAI,aACF,MAAK,eAAe;;CAIxB,aAAa;AACX,SAAO;;CAGT,IAAI,YAAsB;AACxB,SAAO,CAAC,KAAK,SAAS;;CAGxB,IAAI,aAAuB;AACzB,SAAO,CAAC,KAAK,UAAU;;CAGzB,OAAO,QAAQ,OAAyC;EACtD,MAAM,EACJ,OACA,WAAWC,gBAAAA,kBACX,eAAeC,gBAAAA,0BACf,KACA,WACA,OACA,0BAA0B,OAC1B,eAAe,UACb;AAEJ,MAAI,CAAC,aAAa,CAAC,IACjB,OAAM,IAAI,MACR,0DACD;AAGH,MAAI,CAAC,SAAS,CAAC,IACb,OAAM,IAAI,MAAM,sDAAsD;AAGxE,MAAI,aAAa,SAAS,IACxB,OAAM,IAAI,MACR,kGACD;EAGH,MAAM,UAAU,IAAIC,0BAAAA,SAAS;GAC3B,KAAM,SAAS;GACf,QAAQ;GACT,CAAC;AAOF,SAAO,IAAI,mBAAmB;GAC5B,
|
|
1
|
+
{"version":3,"file":"cypher.cjs","names":["BaseChain","CYPHER_QA_PROMPT","CYPHER_GENERATION_PROMPT","LLMChain"],"sources":["../../../src/chains/graph_qa/cypher.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport { ChainValues } from \"@langchain/core/utils/types\";\nimport { BasePromptTemplate } from \"@langchain/core/prompts\";\nimport { CallbackManagerForChainRun } from \"@langchain/core/callbacks/manager\";\nimport { LLMChain, BaseChain, ChainInputs } from \"@langchain/classic/chains\";\nimport { Neo4jGraph } from \"../../graphs/neo4j_graph.js\";\nimport { CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT } from \"./prompts.js\";\n\nexport const INTERMEDIATE_STEPS_KEY = \"intermediateSteps\";\n\nexport interface GraphCypherQAChainInput extends ChainInputs {\n graph: Neo4jGraph;\n cypherGenerationChain: LLMChain;\n qaChain: LLMChain;\n inputKey?: string;\n outputKey?: string;\n topK?: number;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\nexport interface FromLLMInput {\n graph: Neo4jGraph;\n llm?: BaseLanguageModelInterface;\n cypherLLM?: BaseLanguageModelInterface;\n qaLLM?: BaseLanguageModelInterface;\n qaPrompt?: BasePromptTemplate;\n cypherPrompt?: BasePromptTemplate;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\n/**\n * Chain for question-answering against a graph by generating Cypher statements.\n *\n * @example\n * ```typescript\n * const chain = new GraphCypherQAChain({\n * llm: new ChatOpenAI({ model: \"gpt-4o-mini\", temperature: 0 }),\n * graph: new Neo4jGraph(),\n * });\n * const res = await chain.invoke(\"Who played in Pulp Fiction?\");\n * ```\n *\n * @security\n * This chain will execute Cypher statements against the provided database.\n * Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling code\n * may attempt commands that would result in deletion, mutation of data\n * if appropriately prompted or reading sensitive data if such data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate) limit the\n * permissions granted to the credentials used with this tool.\n *\n * See https://js.langchain.com/docs/security for more information.\n */\nexport class GraphCypherQAChain extends BaseChain {\n private graph: Neo4jGraph;\n\n private cypherGenerationChain: LLMChain;\n\n private qaChain: LLMChain;\n\n private inputKey = \"query\";\n\n private outputKey = \"result\";\n\n private topK = 10;\n\n private returnDirect = false;\n\n private returnIntermediateSteps = false;\n\n constructor(props: GraphCypherQAChainInput) {\n super(props);\n const {\n graph,\n cypherGenerationChain,\n qaChain,\n inputKey,\n outputKey,\n topK,\n returnIntermediateSteps,\n returnDirect,\n } = props;\n\n this.graph = graph;\n this.cypherGenerationChain = cypherGenerationChain;\n this.qaChain = qaChain;\n\n if (inputKey) {\n this.inputKey = inputKey;\n }\n if (outputKey) {\n this.outputKey = outputKey;\n }\n if (topK) {\n this.topK = topK;\n }\n if (returnIntermediateSteps) {\n this.returnIntermediateSteps = returnIntermediateSteps;\n }\n if (returnDirect) {\n this.returnDirect = returnDirect;\n }\n }\n\n _chainType() {\n return \"graph_cypher_chain\" as const;\n }\n\n get inputKeys(): string[] {\n return [this.inputKey];\n }\n\n get outputKeys(): string[] {\n return [this.outputKey];\n }\n\n static fromLLM(props: FromLLMInput): GraphCypherQAChain {\n const {\n graph,\n qaPrompt = CYPHER_QA_PROMPT,\n cypherPrompt = CYPHER_GENERATION_PROMPT,\n llm,\n cypherLLM,\n qaLLM,\n returnIntermediateSteps = false,\n returnDirect = false,\n } = props;\n\n if (!cypherLLM && !llm) {\n throw new Error(\n \"Either 'llm' or 'cypherLLM' parameters must be provided\"\n );\n }\n\n if (!qaLLM && !llm) {\n throw new Error(\"Either 'llm' or 'qaLLM' parameters must be provided\");\n }\n\n if (cypherLLM && qaLLM && llm) {\n throw new Error(\n \"You can specify up to two of 'cypherLLM', 'qaLLM', and 'llm', but not all three simultaneously.\"\n );\n }\n\n const qaChain = new LLMChain({\n llm: (qaLLM || llm) as BaseLanguageModelInterface,\n prompt: qaPrompt,\n });\n\n const cypherGenerationChain = new LLMChain({\n llm: (cypherLLM || llm) as BaseLanguageModelInterface,\n prompt: cypherPrompt,\n });\n\n return new GraphCypherQAChain({\n cypherGenerationChain,\n qaChain,\n graph,\n returnIntermediateSteps,\n returnDirect,\n });\n }\n\n private extractCypher(text: string): string {\n const pattern = /```(.*?)```/s;\n const matches = text.match(pattern);\n return matches ? matches[1] : text;\n }\n\n async _call(\n values: ChainValues,\n runManager?: CallbackManagerForChainRun\n ): Promise<ChainValues> {\n const callbacks = runManager?.getChild();\n const question = values[this.inputKey];\n\n const intermediateSteps = [];\n\n const generatedCypher = await this.cypherGenerationChain.call(\n { question, schema: this.graph.getSchema() },\n callbacks\n );\n\n const extractedCypher = this.extractCypher(generatedCypher.text);\n\n await runManager?.handleText(`Generated Cypher:\\n`);\n await runManager?.handleText(`${extractedCypher} green\\n`);\n\n intermediateSteps.push({ query: extractedCypher });\n\n let chainResult: ChainValues;\n const context = await this.graph.query(extractedCypher, {\n topK: this.topK,\n });\n\n if (this.returnDirect) {\n chainResult = { [this.outputKey]: context };\n } else {\n await runManager?.handleText(\"Full Context:\\n\");\n await runManager?.handleText(`${context} green\\n`);\n\n intermediateSteps.push({ context });\n\n const result = await this.qaChain.call(\n { question, context: JSON.stringify(context) },\n callbacks\n );\n\n chainResult = {\n [this.outputKey]: result[this.qaChain.outputKey],\n };\n }\n\n if (this.returnIntermediateSteps) {\n chainResult[INTERMEDIATE_STEPS_KEY] = intermediateSteps;\n }\n\n return chainResult;\n }\n}\n"],"mappings":";;;;AAQA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;AAgDtC,IAAa,qBAAb,MAAa,2BAA2BA,0BAAAA,UAAU;CAChD;CAEA;CAEA;CAEA,WAAmB;CAEnB,YAAoB;CAEpB,OAAe;CAEf,eAAuB;CAEvB,0BAAkC;CAElC,YAAY,OAAgC;AAC1C,QAAM,MAAM;EACZ,MAAM,EACJ,OACA,uBACA,SACA,UACA,WACA,MACA,yBACA,iBACE;AAEJ,OAAK,QAAQ;AACb,OAAK,wBAAwB;AAC7B,OAAK,UAAU;AAEf,MAAI,SACF,MAAK,WAAW;AAElB,MAAI,UACF,MAAK,YAAY;AAEnB,MAAI,KACF,MAAK,OAAO;AAEd,MAAI,wBACF,MAAK,0BAA0B;AAEjC,MAAI,aACF,MAAK,eAAe;;CAIxB,aAAa;AACX,SAAO;;CAGT,IAAI,YAAsB;AACxB,SAAO,CAAC,KAAK,SAAS;;CAGxB,IAAI,aAAuB;AACzB,SAAO,CAAC,KAAK,UAAU;;CAGzB,OAAO,QAAQ,OAAyC;EACtD,MAAM,EACJ,OACA,WAAWC,gBAAAA,kBACX,eAAeC,gBAAAA,0BACf,KACA,WACA,OACA,0BAA0B,OAC1B,eAAe,UACb;AAEJ,MAAI,CAAC,aAAa,CAAC,IACjB,OAAM,IAAI,MACR,0DACD;AAGH,MAAI,CAAC,SAAS,CAAC,IACb,OAAM,IAAI,MAAM,sDAAsD;AAGxE,MAAI,aAAa,SAAS,IACxB,OAAM,IAAI,MACR,kGACD;EAGH,MAAM,UAAU,IAAIC,0BAAAA,SAAS;GAC3B,KAAM,SAAS;GACf,QAAQ;GACT,CAAC;AAOF,SAAO,IAAI,mBAAmB;GAC5B,uBAAA,IANgCA,0BAAAA,SAAS;IACzC,KAAM,aAAa;IACnB,QAAQ;IACT,CAGsB;GACrB;GACA;GACA;GACA;GACD,CAAC;;CAGJ,cAAsB,MAAsB;EAE1C,MAAM,UAAU,KAAK,MAAM,eAAQ;AACnC,SAAO,UAAU,QAAQ,KAAK;;CAGhC,MAAM,MACJ,QACA,YACsB;EACtB,MAAM,YAAY,YAAY,UAAU;EACxC,MAAM,WAAW,OAAO,KAAK;EAE7B,MAAM,oBAAoB,EAAE;EAE5B,MAAM,kBAAkB,MAAM,KAAK,sBAAsB,KACvD;GAAE;GAAU,QAAQ,KAAK,MAAM,WAAW;GAAE,EAC5C,UACD;EAED,MAAM,kBAAkB,KAAK,cAAc,gBAAgB,KAAK;AAEhE,QAAM,YAAY,WAAW,sBAAsB;AACnD,QAAM,YAAY,WAAW,GAAG,gBAAgB,UAAU;AAE1D,oBAAkB,KAAK,EAAE,OAAO,iBAAiB,CAAC;EAElD,IAAI;EACJ,MAAM,UAAU,MAAM,KAAK,MAAM,MAAM,iBAAiB,EACtD,MAAM,KAAK,MACZ,CAAC;AAEF,MAAI,KAAK,aACP,eAAc,GAAG,KAAK,YAAY,SAAS;OACtC;AACL,SAAM,YAAY,WAAW,kBAAkB;AAC/C,SAAM,YAAY,WAAW,GAAG,QAAQ,UAAU;AAElD,qBAAkB,KAAK,EAAE,SAAS,CAAC;GAEnC,MAAM,SAAS,MAAM,KAAK,QAAQ,KAChC;IAAE;IAAU,SAAS,KAAK,UAAU,QAAQ;IAAE,EAC9C,UACD;AAED,iBAAc,GACX,KAAK,YAAY,OAAO,KAAK,QAAQ,YACvC;;AAGH,MAAI,KAAK,wBACP,aAAY,0BAA0B;AAGxC,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cypher.js","names":[],"sources":["../../../src/chains/graph_qa/cypher.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport { ChainValues } from \"@langchain/core/utils/types\";\nimport { BasePromptTemplate } from \"@langchain/core/prompts\";\nimport { CallbackManagerForChainRun } from \"@langchain/core/callbacks/manager\";\nimport { LLMChain, BaseChain, ChainInputs } from \"@langchain/classic/chains\";\nimport { Neo4jGraph } from \"../../graphs/neo4j_graph.js\";\nimport { CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT } from \"./prompts.js\";\n\nexport const INTERMEDIATE_STEPS_KEY = \"intermediateSteps\";\n\nexport interface GraphCypherQAChainInput extends ChainInputs {\n graph: Neo4jGraph;\n cypherGenerationChain: LLMChain;\n qaChain: LLMChain;\n inputKey?: string;\n outputKey?: string;\n topK?: number;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\nexport interface FromLLMInput {\n graph: Neo4jGraph;\n llm?: BaseLanguageModelInterface;\n cypherLLM?: BaseLanguageModelInterface;\n qaLLM?: BaseLanguageModelInterface;\n qaPrompt?: BasePromptTemplate;\n cypherPrompt?: BasePromptTemplate;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\n/**\n * Chain for question-answering against a graph by generating Cypher statements.\n *\n * @example\n * ```typescript\n * const chain = new GraphCypherQAChain({\n * llm: new ChatOpenAI({ model: \"gpt-4o-mini\", temperature: 0 }),\n * graph: new Neo4jGraph(),\n * });\n * const res = await chain.invoke(\"Who played in Pulp Fiction?\");\n * ```\n *\n * @security\n * This chain will execute Cypher statements against the provided database.\n * Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling code\n * may attempt commands that would result in deletion, mutation of data\n * if appropriately prompted or reading sensitive data if such data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate) limit the\n * permissions granted to the credentials used with this tool.\n *\n * See https://js.langchain.com/docs/security for more information.\n */\nexport class GraphCypherQAChain extends BaseChain {\n private graph: Neo4jGraph;\n\n private cypherGenerationChain: LLMChain;\n\n private qaChain: LLMChain;\n\n private inputKey = \"query\";\n\n private outputKey = \"result\";\n\n private topK = 10;\n\n private returnDirect = false;\n\n private returnIntermediateSteps = false;\n\n constructor(props: GraphCypherQAChainInput) {\n super(props);\n const {\n graph,\n cypherGenerationChain,\n qaChain,\n inputKey,\n outputKey,\n topK,\n returnIntermediateSteps,\n returnDirect,\n } = props;\n\n this.graph = graph;\n this.cypherGenerationChain = cypherGenerationChain;\n this.qaChain = qaChain;\n\n if (inputKey) {\n this.inputKey = inputKey;\n }\n if (outputKey) {\n this.outputKey = outputKey;\n }\n if (topK) {\n this.topK = topK;\n }\n if (returnIntermediateSteps) {\n this.returnIntermediateSteps = returnIntermediateSteps;\n }\n if (returnDirect) {\n this.returnDirect = returnDirect;\n }\n }\n\n _chainType() {\n return \"graph_cypher_chain\" as const;\n }\n\n get inputKeys(): string[] {\n return [this.inputKey];\n }\n\n get outputKeys(): string[] {\n return [this.outputKey];\n }\n\n static fromLLM(props: FromLLMInput): GraphCypherQAChain {\n const {\n graph,\n qaPrompt = CYPHER_QA_PROMPT,\n cypherPrompt = CYPHER_GENERATION_PROMPT,\n llm,\n cypherLLM,\n qaLLM,\n returnIntermediateSteps = false,\n returnDirect = false,\n } = props;\n\n if (!cypherLLM && !llm) {\n throw new Error(\n \"Either 'llm' or 'cypherLLM' parameters must be provided\"\n );\n }\n\n if (!qaLLM && !llm) {\n throw new Error(\"Either 'llm' or 'qaLLM' parameters must be provided\");\n }\n\n if (cypherLLM && qaLLM && llm) {\n throw new Error(\n \"You can specify up to two of 'cypherLLM', 'qaLLM', and 'llm', but not all three simultaneously.\"\n );\n }\n\n const qaChain = new LLMChain({\n llm: (qaLLM || llm) as BaseLanguageModelInterface,\n prompt: qaPrompt,\n });\n\n const cypherGenerationChain = new LLMChain({\n llm: (cypherLLM || llm) as BaseLanguageModelInterface,\n prompt: cypherPrompt,\n });\n\n return new GraphCypherQAChain({\n cypherGenerationChain,\n qaChain,\n graph,\n returnIntermediateSteps,\n returnDirect,\n });\n }\n\n private extractCypher(text: string): string {\n const pattern = /```(.*?)```/s;\n const matches = text.match(pattern);\n return matches ? matches[1] : text;\n }\n\n async _call(\n values: ChainValues,\n runManager?: CallbackManagerForChainRun\n ): Promise<ChainValues> {\n const callbacks = runManager?.getChild();\n const question = values[this.inputKey];\n\n const intermediateSteps = [];\n\n const generatedCypher = await this.cypherGenerationChain.call(\n { question, schema: this.graph.getSchema() },\n callbacks\n );\n\n const extractedCypher = this.extractCypher(generatedCypher.text);\n\n await runManager?.handleText(`Generated Cypher:\\n`);\n await runManager?.handleText(`${extractedCypher} green\\n`);\n\n intermediateSteps.push({ query: extractedCypher });\n\n let chainResult: ChainValues;\n const context = await this.graph.query(extractedCypher, {\n topK: this.topK,\n });\n\n if (this.returnDirect) {\n chainResult = { [this.outputKey]: context };\n } else {\n await runManager?.handleText(\"Full Context:\\n\");\n await runManager?.handleText(`${context} green\\n`);\n\n intermediateSteps.push({ context });\n\n const result = await this.qaChain.call(\n { question, context: JSON.stringify(context) },\n callbacks\n );\n\n chainResult = {\n [this.outputKey]: result[this.qaChain.outputKey],\n };\n }\n\n if (this.returnIntermediateSteps) {\n chainResult[INTERMEDIATE_STEPS_KEY] = intermediateSteps;\n }\n\n return chainResult;\n }\n}\n"],"mappings":";;;AAQA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;AAgDtC,IAAa,qBAAb,MAAa,2BAA2B,UAAU;CAChD;CAEA;CAEA;CAEA,WAAmB;CAEnB,YAAoB;CAEpB,OAAe;CAEf,eAAuB;CAEvB,0BAAkC;CAElC,YAAY,OAAgC;AAC1C,QAAM,MAAM;EACZ,MAAM,EACJ,OACA,uBACA,SACA,UACA,WACA,MACA,yBACA,iBACE;AAEJ,OAAK,QAAQ;AACb,OAAK,wBAAwB;AAC7B,OAAK,UAAU;AAEf,MAAI,SACF,MAAK,WAAW;AAElB,MAAI,UACF,MAAK,YAAY;AAEnB,MAAI,KACF,MAAK,OAAO;AAEd,MAAI,wBACF,MAAK,0BAA0B;AAEjC,MAAI,aACF,MAAK,eAAe;;CAIxB,aAAa;AACX,SAAO;;CAGT,IAAI,YAAsB;AACxB,SAAO,CAAC,KAAK,SAAS;;CAGxB,IAAI,aAAuB;AACzB,SAAO,CAAC,KAAK,UAAU;;CAGzB,OAAO,QAAQ,OAAyC;EACtD,MAAM,EACJ,OACA,WAAW,kBACX,eAAe,0BACf,KACA,WACA,OACA,0BAA0B,OAC1B,eAAe,UACb;AAEJ,MAAI,CAAC,aAAa,CAAC,IACjB,OAAM,IAAI,MACR,0DACD;AAGH,MAAI,CAAC,SAAS,CAAC,IACb,OAAM,IAAI,MAAM,sDAAsD;AAGxE,MAAI,aAAa,SAAS,IACxB,OAAM,IAAI,MACR,kGACD;EAGH,MAAM,UAAU,IAAI,SAAS;GAC3B,KAAM,SAAS;GACf,QAAQ;GACT,CAAC;AAOF,SAAO,IAAI,mBAAmB;GAC5B,
|
|
1
|
+
{"version":3,"file":"cypher.js","names":[],"sources":["../../../src/chains/graph_qa/cypher.ts"],"sourcesContent":["import type { BaseLanguageModelInterface } from \"@langchain/core/language_models/base\";\nimport { ChainValues } from \"@langchain/core/utils/types\";\nimport { BasePromptTemplate } from \"@langchain/core/prompts\";\nimport { CallbackManagerForChainRun } from \"@langchain/core/callbacks/manager\";\nimport { LLMChain, BaseChain, ChainInputs } from \"@langchain/classic/chains\";\nimport { Neo4jGraph } from \"../../graphs/neo4j_graph.js\";\nimport { CYPHER_GENERATION_PROMPT, CYPHER_QA_PROMPT } from \"./prompts.js\";\n\nexport const INTERMEDIATE_STEPS_KEY = \"intermediateSteps\";\n\nexport interface GraphCypherQAChainInput extends ChainInputs {\n graph: Neo4jGraph;\n cypherGenerationChain: LLMChain;\n qaChain: LLMChain;\n inputKey?: string;\n outputKey?: string;\n topK?: number;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\nexport interface FromLLMInput {\n graph: Neo4jGraph;\n llm?: BaseLanguageModelInterface;\n cypherLLM?: BaseLanguageModelInterface;\n qaLLM?: BaseLanguageModelInterface;\n qaPrompt?: BasePromptTemplate;\n cypherPrompt?: BasePromptTemplate;\n returnIntermediateSteps?: boolean;\n returnDirect?: boolean;\n}\n\n/**\n * Chain for question-answering against a graph by generating Cypher statements.\n *\n * @example\n * ```typescript\n * const chain = new GraphCypherQAChain({\n * llm: new ChatOpenAI({ model: \"gpt-4o-mini\", temperature: 0 }),\n * graph: new Neo4jGraph(),\n * });\n * const res = await chain.invoke(\"Who played in Pulp Fiction?\");\n * ```\n *\n * @security\n * This chain will execute Cypher statements against the provided database.\n * Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling code\n * may attempt commands that would result in deletion, mutation of data\n * if appropriately prompted or reading sensitive data if such data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate) limit the\n * permissions granted to the credentials used with this tool.\n *\n * See https://js.langchain.com/docs/security for more information.\n */\nexport class GraphCypherQAChain extends BaseChain {\n private graph: Neo4jGraph;\n\n private cypherGenerationChain: LLMChain;\n\n private qaChain: LLMChain;\n\n private inputKey = \"query\";\n\n private outputKey = \"result\";\n\n private topK = 10;\n\n private returnDirect = false;\n\n private returnIntermediateSteps = false;\n\n constructor(props: GraphCypherQAChainInput) {\n super(props);\n const {\n graph,\n cypherGenerationChain,\n qaChain,\n inputKey,\n outputKey,\n topK,\n returnIntermediateSteps,\n returnDirect,\n } = props;\n\n this.graph = graph;\n this.cypherGenerationChain = cypherGenerationChain;\n this.qaChain = qaChain;\n\n if (inputKey) {\n this.inputKey = inputKey;\n }\n if (outputKey) {\n this.outputKey = outputKey;\n }\n if (topK) {\n this.topK = topK;\n }\n if (returnIntermediateSteps) {\n this.returnIntermediateSteps = returnIntermediateSteps;\n }\n if (returnDirect) {\n this.returnDirect = returnDirect;\n }\n }\n\n _chainType() {\n return \"graph_cypher_chain\" as const;\n }\n\n get inputKeys(): string[] {\n return [this.inputKey];\n }\n\n get outputKeys(): string[] {\n return [this.outputKey];\n }\n\n static fromLLM(props: FromLLMInput): GraphCypherQAChain {\n const {\n graph,\n qaPrompt = CYPHER_QA_PROMPT,\n cypherPrompt = CYPHER_GENERATION_PROMPT,\n llm,\n cypherLLM,\n qaLLM,\n returnIntermediateSteps = false,\n returnDirect = false,\n } = props;\n\n if (!cypherLLM && !llm) {\n throw new Error(\n \"Either 'llm' or 'cypherLLM' parameters must be provided\"\n );\n }\n\n if (!qaLLM && !llm) {\n throw new Error(\"Either 'llm' or 'qaLLM' parameters must be provided\");\n }\n\n if (cypherLLM && qaLLM && llm) {\n throw new Error(\n \"You can specify up to two of 'cypherLLM', 'qaLLM', and 'llm', but not all three simultaneously.\"\n );\n }\n\n const qaChain = new LLMChain({\n llm: (qaLLM || llm) as BaseLanguageModelInterface,\n prompt: qaPrompt,\n });\n\n const cypherGenerationChain = new LLMChain({\n llm: (cypherLLM || llm) as BaseLanguageModelInterface,\n prompt: cypherPrompt,\n });\n\n return new GraphCypherQAChain({\n cypherGenerationChain,\n qaChain,\n graph,\n returnIntermediateSteps,\n returnDirect,\n });\n }\n\n private extractCypher(text: string): string {\n const pattern = /```(.*?)```/s;\n const matches = text.match(pattern);\n return matches ? matches[1] : text;\n }\n\n async _call(\n values: ChainValues,\n runManager?: CallbackManagerForChainRun\n ): Promise<ChainValues> {\n const callbacks = runManager?.getChild();\n const question = values[this.inputKey];\n\n const intermediateSteps = [];\n\n const generatedCypher = await this.cypherGenerationChain.call(\n { question, schema: this.graph.getSchema() },\n callbacks\n );\n\n const extractedCypher = this.extractCypher(generatedCypher.text);\n\n await runManager?.handleText(`Generated Cypher:\\n`);\n await runManager?.handleText(`${extractedCypher} green\\n`);\n\n intermediateSteps.push({ query: extractedCypher });\n\n let chainResult: ChainValues;\n const context = await this.graph.query(extractedCypher, {\n topK: this.topK,\n });\n\n if (this.returnDirect) {\n chainResult = { [this.outputKey]: context };\n } else {\n await runManager?.handleText(\"Full Context:\\n\");\n await runManager?.handleText(`${context} green\\n`);\n\n intermediateSteps.push({ context });\n\n const result = await this.qaChain.call(\n { question, context: JSON.stringify(context) },\n callbacks\n );\n\n chainResult = {\n [this.outputKey]: result[this.qaChain.outputKey],\n };\n }\n\n if (this.returnIntermediateSteps) {\n chainResult[INTERMEDIATE_STEPS_KEY] = intermediateSteps;\n }\n\n return chainResult;\n }\n}\n"],"mappings":";;;AAQA,MAAa,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;AAgDtC,IAAa,qBAAb,MAAa,2BAA2B,UAAU;CAChD;CAEA;CAEA;CAEA,WAAmB;CAEnB,YAAoB;CAEpB,OAAe;CAEf,eAAuB;CAEvB,0BAAkC;CAElC,YAAY,OAAgC;AAC1C,QAAM,MAAM;EACZ,MAAM,EACJ,OACA,uBACA,SACA,UACA,WACA,MACA,yBACA,iBACE;AAEJ,OAAK,QAAQ;AACb,OAAK,wBAAwB;AAC7B,OAAK,UAAU;AAEf,MAAI,SACF,MAAK,WAAW;AAElB,MAAI,UACF,MAAK,YAAY;AAEnB,MAAI,KACF,MAAK,OAAO;AAEd,MAAI,wBACF,MAAK,0BAA0B;AAEjC,MAAI,aACF,MAAK,eAAe;;CAIxB,aAAa;AACX,SAAO;;CAGT,IAAI,YAAsB;AACxB,SAAO,CAAC,KAAK,SAAS;;CAGxB,IAAI,aAAuB;AACzB,SAAO,CAAC,KAAK,UAAU;;CAGzB,OAAO,QAAQ,OAAyC;EACtD,MAAM,EACJ,OACA,WAAW,kBACX,eAAe,0BACf,KACA,WACA,OACA,0BAA0B,OAC1B,eAAe,UACb;AAEJ,MAAI,CAAC,aAAa,CAAC,IACjB,OAAM,IAAI,MACR,0DACD;AAGH,MAAI,CAAC,SAAS,CAAC,IACb,OAAM,IAAI,MAAM,sDAAsD;AAGxE,MAAI,aAAa,SAAS,IACxB,OAAM,IAAI,MACR,kGACD;EAGH,MAAM,UAAU,IAAI,SAAS;GAC3B,KAAM,SAAS;GACf,QAAQ;GACT,CAAC;AAOF,SAAO,IAAI,mBAAmB;GAC5B,uBAAA,IANgC,SAAS;IACzC,KAAM,aAAa;IACnB,QAAQ;IACT,CAGsB;GACrB;GACA;GACA;GACA;GACD,CAAC;;CAGJ,cAAsB,MAAsB;EAE1C,MAAM,UAAU,KAAK,MAAM,eAAQ;AACnC,SAAO,UAAU,QAAQ,KAAK;;CAGhC,MAAM,MACJ,QACA,YACsB;EACtB,MAAM,YAAY,YAAY,UAAU;EACxC,MAAM,WAAW,OAAO,KAAK;EAE7B,MAAM,oBAAoB,EAAE;EAE5B,MAAM,kBAAkB,MAAM,KAAK,sBAAsB,KACvD;GAAE;GAAU,QAAQ,KAAK,MAAM,WAAW;GAAE,EAC5C,UACD;EAED,MAAM,kBAAkB,KAAK,cAAc,gBAAgB,KAAK;AAEhE,QAAM,YAAY,WAAW,sBAAsB;AACnD,QAAM,YAAY,WAAW,GAAG,gBAAgB,UAAU;AAE1D,oBAAkB,KAAK,EAAE,OAAO,iBAAiB,CAAC;EAElD,IAAI;EACJ,MAAM,UAAU,MAAM,KAAK,MAAM,MAAM,iBAAiB,EACtD,MAAM,KAAK,MACZ,CAAC;AAEF,MAAI,KAAK,aACP,eAAc,GAAG,KAAK,YAAY,SAAS;OACtC;AACL,SAAM,YAAY,WAAW,kBAAkB;AAC/C,SAAM,YAAY,WAAW,GAAG,QAAQ,UAAU;AAElD,qBAAkB,KAAK,EAAE,SAAS,CAAC;GAEnC,MAAM,SAAS,MAAM,KAAK,QAAQ,KAChC;IAAE;IAAU,SAAS,KAAK,UAAU,QAAQ;IAAE,EAC9C,UACD;AAED,iBAAc,GACX,KAAK,YAAY,OAAO,KAAK,QAAQ,YACvC;;AAGH,MAAI,KAAK,wBACP,aAAY,0BAA0B;AAGxC,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.cjs","names":["PromptTemplate"],"sources":["../../../src/chains/graph_qa/prompts.ts"],"sourcesContent":["import { PromptTemplate } from \"@langchain/core/prompts\";\n\nconst CYPHER_GENERATION_TEMPLATE = `Task:Generate Cypher statement to query a graph database.\nInstructions:\nUse only the provided relationship types and properties in the schema.\nDo not use any other relationship types or properties that are not provided.\nSchema:\n{schema}\nNote: Do not include any explanations or apologies in your responses.\nDo not respond to any questions that might ask anything else than for you to construct a Cypher statement.\nDo not include any text except the generated Cypher statement.\n\nThe question is:\n{question}`;\nexport const CYPHER_GENERATION_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_GENERATION_TEMPLATE,\n inputVariables: [\"schema\", \"question\"],\n});\n\nconst CYPHER_QA_TEMPLATE = `You are an assistant that helps to form nice and human understandable answers.\nThe information part contains the provided information that you must use to construct an answer.\nThe provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\nMake the answer sound as a response to the question. Do not mention that you based the result on the given information.\nHere is an example:\n\nQuestion: Which managers own Neo4j stocks?\nContext:[manager:CTL LLC, manager:JANE STREET GROUP LLC]\nHelpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.\n\nFollow this example when generating answers.\nIf the provided information is empty, say that you don't know the answer.\nInformation:\n{context}\n\nQuestion: {question}\nHelpful Answer:`;\nexport const CYPHER_QA_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_QA_TEMPLATE,\n inputVariables: [\"context\", \"question\"],\n});\n"],"mappings":";;AAcA,MAAa,2CAA2C,IAAIA,wBAAAA,eAAe;CACzE,
|
|
1
|
+
{"version":3,"file":"prompts.cjs","names":["PromptTemplate"],"sources":["../../../src/chains/graph_qa/prompts.ts"],"sourcesContent":["import { PromptTemplate } from \"@langchain/core/prompts\";\n\nconst CYPHER_GENERATION_TEMPLATE = `Task:Generate Cypher statement to query a graph database.\nInstructions:\nUse only the provided relationship types and properties in the schema.\nDo not use any other relationship types or properties that are not provided.\nSchema:\n{schema}\nNote: Do not include any explanations or apologies in your responses.\nDo not respond to any questions that might ask anything else than for you to construct a Cypher statement.\nDo not include any text except the generated Cypher statement.\n\nThe question is:\n{question}`;\nexport const CYPHER_GENERATION_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_GENERATION_TEMPLATE,\n inputVariables: [\"schema\", \"question\"],\n});\n\nconst CYPHER_QA_TEMPLATE = `You are an assistant that helps to form nice and human understandable answers.\nThe information part contains the provided information that you must use to construct an answer.\nThe provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\nMake the answer sound as a response to the question. Do not mention that you based the result on the given information.\nHere is an example:\n\nQuestion: Which managers own Neo4j stocks?\nContext:[manager:CTL LLC, manager:JANE STREET GROUP LLC]\nHelpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.\n\nFollow this example when generating answers.\nIf the provided information is empty, say that you don't know the answer.\nInformation:\n{context}\n\nQuestion: {question}\nHelpful Answer:`;\nexport const CYPHER_QA_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_QA_TEMPLATE,\n inputVariables: [\"context\", \"question\"],\n});\n"],"mappings":";;AAcA,MAAa,2CAA2C,IAAIA,wBAAAA,eAAe;CACzE,UAAU;;;;;;;;;;;;CACV,gBAAgB,CAAC,UAAU,WAAW;CACvC,CAAC;AAmBF,MAAa,mCAAmC,IAAIA,wBAAAA,eAAe;CACjE,UAAU;;;;;;;;;;;;;;;;;CACV,gBAAgB,CAAC,WAAW,WAAW;CACxC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.js","names":[],"sources":["../../../src/chains/graph_qa/prompts.ts"],"sourcesContent":["import { PromptTemplate } from \"@langchain/core/prompts\";\n\nconst CYPHER_GENERATION_TEMPLATE = `Task:Generate Cypher statement to query a graph database.\nInstructions:\nUse only the provided relationship types and properties in the schema.\nDo not use any other relationship types or properties that are not provided.\nSchema:\n{schema}\nNote: Do not include any explanations or apologies in your responses.\nDo not respond to any questions that might ask anything else than for you to construct a Cypher statement.\nDo not include any text except the generated Cypher statement.\n\nThe question is:\n{question}`;\nexport const CYPHER_GENERATION_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_GENERATION_TEMPLATE,\n inputVariables: [\"schema\", \"question\"],\n});\n\nconst CYPHER_QA_TEMPLATE = `You are an assistant that helps to form nice and human understandable answers.\nThe information part contains the provided information that you must use to construct an answer.\nThe provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\nMake the answer sound as a response to the question. Do not mention that you based the result on the given information.\nHere is an example:\n\nQuestion: Which managers own Neo4j stocks?\nContext:[manager:CTL LLC, manager:JANE STREET GROUP LLC]\nHelpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.\n\nFollow this example when generating answers.\nIf the provided information is empty, say that you don't know the answer.\nInformation:\n{context}\n\nQuestion: {question}\nHelpful Answer:`;\nexport const CYPHER_QA_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_QA_TEMPLATE,\n inputVariables: [\"context\", \"question\"],\n});\n"],"mappings":";AAcA,MAAa,2CAA2C,IAAI,eAAe;CACzE,
|
|
1
|
+
{"version":3,"file":"prompts.js","names":[],"sources":["../../../src/chains/graph_qa/prompts.ts"],"sourcesContent":["import { PromptTemplate } from \"@langchain/core/prompts\";\n\nconst CYPHER_GENERATION_TEMPLATE = `Task:Generate Cypher statement to query a graph database.\nInstructions:\nUse only the provided relationship types and properties in the schema.\nDo not use any other relationship types or properties that are not provided.\nSchema:\n{schema}\nNote: Do not include any explanations or apologies in your responses.\nDo not respond to any questions that might ask anything else than for you to construct a Cypher statement.\nDo not include any text except the generated Cypher statement.\n\nThe question is:\n{question}`;\nexport const CYPHER_GENERATION_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_GENERATION_TEMPLATE,\n inputVariables: [\"schema\", \"question\"],\n});\n\nconst CYPHER_QA_TEMPLATE = `You are an assistant that helps to form nice and human understandable answers.\nThe information part contains the provided information that you must use to construct an answer.\nThe provided information is authoritative, you must never doubt it or try to use your internal knowledge to correct it.\nMake the answer sound as a response to the question. Do not mention that you based the result on the given information.\nHere is an example:\n\nQuestion: Which managers own Neo4j stocks?\nContext:[manager:CTL LLC, manager:JANE STREET GROUP LLC]\nHelpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks.\n\nFollow this example when generating answers.\nIf the provided information is empty, say that you don't know the answer.\nInformation:\n{context}\n\nQuestion: {question}\nHelpful Answer:`;\nexport const CYPHER_QA_PROMPT = /* #__PURE__ */ new PromptTemplate({\n template: CYPHER_QA_TEMPLATE,\n inputVariables: [\"context\", \"question\"],\n});\n"],"mappings":";AAcA,MAAa,2CAA2C,IAAI,eAAe;CACzE,UAAU;;;;;;;;;;;;CACV,gBAAgB,CAAC,UAAU,WAAW;CACvC,CAAC;AAmBF,MAAa,mCAAmC,IAAI,eAAe;CACjE,UAAU;;;;;;;;;;;;;;;;;CACV,gBAAgB,CAAC,WAAW,WAAW;CACxC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memgraph_graph.cjs","names":["Neo4jGraph"],"sources":["../../src/graphs/memgraph_graph.ts"],"sourcesContent":["import { Neo4jGraph } from \"./neo4j_graph.js\";\n\nexport interface MemgraphGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\nconst rawSchemaQuery = `\nCALL llm_util.schema(\"raw\")\nYIELD *\nRETURN *\n`;\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class MemgraphGraph extends Neo4jGraph {\n constructor({\n url,\n username,\n password,\n database = \"memgraph\",\n }: MemgraphGraphConfig) {\n super({ url, username, password, database });\n }\n\n static async initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph> {\n const graph = new MemgraphGraph(config);\n\n try {\n await graph.verifyConnectivity();\n } catch {\n console.error(\"Failed to verify connection.\");\n }\n\n try {\n await graph.refreshSchema();\n console.debug(\"Schema refreshed successfully.\");\n } catch (e) {\n const error = e as Error;\n throw new Error(error.message);\n }\n\n return graph;\n }\n\n async refreshSchema() {\n const rawSchemaQueryResult = await this.query(rawSchemaQuery);\n if (rawSchemaQueryResult?.[0]?.schema) {\n const rawSchema = rawSchemaQueryResult?.[0]?.schema;\n\n this.structuredSchema = {\n nodeProps: rawSchema.node_props,\n relProps: rawSchema.rel_props,\n relationships: rawSchema.relationships,\n };\n\n const formattedNodeProps = Object.entries(rawSchema.node_props)\n .map(([nodeName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Node name: '${nodeName}', Node properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRelProps = Object.entries(rawSchema.rel_props)\n .map(([relationshipName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Relationship name: '${relationshipName}', Relationship properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRels = rawSchema.relationships\n ?.map(\n (el: { end: string; start: string; type: string }) =>\n `(:${el.start})-[:${el.type}]->(:${el.end})`\n )\n .join(\"\\n\");\n\n this.schema = [\n \"Node properties are the following:\",\n formattedNodeProps,\n \"Relationship properties are the following:\",\n formattedRelProps,\n \"The relationships are the following:\",\n formattedRels,\n ].join(\"\\n\");\n }\n }\n}\n"],"mappings":";;AASA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;AAoBvB,IAAa,gBAAb,MAAa,sBAAsBA,oBAAAA,WAAW;CAC5C,YAAY,EACV,KACA,UACA,UACA,WAAW,cACW;AACtB,QAAM;GAAE;GAAK;GAAU;GAAU;GAAU,CAAC;;CAG9C,aAAa,WAAW,QAAqD;EAC3E,MAAM,QAAQ,IAAI,cAAc,OAAO;AAEvC,MAAI;AACF,SAAM,MAAM,oBAAoB;UAC1B;AACN,WAAQ,MAAM,+BAA+B;;AAG/C,MAAI;AACF,SAAM,MAAM,eAAe;AAC3B,WAAQ,MAAM,iCAAiC;WACxC,GAAG;AAEV,SAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"memgraph_graph.cjs","names":["Neo4jGraph","error"],"sources":["../../src/graphs/memgraph_graph.ts"],"sourcesContent":["import { Neo4jGraph } from \"./neo4j_graph.js\";\n\nexport interface MemgraphGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\nconst rawSchemaQuery = `\nCALL llm_util.schema(\"raw\")\nYIELD *\nRETURN *\n`;\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class MemgraphGraph extends Neo4jGraph {\n constructor({\n url,\n username,\n password,\n database = \"memgraph\",\n }: MemgraphGraphConfig) {\n super({ url, username, password, database });\n }\n\n static async initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph> {\n const graph = new MemgraphGraph(config);\n\n try {\n await graph.verifyConnectivity();\n } catch {\n console.error(\"Failed to verify connection.\");\n }\n\n try {\n await graph.refreshSchema();\n console.debug(\"Schema refreshed successfully.\");\n } catch (e) {\n const error = e as Error;\n throw new Error(error.message);\n }\n\n return graph;\n }\n\n async refreshSchema() {\n const rawSchemaQueryResult = await this.query(rawSchemaQuery);\n if (rawSchemaQueryResult?.[0]?.schema) {\n const rawSchema = rawSchemaQueryResult?.[0]?.schema;\n\n this.structuredSchema = {\n nodeProps: rawSchema.node_props,\n relProps: rawSchema.rel_props,\n relationships: rawSchema.relationships,\n };\n\n const formattedNodeProps = Object.entries(rawSchema.node_props)\n .map(([nodeName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Node name: '${nodeName}', Node properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRelProps = Object.entries(rawSchema.rel_props)\n .map(([relationshipName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Relationship name: '${relationshipName}', Relationship properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRels = rawSchema.relationships\n ?.map(\n (el: { end: string; start: string; type: string }) =>\n `(:${el.start})-[:${el.type}]->(:${el.end})`\n )\n .join(\"\\n\");\n\n this.schema = [\n \"Node properties are the following:\",\n formattedNodeProps,\n \"Relationship properties are the following:\",\n formattedRelProps,\n \"The relationships are the following:\",\n formattedRels,\n ].join(\"\\n\");\n }\n }\n}\n"],"mappings":";;AASA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;AAoBvB,IAAa,gBAAb,MAAa,sBAAsBA,oBAAAA,WAAW;CAC5C,YAAY,EACV,KACA,UACA,UACA,WAAW,cACW;AACtB,QAAM;GAAE;GAAK;GAAU;GAAU;GAAU,CAAC;;CAG9C,aAAa,WAAW,QAAqD;EAC3E,MAAM,QAAQ,IAAI,cAAc,OAAO;AAEvC,MAAI;AACF,SAAM,MAAM,oBAAoB;UAC1B;AACN,WAAQ,MAAM,+BAA+B;;AAG/C,MAAI;AACF,SAAM,MAAM,eAAe;AAC3B,WAAQ,MAAM,iCAAiC;WACxC,GAAG;AAEV,SAAM,IAAI,MAAMC,EAAM,QAAQ;;AAGhC,SAAO;;CAGT,MAAM,gBAAgB;EACpB,MAAM,uBAAuB,MAAM,KAAK,MAAM,eAAe;AAC7D,MAAI,uBAAuB,IAAI,QAAQ;GACrC,MAAM,YAAY,uBAAuB,IAAI;AAE7C,QAAK,mBAAmB;IACtB,WAAW,UAAU;IACrB,UAAU,UAAU;IACpB,eAAe,UAAU;IAC1B;GAED,MAAM,qBAAqB,OAAO,QAAQ,UAAU,WAAW,CAC5D,KAAK,CAAC,UAAU,gBAAgB;AAE/B,WAAO,eAAe,SAAS,sBADT,KAAK,UAAU,WAC6B;KAClE,CACD,KAAK,KAAK;GAEb,MAAM,oBAAoB,OAAO,QAAQ,UAAU,UAAU,CAC1D,KAAK,CAAC,kBAAkB,gBAAgB;AAEvC,WAAO,uBAAuB,iBAAiB,8BADzB,KAAK,UAAU,WACqD;KAC1F,CACD,KAAK,KAAK;GAEb,MAAM,gBAAgB,UAAU,eAC5B,KACC,OACC,KAAK,GAAG,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,GAC7C,CACA,KAAK,KAAK;AAEb,QAAK,SAAS;IACZ;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memgraph_graph.js","names":[],"sources":["../../src/graphs/memgraph_graph.ts"],"sourcesContent":["import { Neo4jGraph } from \"./neo4j_graph.js\";\n\nexport interface MemgraphGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\nconst rawSchemaQuery = `\nCALL llm_util.schema(\"raw\")\nYIELD *\nRETURN *\n`;\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class MemgraphGraph extends Neo4jGraph {\n constructor({\n url,\n username,\n password,\n database = \"memgraph\",\n }: MemgraphGraphConfig) {\n super({ url, username, password, database });\n }\n\n static async initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph> {\n const graph = new MemgraphGraph(config);\n\n try {\n await graph.verifyConnectivity();\n } catch {\n console.error(\"Failed to verify connection.\");\n }\n\n try {\n await graph.refreshSchema();\n console.debug(\"Schema refreshed successfully.\");\n } catch (e) {\n const error = e as Error;\n throw new Error(error.message);\n }\n\n return graph;\n }\n\n async refreshSchema() {\n const rawSchemaQueryResult = await this.query(rawSchemaQuery);\n if (rawSchemaQueryResult?.[0]?.schema) {\n const rawSchema = rawSchemaQueryResult?.[0]?.schema;\n\n this.structuredSchema = {\n nodeProps: rawSchema.node_props,\n relProps: rawSchema.rel_props,\n relationships: rawSchema.relationships,\n };\n\n const formattedNodeProps = Object.entries(rawSchema.node_props)\n .map(([nodeName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Node name: '${nodeName}', Node properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRelProps = Object.entries(rawSchema.rel_props)\n .map(([relationshipName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Relationship name: '${relationshipName}', Relationship properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRels = rawSchema.relationships\n ?.map(\n (el: { end: string; start: string; type: string }) =>\n `(:${el.start})-[:${el.type}]->(:${el.end})`\n )\n .join(\"\\n\");\n\n this.schema = [\n \"Node properties are the following:\",\n formattedNodeProps,\n \"Relationship properties are the following:\",\n formattedRelProps,\n \"The relationships are the following:\",\n formattedRels,\n ].join(\"\\n\");\n }\n }\n}\n"],"mappings":";;AASA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;AAoBvB,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,YAAY,EACV,KACA,UACA,UACA,WAAW,cACW;AACtB,QAAM;GAAE;GAAK;GAAU;GAAU;GAAU,CAAC;;CAG9C,aAAa,WAAW,QAAqD;EAC3E,MAAM,QAAQ,IAAI,cAAc,OAAO;AAEvC,MAAI;AACF,SAAM,MAAM,oBAAoB;UAC1B;AACN,WAAQ,MAAM,+BAA+B;;AAG/C,MAAI;AACF,SAAM,MAAM,eAAe;AAC3B,WAAQ,MAAM,iCAAiC;WACxC,GAAG;AAEV,SAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"memgraph_graph.js","names":["error"],"sources":["../../src/graphs/memgraph_graph.ts"],"sourcesContent":["import { Neo4jGraph } from \"./neo4j_graph.js\";\n\nexport interface MemgraphGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n}\n\nconst rawSchemaQuery = `\nCALL llm_util.schema(\"raw\")\nYIELD *\nRETURN *\n`;\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class MemgraphGraph extends Neo4jGraph {\n constructor({\n url,\n username,\n password,\n database = \"memgraph\",\n }: MemgraphGraphConfig) {\n super({ url, username, password, database });\n }\n\n static async initialize(config: MemgraphGraphConfig): Promise<MemgraphGraph> {\n const graph = new MemgraphGraph(config);\n\n try {\n await graph.verifyConnectivity();\n } catch {\n console.error(\"Failed to verify connection.\");\n }\n\n try {\n await graph.refreshSchema();\n console.debug(\"Schema refreshed successfully.\");\n } catch (e) {\n const error = e as Error;\n throw new Error(error.message);\n }\n\n return graph;\n }\n\n async refreshSchema() {\n const rawSchemaQueryResult = await this.query(rawSchemaQuery);\n if (rawSchemaQueryResult?.[0]?.schema) {\n const rawSchema = rawSchemaQueryResult?.[0]?.schema;\n\n this.structuredSchema = {\n nodeProps: rawSchema.node_props,\n relProps: rawSchema.rel_props,\n relationships: rawSchema.relationships,\n };\n\n const formattedNodeProps = Object.entries(rawSchema.node_props)\n .map(([nodeName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Node name: '${nodeName}', Node properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRelProps = Object.entries(rawSchema.rel_props)\n .map(([relationshipName, properties]) => {\n const propertiesStr = JSON.stringify(properties);\n return `Relationship name: '${relationshipName}', Relationship properties: ${propertiesStr}`;\n })\n .join(\"\\n\");\n\n const formattedRels = rawSchema.relationships\n ?.map(\n (el: { end: string; start: string; type: string }) =>\n `(:${el.start})-[:${el.type}]->(:${el.end})`\n )\n .join(\"\\n\");\n\n this.schema = [\n \"Node properties are the following:\",\n formattedNodeProps,\n \"Relationship properties are the following:\",\n formattedRelProps,\n \"The relationships are the following:\",\n formattedRels,\n ].join(\"\\n\");\n }\n }\n}\n"],"mappings":";;AASA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;AAoBvB,IAAa,gBAAb,MAAa,sBAAsB,WAAW;CAC5C,YAAY,EACV,KACA,UACA,UACA,WAAW,cACW;AACtB,QAAM;GAAE;GAAK;GAAU;GAAU;GAAU,CAAC;;CAG9C,aAAa,WAAW,QAAqD;EAC3E,MAAM,QAAQ,IAAI,cAAc,OAAO;AAEvC,MAAI;AACF,SAAM,MAAM,oBAAoB;UAC1B;AACN,WAAQ,MAAM,+BAA+B;;AAG/C,MAAI;AACF,SAAM,MAAM,eAAe;AAC3B,WAAQ,MAAM,iCAAiC;WACxC,GAAG;AAEV,SAAM,IAAI,MAAMA,EAAM,QAAQ;;AAGhC,SAAO;;CAGT,MAAM,gBAAgB;EACpB,MAAM,uBAAuB,MAAM,KAAK,MAAM,eAAe;AAC7D,MAAI,uBAAuB,IAAI,QAAQ;GACrC,MAAM,YAAY,uBAAuB,IAAI;AAE7C,QAAK,mBAAmB;IACtB,WAAW,UAAU;IACrB,UAAU,UAAU;IACpB,eAAe,UAAU;IAC1B;GAED,MAAM,qBAAqB,OAAO,QAAQ,UAAU,WAAW,CAC5D,KAAK,CAAC,UAAU,gBAAgB;AAE/B,WAAO,eAAe,SAAS,sBADT,KAAK,UAAU,WAC6B;KAClE,CACD,KAAK,KAAK;GAEb,MAAM,oBAAoB,OAAO,QAAQ,UAAU,UAAU,CAC1D,KAAK,CAAC,kBAAkB,gBAAgB;AAEvC,WAAO,uBAAuB,iBAAiB,8BADzB,KAAK,UAAU,WACqD;KAC1F,CACD,KAAK,KAAK;GAEb,MAAM,gBAAgB,UAAU,eAC5B,KACC,OACC,KAAK,GAAG,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,GAC7C,CACA,KAAK,KAAK;AAEb,QAAK,SAAS;IACZ;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j_graph.cjs","names":["neo4j"],"sources":["../../src/graphs/neo4j_graph.ts"],"sourcesContent":["import neo4j, {\n RoutingControl,\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport { sha256 } from \"@langchain/core/utils/hash\";\nimport { GraphDocument } from \"./document.js\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport interface Neo4jGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n timeoutMs?: number;\n enhancedSchema?: boolean;\n}\n\nexport interface StructuredSchema {\n nodeProps: { [key: NodeType[\"labels\"]]: NodeType[\"properties\"] };\n relProps: { [key: RelType[\"type\"]]: RelType[\"properties\"] };\n relationships: PathType[];\n metadata?: {\n constraint: Record<string, Any>;\n index: Record<string, Any>;\n };\n}\n\nexport interface AddGraphDocumentsConfig {\n baseEntityLabel?: boolean;\n includeSource?: boolean;\n}\n\nexport type NodeType = {\n labels: string;\n properties: { property: string; type: string }[];\n};\n\nexport type RelType = {\n type: string;\n properties: { property: string; type: string }[];\n};\n\nexport type PathType = { start: string; type: string; end: string };\n\nexport const BASE_ENTITY_LABEL = \"__Entity__\";\n\nconst DISTINCT_VALUE_LIMIT = 10;\nconst LIST_LIMIT = 128;\nconst EXHAUSTIVE_SEARCH_LIMIT = 10000;\nconst EXCLUDED_LABELS = [\"Bloom_Perspective\", \"Bloom_Scene\"];\nconst EXCLUDED_RELS = [\"Bloom_HAS_SCENE\"];\n\nconst INCLUDE_DOCS_QUERY = `\nMERGE (d:Document {id:$document.metadata.id})\nSET d.text = $document.pageContent\nSET d += $document.metadata\nWITH d\n`;\n\nconst NODE_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"node\"\n AND NOT label IN $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {labels: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"relationship\"\n AND NOT label in $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {type: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE type = \"RELATIONSHIP\" AND elementType = \"node\"\nUNWIND other AS other_node\nWITH * WHERE NOT label IN $EXCLUDED_LABELS\n AND NOT other_node IN $EXCLUDED_LABELS\nRETURN {start: label, type: property, end: toString(other_node)} AS output\n`;\n\nfunction isDistinctMoreThanLimit(\n distinct_count = 11,\n limit = DISTINCT_VALUE_LIMIT\n): boolean {\n return distinct_count !== undefined && distinct_count > limit;\n}\n\nfunction cleanStringValues(text: string) {\n return text.replace(/\\n/g, \" \").replace(/\\r/g, \" \");\n}\n\nexport function formatSchema(\n schema: Record<string, Any>,\n isEnhanced: boolean\n): string {\n let formattedNodeProps: string[] = [];\n let formattedRelProps: string[] = [];\n\n if (isEnhanced) {\n for (const [nodeType, properties] of Object.entries(schema.nodeProps)) {\n formattedNodeProps.push(`- **${nodeType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min !== undefined) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values.length > 0) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (!prop.min_size || prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedNodeProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n\n for (const [relType, properties] of Object.entries(schema.relProps)) {\n formattedRelProps.push(`- **${relType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedRelProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n } else {\n formattedNodeProps = Object.entries(schema.nodeProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type}`)\n .join(\", \");\n return `${key} {${propsStr}}`;\n }\n );\n\n formattedRelProps = Object.entries(schema.relProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type} `)\n .join(\", \");\n return `${key} {${propsStr} } `;\n }\n );\n }\n\n const formattedRels = schema.relationships.map(\n (el: Record<string, string>) =>\n `(: ${el.start}) - [: ${el.type}] -> (:${el.end})`\n );\n\n return [\n \"Node properties are the following:\",\n formattedNodeProps?.join(\", \"),\n \"Relationship properties are the following:\",\n formattedRelProps?.join(\", \"),\n \"The relationships are the following:\",\n formattedRels?.join(\", \"),\n ].join(\"\\n\");\n}\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jGraph {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private timeoutMs: number | undefined;\n\n private enhancedSchema: boolean;\n\n protected schema = \"\";\n\n protected structuredSchema: StructuredSchema = {\n nodeProps: {},\n relProps: {},\n relationships: [],\n metadata: {\n constraint: {},\n index: {},\n },\n };\n\n constructor({\n url,\n username,\n password,\n database = \"neo4j\",\n timeoutMs,\n enhancedSchema = false,\n }: Neo4jGraphConfig) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n this.timeoutMs = timeoutMs;\n this.enhancedSchema = enhancedSchema;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n static async initialize(config: Neo4jGraphConfig): Promise<Neo4jGraph> {\n const graph = new Neo4jGraph(config);\n\n await graph.verifyConnectivity();\n\n try {\n await graph.refreshSchema();\n } catch (error: Any) {\n if (error.code === \"Neo.ClientError.Procedure.ProcedureNotFound\") {\n throw new Error(\n \"Could not use APOC procedures. Please ensure the APOC plugin is installed in Neo4j and that 'apoc.meta.data()' is allowed in Neo4j configuration.\"\n );\n }\n\n throw error;\n } finally {\n console.log(\"Schema refreshed successfully.\");\n }\n\n return graph;\n }\n\n getSchema(): string {\n return this.schema;\n }\n\n getStructuredSchema() {\n return this.structuredSchema;\n }\n\n async query<RecordShape extends Record<string, Any> = Record<string, Any>>(\n query: string,\n params: Record<string, Any> = {},\n routing: RoutingControl = neo4j.routing.WRITE\n ): Promise<RecordShape[]> {\n const result = await this.driver.executeQuery<RecordShape>(query, params, {\n database: this.database,\n routing,\n transactionConfig: { timeout: this.timeoutMs },\n });\n return toObjects<RecordShape>(result.records);\n }\n\n async verifyConnectivity() {\n await this.driver.getServerInfo();\n }\n\n async refreshSchema() {\n const nodeProperties = (\n await this.query<{ output: NodeType }>(NODE_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const relationshipsProperties = (\n await this.query<{ output: RelType }>(REL_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_RELS,\n })\n )?.map((el) => el.output);\n\n const relationships: PathType[] = (\n await this.query<{ output: PathType }>(REL_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const constraint = await this.query(\"SHOW CONSTRAINTS\");\n\n const index = await this.query(\"SHOW INDEXES YIELD *\");\n\n this.structuredSchema = {\n nodeProps: Object.fromEntries(\n nodeProperties?.map((el) => [el.labels, el.properties]) || []\n ),\n relProps: Object.fromEntries(\n relationshipsProperties?.map((el) => [el.type, el.properties]) || []\n ),\n relationships: relationships || [],\n metadata: {\n constraint,\n index,\n },\n };\n\n if (this.enhancedSchema) {\n const schemaCounts = await this.query(\n `CALL apoc.meta.graphSample() YIELD nodes, relationships ` +\n `RETURN nodes, [rel in relationships | {name: apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships`\n );\n for (const node of schemaCounts[0].nodes) {\n if (EXCLUDED_LABELS.includes(node.name)) {\n continue;\n }\n\n const nodeProps = this.structuredSchema.nodeProps[node.name];\n\n if (!nodeProps) {\n continue;\n }\n\n const enhancedCypher = await this.enhancedSchemaCypher(\n node.name,\n nodeProps,\n node.count < EXHAUSTIVE_SEARCH_LIMIT\n );\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of nodeProps) {\n if (enhancedInfo[prop.property]) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n\n for (const rel of schemaCounts[0].relationships) {\n if (EXCLUDED_RELS.includes(rel.name)) {\n continue;\n }\n const relProps = this.structuredSchema.relProps[rel.name];\n\n if (!relProps) {\n continue;\n }\n const enhancedCypher = await this.enhancedSchemaCypher(\n rel.name,\n relProps,\n rel.count < EXHAUSTIVE_SEARCH_LIMIT,\n true\n );\n\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of relProps) {\n if (prop.property in enhancedInfo) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n }\n\n this.schema = formatSchema(this.structuredSchema, this.enhancedSchema);\n }\n\n async enhancedSchemaCypher(\n labelOrType: string,\n properties: { property: string; type: string }[],\n exhaustive: boolean,\n isRelationship = false\n ) {\n let matchClause = isRelationship\n ? `MATCH ()-[n:\\`${labelOrType}\\`]->()`\n : `MATCH (n:\\`${labelOrType}\\`)`;\n\n const withClauses: string[] = [];\n const returnClauses: string[] = [];\n const outputDict: { [key: string]: string } = {};\n\n if (exhaustive) {\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n if (propType === \"STRING\") {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(\n `values: \\`${propName}_values\\`[..${DISTINCT_VALUE_LIMIT}], distinct_count: size(\\`${propName}_values\\`)`\n );\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n } else {\n matchClause += ` WITH n LIMIT 5`;\n\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n const propIndex = this.structuredSchema?.metadata?.index.filter(\n (el: Any) =>\n el.label === labelOrType &&\n el.properties[0] === propName &&\n el.type === \"RANGE\"\n );\n\n if (propType === \"STRING\") {\n if (\n propIndex.length > 0 &&\n propIndex[0].size > 0 &&\n propIndex[0].distinctValues <= DISTINCT_VALUE_LIMIT\n ) {\n const distinctValuesPromise = await this.query(\n `CALL apoc.schema.properties.distinct('${labelOrType}', '${propName}') YIELD value`\n );\n const distinctValues = distinctValuesPromise[0].value;\n returnClauses.push(\n `values: ${distinctValues}, distinct_count: ${distinctValues.length}`\n );\n } else {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n if (!propIndex) {\n withClauses.push(\n `collect(distinct toString(n.\\`${propName}\\`)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n } else {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n }\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n }\n\n const withClause = `WITH ${withClauses.join(\", \")}`;\n const returnClause = `RETURN {${Object.entries(outputDict)\n .map(([k, v]) => `\\`${k}\\`: ${v}`)\n .join(\", \")}} AS output`;\n\n const cypherQuery = [matchClause, withClause, returnClause].join(\"\\n\");\n\n return cypherQuery;\n }\n\n async addGraphDocuments(\n graphDocuments: GraphDocument[],\n config: AddGraphDocumentsConfig = {}\n ): Promise<void> {\n const { baseEntityLabel } = config;\n\n if (baseEntityLabel) {\n const constraintExists =\n this.structuredSchema?.metadata?.constraint?.some(\n (el: Any) =>\n JSON.stringify(el.labelsOrTypes) ===\n JSON.stringify([BASE_ENTITY_LABEL]) &&\n JSON.stringify(el.properties) === JSON.stringify([\"id\"])\n ) ?? false;\n\n if (!constraintExists) {\n await this.query(`\n CREATE CONSTRAINT IF NOT EXISTS FOR (b:${BASE_ENTITY_LABEL})\n REQUIRE b.id IS UNIQUE;\n `);\n await this.refreshSchema();\n }\n }\n\n const nodeImportQuery = getNodeImportQuery(config);\n const relImportQuery = getRelImportQuery(config);\n\n for (const document of graphDocuments) {\n if (!document.source.metadata.id) {\n document.source.metadata.id = sha256(document.source.pageContent);\n }\n\n await this.query(nodeImportQuery, {\n data: document.nodes.map((el: Any) => ({ ...el })),\n document: { ...document.source },\n });\n\n await this.query(relImportQuery, {\n data: document.relationships.map((el: Any) => ({\n source: el.source.id,\n source_label: el.source.type,\n target: el.target.id,\n target_label: el.target.type,\n type: el.type.replace(/ /g, \"_\").toUpperCase(),\n properties: el.properties,\n })),\n });\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n\nfunction getNodeImportQuery({\n baseEntityLabel,\n includeSource,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n MERGE(source: \\`${BASE_ENTITY_LABEL}\\` {id: row.id})\n SET source += row.properties\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(source)\" : \"\"}\n WITH source, row\n CALL apoc.create.addLabels(source, [row.type]) YIELD node\n RETURN distinct 'done' AS result\n `;\n } else {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n CALL apoc.merge.node([row.type], {id: row.id},\n row.properties, {}) YIELD node\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(node)\" : \"\"}\n RETURN distinct 'done' AS result\n `;\n }\n}\n\nfunction getRelImportQuery({\n baseEntityLabel,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n UNWIND $data AS row\n MERGE (source:\\`${BASE_ENTITY_LABEL}\\` {id: row.source})\n MERGE (target:\\`${BASE_ENTITY_LABEL}\\` {id: row.target})\n WITH source, target, row\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n } else {\n return `\n UNWIND $data AS row\n CALL apoc.merge.node([row.source_label], {id: row.source},\n {}, {}) YIELD node as source\n CALL apoc.merge.node([row.target_label], {id: row.target},\n {}, {}) YIELD node as target\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n }\n}\n\nfunction toObjects<\n RecordShape extends Record<string, Any> = Record<string, Any>,\n>(records: Neo4jRecord<RecordShape>): RecordShape[] {\n return records.map((record: Any) => {\n const rObj = record.toObject();\n const out: Partial<RecordShape> = {};\n Object.keys(rObj).forEach((key: keyof RecordShape) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out as RecordShape;\n });\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nconst extractPathForRows = (path: Neo4jPath) => {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n};\n"],"mappings":";;;;;AAgDA,MAAa,oBAAoB;AAEjC,MAAM,uBAAuB;AAC7B,MAAM,aAAa;AACnB,MAAM,0BAA0B;AAChC,MAAM,kBAAkB,CAAC,qBAAqB,cAAc;AAC5D,MAAM,gBAAgB,CAAC,kBAAkB;AAEzC,MAAM,qBAAqB;;;;;;AAO3B,MAAM,wBAAwB;;;;;;;;AAS9B,MAAM,uBAAuB;;;;;;;;AAS7B,MAAM,YAAY;;;;;;;;;AAUlB,SAAS,wBACP,iBAAiB,IACjB,QAAQ,sBACC;AACT,QAAO,mBAAmB,KAAA,KAAa,iBAAiB;;AAG1D,SAAS,kBAAkB,MAAc;AACvC,QAAO,KAAK,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI;;AAGrD,SAAgB,aACd,QACA,YACQ;CACR,IAAI,qBAA+B,EAAE;CACrC,IAAI,oBAA8B,EAAE;AAEpC,KAAI,YAAY;AACd,OAAK,MAAM,CAAC,UAAU,eAAe,OAAO,QAAQ,OAAO,UAAU,EAAE;AACrE,sBAAmB,KAAK,OAAO,SAAS,IAAI;AAE5C,QAAK,MAAM,QAAQ,YAA0C;IAC3D,IAAI,UAAU;AAEd,QAAI,KAAK,SAAS;SACZ,KAAK,OAAO,SAAS,EACvB,KAAI,wBAAwB,KAAK,eAAe,CAC9C,WAAU,YAAY,kBAAkB,KAAK,OAAO,GAAG;SAEvD,WAAU,sBAAsB,KAAK,OAClC,IAAI,kBAAkB,CACtB,KAAK,KAAK;eAIjB;KAAC;KAAW;KAAS;KAAQ;KAAa;KAAkB,CAAC,SAC3D,KAAK,KACN;SAEG,KAAK,QAAQ,KAAA,EACf,WAAU,QAAQ,KAAK,IAAI,SAAS,KAAK;cAErC,KAAK,OAAO,SAAS,EACvB,WAAU,YAAY,KAAK,OAAO;eAG7B,KAAK,SAAS,QAAQ;AAC/B,SAAI,CAAC,KAAK,YAAY,KAAK,WAAW,WACpC;AAEF,eAAU,aAAa,KAAK,SAAS,cAAc,KAAK;;AAG1D,uBAAmB,KACjB,SAAS,KAAK,SAAS,MAAM,KAAK,KAAK,GAAG,UAC3C;;;AAIL,OAAK,MAAM,CAAC,SAAS,eAAe,OAAO,QAAQ,OAAO,SAAS,EAAE;AACnE,qBAAkB,KAAK,OAAO,QAAQ,IAAI;AAE1C,QAAK,MAAM,QAAQ,YAA0C;IAC3D,IAAI,UAAU;AAEd,QAAI,KAAK,SAAS;SACZ,KAAK,OAAO,SAAS,EACvB,KAAI,wBAAwB,KAAK,eAAe,CAC9C,WAAU,YAAY,kBAAkB,KAAK,OAAO,GAAG;SAEvD,WAAU,sBAAsB,KAAK,OAClC,IAAI,kBAAkB,CACtB,KAAK,KAAK;eAIjB;KAAC;KAAW;KAAS;KAAQ;KAAa;KAAkB,CAAC,SAC3D,KAAK,KACN;SAEG,KAAK,IACP,WAAU,QAAQ,KAAK,IAAI,SAAS,KAAK;cAErC,KAAK,OACP,WAAU,YAAY,KAAK,OAAO;eAG7B,KAAK,SAAS,QAAQ;AAC/B,SAAI,KAAK,WAAW,WAClB;AAEF,eAAU,aAAa,KAAK,SAAS,cAAc,KAAK;;AAG1D,sBAAkB,KAChB,SAAS,KAAK,SAAS,MAAM,KAAK,KAAK,GAAG,UAC3C;;;QAGA;AACL,uBAAqB,OAAO,QAAQ,OAAO,UAAU,CAAC,KACnD,CAAC,KAAK,WAA0B;AAI/B,UAAO,GAAG,IAAI,IAHG,MACd,KAAK,SAA8B,GAAG,KAAK,SAAS,IAAI,KAAK,OAAO,CACpE,KAAK,KAAK,CACc;IAE9B;AAED,sBAAoB,OAAO,QAAQ,OAAO,SAAS,CAAC,KACjD,CAAC,KAAK,WAA0B;AAI/B,UAAO,GAAG,IAAI,IAHG,MACd,KAAK,SAA8B,GAAG,KAAK,SAAS,IAAI,KAAK,KAAK,GAAG,CACrE,KAAK,KAAK,CACc;IAE9B;;CAGH,MAAM,gBAAgB,OAAO,cAAc,KACxC,OACC,MAAM,GAAG,MAAM,SAAS,GAAG,KAAK,SAAS,GAAG,IAAI,GACnD;AAED,QAAO;EACL;EACA,oBAAoB,KAAK,KAAK;EAC9B;EACA,mBAAmB,KAAK,KAAK;EAC7B;EACA,eAAe,KAAK,KAAK;EAC1B,CAAC,KAAK,KAAK;;;;;;;;;;;;;;;;AAiBd,IAAa,aAAb,MAAa,WAAW;CACtB;CAEA;CAEA;CAEA;CAEA,SAAmB;CAEnB,mBAA+C;EAC7C,WAAW,EAAE;EACb,UAAU,EAAE;EACZ,eAAe,EAAE;EACjB,UAAU;GACR,YAAY,EAAE;GACd,OAAO,EAAE;GACV;EACF;CAED,YAAY,EACV,KACA,UACA,UACA,WAAW,SACX,WACA,iBAAiB,SACE;AACnB,MAAI;AACF,QAAK,SAASA,aAAAA,QAAM,OAAO,KAAKA,aAAAA,QAAM,KAAK,MAAM,UAAU,SAAS,CAAC;AACrE,QAAK,WAAW;AAChB,QAAK,YAAY;AACjB,QAAK,iBAAiB;UAChB;AACN,SAAM,IAAI,MACR,iFACD;;;CAIL,aAAa,WAAW,QAA+C;EACrE,MAAM,QAAQ,IAAI,WAAW,OAAO;AAEpC,QAAM,MAAM,oBAAoB;AAEhC,MAAI;AACF,SAAM,MAAM,eAAe;WACpB,OAAY;AACnB,OAAI,MAAM,SAAS,8CACjB,OAAM,IAAI,MACR,oJACD;AAGH,SAAM;YACE;AACR,WAAQ,IAAI,iCAAiC;;AAG/C,SAAO;;CAGT,YAAoB;AAClB,SAAO,KAAK;;CAGd,sBAAsB;AACpB,SAAO,KAAK;;CAGd,MAAM,MACJ,OACA,SAA8B,EAAE,EAChC,UAA0BA,aAAAA,QAAM,QAAQ,OAChB;AAMxB,SAAO,WALQ,MAAM,KAAK,OAAO,aAA0B,OAAO,QAAQ;GACxE,UAAU,KAAK;GACf;GACA,mBAAmB,EAAE,SAAS,KAAK,WAAW;GAC/C,CAAC,EACmC,QAAQ;;CAG/C,MAAM,qBAAqB;AACzB,QAAM,KAAK,OAAO,eAAe;;CAGnC,MAAM,gBAAgB;EACpB,MAAM,kBACJ,MAAM,KAAK,MAA4B,uBAAuB,EAC5D,iBAAiB,gBAAgB,OAAO,CAAC,kBAAkB,CAAC,EAC7D,CAAC,GACD,KAAK,OAAO,GAAG,OAAO;EAEzB,MAAM,2BACJ,MAAM,KAAK,MAA2B,sBAAsB,EAC1D,iBAAiB,eAClB,CAAC,GACD,KAAK,OAAO,GAAG,OAAO;EAEzB,MAAM,iBACJ,MAAM,KAAK,MAA4B,WAAW,EAChD,iBAAiB,gBAAgB,OAAO,CAAC,kBAAkB,CAAC,EAC7D,CAAC,GACD,KAAK,OAAO,GAAG,OAAO;EAEzB,MAAM,aAAa,MAAM,KAAK,MAAM,mBAAmB;EAEvD,MAAM,QAAQ,MAAM,KAAK,MAAM,uBAAuB;AAEtD,OAAK,mBAAmB;GACtB,WAAW,OAAO,YAChB,gBAAgB,KAAK,OAAO,CAAC,GAAG,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,CAC9D;GACD,UAAU,OAAO,YACf,yBAAyB,KAAK,OAAO,CAAC,GAAG,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,CACrE;GACD,eAAe,iBAAiB,EAAE;GAClC,UAAU;IACR;IACA;IACD;GACF;AAED,MAAI,KAAK,gBAAgB;GACvB,MAAM,eAAe,MAAM,KAAK,MAC9B,iMAED;AACD,QAAK,MAAM,QAAQ,aAAa,GAAG,OAAO;AACxC,QAAI,gBAAgB,SAAS,KAAK,KAAK,CACrC;IAGF,MAAM,YAAY,KAAK,iBAAiB,UAAU,KAAK;AAEvD,QAAI,CAAC,UACH;IAGF,MAAM,iBAAiB,MAAM,KAAK,qBAChC,KAAK,MACL,WACA,KAAK,QAAQ,wBACd;IAED,MAAM,gBADsB,MAAM,KAAK,MAAM,eAAe,EACnB,GAAG;AAE5C,SAAK,MAAM,QAAQ,UACjB,KAAI,aAAa,KAAK,UACpB,QAAO,OAAO,MAAM,aAAa,KAAK,UAAU;;AAKtD,QAAK,MAAM,OAAO,aAAa,GAAG,eAAe;AAC/C,QAAI,cAAc,SAAS,IAAI,KAAK,CAClC;IAEF,MAAM,WAAW,KAAK,iBAAiB,SAAS,IAAI;AAEpD,QAAI,CAAC,SACH;IAEF,MAAM,iBAAiB,MAAM,KAAK,qBAChC,IAAI,MACJ,UACA,IAAI,QAAQ,yBACZ,KACD;IAGD,MAAM,gBADsB,MAAM,KAAK,MAAM,eAAe,EACnB,GAAG;AAE5C,SAAK,MAAM,QAAQ,SACjB,KAAI,KAAK,YAAY,aACnB,QAAO,OAAO,MAAM,aAAa,KAAK,UAAU;;;AAMxD,OAAK,SAAS,aAAa,KAAK,kBAAkB,KAAK,eAAe;;CAGxE,MAAM,qBACJ,aACA,YACA,YACA,iBAAiB,OACjB;EACA,IAAI,cAAc,iBACd,iBAAiB,YAAY,WAC7B,cAAc,YAAY;EAE9B,MAAM,cAAwB,EAAE;EAChC,MAAM,gBAA0B,EAAE;EAClC,MAAM,aAAwC,EAAE;AAEhD,MAAI,WACF,MAAK,MAAM,QAAQ,YAAY;GAC7B,MAAM,WAAW,KAAK;GACtB,MAAM,WAAW,KAAK;AAEtB,OAAI,aAAa,UAAU;AACzB,gBAAY,KACV,kCAAkC,SAAS,mBAAmB,SAAS,WACxE;AACD,kBAAc,KACZ,aAAa,SAAS,cAAc,qBAAqB,4BAA4B,SAAS,YAC/F;cAED;IAAC;IAAW;IAAS;IAAQ;IAAa;IAAkB,CAAC,SAC3D,SACD,EACD;AACA,gBAAY,KAAK,WAAW,SAAS,WAAW,SAAS,QAAQ;AACjE,gBAAY,KAAK,WAAW,SAAS,WAAW,SAAS,QAAQ;AACjE,gBAAY,KACV,sBAAsB,SAAS,WAAW,SAAS,aACpD;AACD,kBAAc,KACZ,mBAAmB,SAAS,2BAA2B,SAAS,6BAA6B,SAAS,aACvG;cACQ,aAAa,QAAQ;AAC9B,gBAAY,KACV,gBAAgB,SAAS,YAAY,SAAS,4BAA4B,SAAS,YAAY,SAAS,aACzG;AACD,kBAAc,KACZ,eAAe,SAAS,2BAA2B,SAAS,aAC7D;cACQ;IAAC;IAAW;IAAS;IAAW,CAAC,SAAS,SAAS,CAC5D;AAEF,cAAW,YAAY,IAAI,cAAc,KAAK,CAAC;;OAE5C;AACL,kBAAe;AAEf,QAAK,MAAM,QAAQ,YAAY;IAC7B,MAAM,WAAW,KAAK;IACtB,MAAM,WAAW,KAAK;IAEtB,MAAM,YAAY,KAAK,kBAAkB,UAAU,MAAM,QACtD,OACC,GAAG,UAAU,eACb,GAAG,WAAW,OAAO,YACrB,GAAG,SAAS,QACf;AAED,QAAI,aAAa,SACf,KACE,UAAU,SAAS,KACnB,UAAU,GAAG,OAAO,KACpB,UAAU,GAAG,kBAAkB,sBAC/B;KAIA,MAAM,kBAHwB,MAAM,KAAK,MACvC,yCAAyC,YAAY,MAAM,SAAS,gBACrE,EAC4C,GAAG;AAChD,mBAAc,KACZ,WAAW,eAAe,oBAAoB,eAAe,SAC9D;WACI;AACL,iBAAY,KACV,kCAAkC,SAAS,mBAAmB,SAAS,WACxE;AACD,mBAAc,KAAK,WAAW,SAAS,SAAS;;aAGlD;KAAC;KAAW;KAAS;KAAQ;KAAa;KAAkB,CAAC,SAC3D,SACD,CAED,KAAI,CAAC,WAAW;AACd,iBAAY,KACV,iCAAiC,SAAS,YAAY,SAAS,WAChE;AACD,mBAAc,KAAK,WAAW,SAAS,SAAS;WAC3C;AACL,iBAAY,KAAK,WAAW,SAAS,WAAW,SAAS,QAAQ;AACjE,iBAAY,KAAK,WAAW,SAAS,WAAW,SAAS,QAAQ;AACjE,iBAAY,KACV,sBAAsB,SAAS,WAAW,SAAS,aACpD;AACD,mBAAc,KACZ,mBAAmB,SAAS,2BAA2B,SAAS,6BAA6B,SAAS,aACvG;;aAEM,aAAa,QAAQ;AAC9B,iBAAY,KACV,gBAAgB,SAAS,YAAY,SAAS,4BAA4B,SAAS,YAAY,SAAS,aACzG;AACD,mBAAc,KACZ,eAAe,SAAS,2BAA2B,SAAS,aAC7D;eACQ;KAAC;KAAW;KAAS;KAAW,CAAC,SAAS,SAAS,CAC5D;AAGF,eAAW,YAAY,IAAI,cAAc,KAAK,CAAC;;;EAInD,MAAM,aAAa,QAAQ,YAAY,KAAK,KAAK;EACjD,MAAM,eAAe,WAAW,OAAO,QAAQ,WAAW,CACvD,KAAK,CAAC,GAAG,OAAO,KAAK,EAAE,MAAM,IAAI,CACjC,KAAK,KAAK,CAAC;AAId,SAFoB;GAAC;GAAa;GAAY;GAAa,CAAC,KAAK,KAAK;;CAKxE,MAAM,kBACJ,gBACA,SAAkC,EAAE,EACrB;EACf,MAAM,EAAE,oBAAoB;AAE5B,MAAI;OASE,EAPF,KAAK,kBAAkB,UAAU,YAAY,MAC1C,OACC,KAAK,UAAU,GAAG,cAAc,KAC9B,KAAK,UAAU,CAAA,aAAmB,CAAC,IACrC,KAAK,UAAU,GAAG,WAAW,KAAK,KAAK,UAAU,CAAC,KAAK,CAAC,CAC3D,IAAI,QAEgB;AACrB,UAAM,KAAK,MAAM;mDAC0B,kBAAkB;;UAE3D;AACF,UAAM,KAAK,eAAe;;;EAI9B,MAAM,kBAAkB,mBAAmB,OAAO;EAClD,MAAM,iBAAiB,kBAAkB,OAAO;AAEhD,OAAK,MAAM,YAAY,gBAAgB;AACrC,OAAI,CAAC,SAAS,OAAO,SAAS,GAC5B,UAAS,OAAO,SAAS,MAAA,GAAA,2BAAA,QAAY,SAAS,OAAO,YAAY;AAGnE,SAAM,KAAK,MAAM,iBAAiB;IAChC,MAAM,SAAS,MAAM,KAAK,QAAa,EAAE,GAAG,IAAI,EAAE;IAClD,UAAU,EAAE,GAAG,SAAS,QAAQ;IACjC,CAAC;AAEF,SAAM,KAAK,MAAM,gBAAgB,EAC/B,MAAM,SAAS,cAAc,KAAK,QAAa;IAC7C,QAAQ,GAAG,OAAO;IAClB,cAAc,GAAG,OAAO;IACxB,QAAQ,GAAG,OAAO;IAClB,cAAc,GAAG,OAAO;IACxB,MAAM,GAAG,KAAK,QAAQ,MAAM,IAAI,CAAC,aAAa;IAC9C,YAAY,GAAG;IAChB,EAAE,EACJ,CAAC;;;CAIN,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO;;;AAI7B,SAAS,mBAAmB,EAC1B,iBACA,iBACkC;AAClC,KAAI,gBACF,QAAO;YACC,gBAAgB,qBAAqB,GAAG;;gCAEpB,kBAAkB;;YAEtC,gBAAgB,oCAAoC,GAAG;;;;;KAM/D,QAAO;YACC,gBAAgB,qBAAqB,GAAG;;;;YAIxC,gBAAgB,kCAAkC,GAAG;;;;AAMjE,SAAS,kBAAkB,EACzB,mBACkC;AAClC,KAAI,gBACF,QAAO;;4BAEiB,kBAAkB;4BAClB,kBAAkB;;;;;;KAO1C,QAAO;;;;;;;;;;;AAaX,SAAS,UAEP,SAAkD;AAClD,QAAO,QAAQ,KAAK,WAAgB;EAClC,MAAM,OAAO,OAAO,UAAU;EAC9B,MAAM,MAA4B,EAAE;AACpC,SAAO,KAAK,KAAK,CAAC,SAAS,QAA2B;AACpD,OAAI,OAAO,gBAAgB,KAAK,KAAK;IACrC;AACF,SAAO;GACP;;AAGJ,SAAS,gBAAgB,MAAgB;AACvC,KAAIA,aAAAA,QAAM,MAAM,KAAK,CAAE,QAAO,KAAK,UAAU;AAC7C,KAAI,MAAM,QAAQ,KAAK,CAAE,QAAO,KAAK,KAAK,OAAO,gBAAgB,GAAG,CAAC;AACrE,KAAI;EAAC;EAAU;EAAU;EAAU,CAAC,QAAQ,OAAO,KAAK,KAAK,GAAI,QAAO;AACxE,KAAI,SAAS,KAAM,QAAO;AAC1B,KAAI,OAAO,SAAS,SAAU,QAAO,eAAe,KAAK;;AAG3D,SAAS,eAAe,KAAU;CAChC,MAAM,QAAQ,sBAAsB,IAAI;CACxC,IAAI,SAAc;AAClB,KAAI,MAAM,QAAQ,MAAM,CACtB,UAAS,MAAM,KAAK,SAAS,gBAAgB,KAAK,CAAC;UAC1C,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtD,WAAS,EAAE;AACX,SAAO,KAAK,MAAM,CAAC,SAAS,QAAQ;AAClC,UAAO,OAAO,gBAAgB,MAAM,KAAK;IACzC;;AAEJ,QAAO;;AAGT,SAAS,sBAAsB,KAAU;AACvC,KAEE,eAAgBA,aAAAA,QAAM,MAAM,QAE5B,eAAgBA,aAAAA,QAAM,MAAM,aAE5B,QAAO,IAAI;UAEF,eAAgBA,aAAAA,QAAM,MAAM,KAErC,QAAO,EAAE,CAAC,OAAO,MAA2B,EAAE,EAAE,mBAAmB,IAAI,CAAC;AAE1E,QAAO;;AAGT,MAAM,sBAAsB,SAAoB;CAC9C,IAAI,EAAE,aAAa;AACnB,KAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,SAAS,SAAS,EAC1D,YAAW,CAAC;EAAE,GAAG;EAAM,KAAK;EAAM,CAAQ;AAG5C,QAAO,SAAS,KAAK,YACnB;EACE,eAAe,QAAQ,MAAM;EAC7B,eAAe,QAAQ,aAAa;EACpC,eAAe,QAAQ,IAAI;EAC5B,CAAC,QAAQ,SAAS,SAAS,KAAK,CAClC"}
|
|
1
|
+
{"version":3,"file":"neo4j_graph.cjs","names":["neo4j"],"sources":["../../src/graphs/neo4j_graph.ts"],"sourcesContent":["import neo4j, {\n RoutingControl,\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport { sha256 } from \"@langchain/core/utils/hash\";\nimport { GraphDocument } from \"./document.js\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport interface Neo4jGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n timeoutMs?: number;\n enhancedSchema?: boolean;\n}\n\nexport interface StructuredSchema {\n nodeProps: { [key: NodeType[\"labels\"]]: NodeType[\"properties\"] };\n relProps: { [key: RelType[\"type\"]]: RelType[\"properties\"] };\n relationships: PathType[];\n metadata?: {\n constraint: Record<string, Any>;\n index: Record<string, Any>;\n };\n}\n\nexport interface AddGraphDocumentsConfig {\n baseEntityLabel?: boolean;\n includeSource?: boolean;\n}\n\nexport type NodeType = {\n labels: string;\n properties: { property: string; type: string }[];\n};\n\nexport type RelType = {\n type: string;\n properties: { property: string; type: string }[];\n};\n\nexport type PathType = { start: string; type: string; end: string };\n\nexport const BASE_ENTITY_LABEL = \"__Entity__\";\n\nconst DISTINCT_VALUE_LIMIT = 10;\nconst LIST_LIMIT = 128;\nconst EXHAUSTIVE_SEARCH_LIMIT = 10000;\nconst EXCLUDED_LABELS = [\"Bloom_Perspective\", \"Bloom_Scene\"];\nconst EXCLUDED_RELS = [\"Bloom_HAS_SCENE\"];\n\nconst INCLUDE_DOCS_QUERY = `\nMERGE (d:Document {id:$document.metadata.id})\nSET d.text = $document.pageContent\nSET d += $document.metadata\nWITH d\n`;\n\nconst NODE_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"node\"\n AND NOT label IN $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {labels: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"relationship\"\n AND NOT label in $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {type: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE type = \"RELATIONSHIP\" AND elementType = \"node\"\nUNWIND other AS other_node\nWITH * WHERE NOT label IN $EXCLUDED_LABELS\n AND NOT other_node IN $EXCLUDED_LABELS\nRETURN {start: label, type: property, end: toString(other_node)} AS output\n`;\n\nfunction isDistinctMoreThanLimit(\n distinct_count = 11,\n limit = DISTINCT_VALUE_LIMIT\n): boolean {\n return distinct_count !== undefined && distinct_count > limit;\n}\n\nfunction cleanStringValues(text: string) {\n return text.replace(/\\n/g, \" \").replace(/\\r/g, \" \");\n}\n\nexport function formatSchema(\n schema: Record<string, Any>,\n isEnhanced: boolean\n): string {\n let formattedNodeProps: string[] = [];\n let formattedRelProps: string[] = [];\n\n if (isEnhanced) {\n for (const [nodeType, properties] of Object.entries(schema.nodeProps)) {\n formattedNodeProps.push(`- **${nodeType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min !== undefined) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values.length > 0) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (!prop.min_size || prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedNodeProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n\n for (const [relType, properties] of Object.entries(schema.relProps)) {\n formattedRelProps.push(`- **${relType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedRelProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n } else {\n formattedNodeProps = Object.entries(schema.nodeProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type}`)\n .join(\", \");\n return `${key} {${propsStr}}`;\n }\n );\n\n formattedRelProps = Object.entries(schema.relProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type} `)\n .join(\", \");\n return `${key} {${propsStr} } `;\n }\n );\n }\n\n const formattedRels = schema.relationships.map(\n (el: Record<string, string>) =>\n `(: ${el.start}) - [: ${el.type}] -> (:${el.end})`\n );\n\n return [\n \"Node properties are the following:\",\n formattedNodeProps?.join(\", \"),\n \"Relationship properties are the following:\",\n formattedRelProps?.join(\", \"),\n \"The relationships are the following:\",\n formattedRels?.join(\", \"),\n ].join(\"\\n\");\n}\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jGraph {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private timeoutMs: number | undefined;\n\n private enhancedSchema: boolean;\n\n protected schema = \"\";\n\n protected structuredSchema: StructuredSchema = {\n nodeProps: {},\n relProps: {},\n relationships: [],\n metadata: {\n constraint: {},\n index: {},\n },\n };\n\n constructor({\n url,\n username,\n password,\n database = \"neo4j\",\n timeoutMs,\n enhancedSchema = false,\n }: Neo4jGraphConfig) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n this.timeoutMs = timeoutMs;\n this.enhancedSchema = enhancedSchema;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n static async initialize(config: Neo4jGraphConfig): Promise<Neo4jGraph> {\n const graph = new Neo4jGraph(config);\n\n await graph.verifyConnectivity();\n\n try {\n await graph.refreshSchema();\n } catch (error: Any) {\n if (error.code === \"Neo.ClientError.Procedure.ProcedureNotFound\") {\n throw new Error(\n \"Could not use APOC procedures. Please ensure the APOC plugin is installed in Neo4j and that 'apoc.meta.data()' is allowed in Neo4j configuration.\"\n );\n }\n\n throw error;\n } finally {\n console.log(\"Schema refreshed successfully.\");\n }\n\n return graph;\n }\n\n getSchema(): string {\n return this.schema;\n }\n\n getStructuredSchema() {\n return this.structuredSchema;\n }\n\n async query<RecordShape extends Record<string, Any> = Record<string, Any>>(\n query: string,\n params: Record<string, Any> = {},\n routing: RoutingControl = neo4j.routing.WRITE\n ): Promise<RecordShape[]> {\n const result = await this.driver.executeQuery<RecordShape>(query, params, {\n database: this.database,\n routing,\n transactionConfig: { timeout: this.timeoutMs },\n });\n return toObjects<RecordShape>(result.records);\n }\n\n async verifyConnectivity() {\n await this.driver.getServerInfo();\n }\n\n async refreshSchema() {\n const nodeProperties = (\n await this.query<{ output: NodeType }>(NODE_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const relationshipsProperties = (\n await this.query<{ output: RelType }>(REL_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_RELS,\n })\n )?.map((el) => el.output);\n\n const relationships: PathType[] = (\n await this.query<{ output: PathType }>(REL_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const constraint = await this.query(\"SHOW CONSTRAINTS\");\n\n const index = await this.query(\"SHOW INDEXES YIELD *\");\n\n this.structuredSchema = {\n nodeProps: Object.fromEntries(\n nodeProperties?.map((el) => [el.labels, el.properties]) || []\n ),\n relProps: Object.fromEntries(\n relationshipsProperties?.map((el) => [el.type, el.properties]) || []\n ),\n relationships: relationships || [],\n metadata: {\n constraint,\n index,\n },\n };\n\n if (this.enhancedSchema) {\n const schemaCounts = await this.query(\n `CALL apoc.meta.graphSample() YIELD nodes, relationships ` +\n `RETURN nodes, [rel in relationships | {name: apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships`\n );\n for (const node of schemaCounts[0].nodes) {\n if (EXCLUDED_LABELS.includes(node.name)) {\n continue;\n }\n\n const nodeProps = this.structuredSchema.nodeProps[node.name];\n\n if (!nodeProps) {\n continue;\n }\n\n const enhancedCypher = await this.enhancedSchemaCypher(\n node.name,\n nodeProps,\n node.count < EXHAUSTIVE_SEARCH_LIMIT\n );\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of nodeProps) {\n if (enhancedInfo[prop.property]) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n\n for (const rel of schemaCounts[0].relationships) {\n if (EXCLUDED_RELS.includes(rel.name)) {\n continue;\n }\n const relProps = this.structuredSchema.relProps[rel.name];\n\n if (!relProps) {\n continue;\n }\n const enhancedCypher = await this.enhancedSchemaCypher(\n rel.name,\n relProps,\n rel.count < EXHAUSTIVE_SEARCH_LIMIT,\n true\n );\n\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of relProps) {\n if (prop.property in enhancedInfo) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n }\n\n this.schema = formatSchema(this.structuredSchema, this.enhancedSchema);\n }\n\n async enhancedSchemaCypher(\n labelOrType: string,\n properties: { property: string; type: string }[],\n exhaustive: boolean,\n isRelationship = false\n ) {\n let matchClause = isRelationship\n ? `MATCH ()-[n:\\`${labelOrType}\\`]->()`\n : `MATCH (n:\\`${labelOrType}\\`)`;\n\n const withClauses: string[] = [];\n const returnClauses: string[] = [];\n const outputDict: { [key: string]: string } = {};\n\n if (exhaustive) {\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n if (propType === \"STRING\") {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(\n `values: \\`${propName}_values\\`[..${DISTINCT_VALUE_LIMIT}], distinct_count: size(\\`${propName}_values\\`)`\n );\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n } else {\n matchClause += ` WITH n LIMIT 5`;\n\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n const propIndex = this.structuredSchema?.metadata?.index.filter(\n (el: Any) =>\n el.label === labelOrType &&\n el.properties[0] === propName &&\n el.type === \"RANGE\"\n );\n\n if (propType === \"STRING\") {\n if (\n propIndex.length > 0 &&\n propIndex[0].size > 0 &&\n propIndex[0].distinctValues <= DISTINCT_VALUE_LIMIT\n ) {\n const distinctValuesPromise = await this.query(\n `CALL apoc.schema.properties.distinct('${labelOrType}', '${propName}') YIELD value`\n );\n const distinctValues = distinctValuesPromise[0].value;\n returnClauses.push(\n `values: ${distinctValues}, distinct_count: ${distinctValues.length}`\n );\n } else {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n if (!propIndex) {\n withClauses.push(\n `collect(distinct toString(n.\\`${propName}\\`)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n } else {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n }\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n }\n\n const withClause = `WITH ${withClauses.join(\", \")}`;\n const returnClause = `RETURN {${Object.entries(outputDict)\n .map(([k, v]) => `\\`${k}\\`: ${v}`)\n .join(\", \")}} AS output`;\n\n const cypherQuery = [matchClause, withClause, returnClause].join(\"\\n\");\n\n return cypherQuery;\n }\n\n async addGraphDocuments(\n graphDocuments: GraphDocument[],\n config: AddGraphDocumentsConfig = {}\n ): Promise<void> {\n const { baseEntityLabel } = config;\n\n if (baseEntityLabel) {\n const constraintExists =\n this.structuredSchema?.metadata?.constraint?.some(\n (el: Any) =>\n JSON.stringify(el.labelsOrTypes) ===\n JSON.stringify([BASE_ENTITY_LABEL]) &&\n JSON.stringify(el.properties) === JSON.stringify([\"id\"])\n ) ?? false;\n\n if (!constraintExists) {\n await this.query(`\n CREATE CONSTRAINT IF NOT EXISTS FOR (b:${BASE_ENTITY_LABEL})\n REQUIRE b.id IS UNIQUE;\n `);\n await this.refreshSchema();\n }\n }\n\n const nodeImportQuery = getNodeImportQuery(config);\n const relImportQuery = getRelImportQuery(config);\n\n for (const document of graphDocuments) {\n if (!document.source.metadata.id) {\n document.source.metadata.id = sha256(document.source.pageContent);\n }\n\n await this.query(nodeImportQuery, {\n data: document.nodes.map((el: Any) => ({ ...el })),\n document: { ...document.source },\n });\n\n await this.query(relImportQuery, {\n data: document.relationships.map((el: Any) => ({\n source: el.source.id,\n source_label: el.source.type,\n target: el.target.id,\n target_label: el.target.type,\n type: el.type.replace(/ /g, \"_\").toUpperCase(),\n properties: el.properties,\n })),\n });\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n\nfunction getNodeImportQuery({\n baseEntityLabel,\n includeSource,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n MERGE(source: \\`${BASE_ENTITY_LABEL}\\` {id: row.id})\n SET source += row.properties\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(source)\" : \"\"}\n WITH source, row\n CALL apoc.create.addLabels(source, [row.type]) YIELD node\n RETURN distinct 'done' AS result\n `;\n } else {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n CALL apoc.merge.node([row.type], {id: row.id},\n row.properties, {}) YIELD node\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(node)\" : \"\"}\n RETURN distinct 'done' AS result\n `;\n }\n}\n\nfunction getRelImportQuery({\n baseEntityLabel,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n UNWIND $data AS row\n MERGE (source:\\`${BASE_ENTITY_LABEL}\\` {id: row.source})\n MERGE (target:\\`${BASE_ENTITY_LABEL}\\` {id: row.target})\n WITH source, target, row\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n } else {\n return `\n UNWIND $data AS row\n CALL apoc.merge.node([row.source_label], {id: row.source},\n {}, {}) YIELD node as source\n CALL apoc.merge.node([row.target_label], {id: row.target},\n {}, {}) YIELD node as target\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n }\n}\n\nfunction toObjects<\n RecordShape extends Record<string, Any> = Record<string, Any>,\n>(records: Neo4jRecord<RecordShape>): RecordShape[] {\n return records.map((record: Any) => {\n const rObj = record.toObject();\n const out: Partial<RecordShape> = {};\n Object.keys(rObj).forEach((key: keyof RecordShape) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out as RecordShape;\n });\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nconst extractPathForRows = (path: Neo4jPath) => {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j_graph.js","names":[],"sources":["../../src/graphs/neo4j_graph.ts"],"sourcesContent":["import neo4j, {\n RoutingControl,\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport { sha256 } from \"@langchain/core/utils/hash\";\nimport { GraphDocument } from \"./document.js\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport interface Neo4jGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n timeoutMs?: number;\n enhancedSchema?: boolean;\n}\n\nexport interface StructuredSchema {\n nodeProps: { [key: NodeType[\"labels\"]]: NodeType[\"properties\"] };\n relProps: { [key: RelType[\"type\"]]: RelType[\"properties\"] };\n relationships: PathType[];\n metadata?: {\n constraint: Record<string, Any>;\n index: Record<string, Any>;\n };\n}\n\nexport interface AddGraphDocumentsConfig {\n baseEntityLabel?: boolean;\n includeSource?: boolean;\n}\n\nexport type NodeType = {\n labels: string;\n properties: { property: string; type: string }[];\n};\n\nexport type RelType = {\n type: string;\n properties: { property: string; type: string }[];\n};\n\nexport type PathType = { start: string; type: string; end: string };\n\nexport const BASE_ENTITY_LABEL = \"__Entity__\";\n\nconst DISTINCT_VALUE_LIMIT = 10;\nconst LIST_LIMIT = 128;\nconst EXHAUSTIVE_SEARCH_LIMIT = 10000;\nconst EXCLUDED_LABELS = [\"Bloom_Perspective\", \"Bloom_Scene\"];\nconst EXCLUDED_RELS = [\"Bloom_HAS_SCENE\"];\n\nconst INCLUDE_DOCS_QUERY = `\nMERGE (d:Document {id:$document.metadata.id})\nSET d.text = $document.pageContent\nSET d += $document.metadata\nWITH d\n`;\n\nconst NODE_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"node\"\n AND NOT label IN $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {labels: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"relationship\"\n AND NOT label in $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {type: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE type = \"RELATIONSHIP\" AND elementType = \"node\"\nUNWIND other AS other_node\nWITH * WHERE NOT label IN $EXCLUDED_LABELS\n AND NOT other_node IN $EXCLUDED_LABELS\nRETURN {start: label, type: property, end: toString(other_node)} AS output\n`;\n\nfunction isDistinctMoreThanLimit(\n distinct_count = 11,\n limit = DISTINCT_VALUE_LIMIT\n): boolean {\n return distinct_count !== undefined && distinct_count > limit;\n}\n\nfunction cleanStringValues(text: string) {\n return text.replace(/\\n/g, \" \").replace(/\\r/g, \" \");\n}\n\nexport function formatSchema(\n schema: Record<string, Any>,\n isEnhanced: boolean\n): string {\n let formattedNodeProps: string[] = [];\n let formattedRelProps: string[] = [];\n\n if (isEnhanced) {\n for (const [nodeType, properties] of Object.entries(schema.nodeProps)) {\n formattedNodeProps.push(`- **${nodeType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min !== undefined) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values.length > 0) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (!prop.min_size || prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedNodeProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n\n for (const [relType, properties] of Object.entries(schema.relProps)) {\n formattedRelProps.push(`- **${relType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedRelProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n } else {\n formattedNodeProps = Object.entries(schema.nodeProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type}`)\n .join(\", \");\n return `${key} {${propsStr}}`;\n }\n );\n\n formattedRelProps = Object.entries(schema.relProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type} `)\n .join(\", \");\n return `${key} {${propsStr} } `;\n }\n );\n }\n\n const formattedRels = schema.relationships.map(\n (el: Record<string, string>) =>\n `(: ${el.start}) - [: ${el.type}] -> (:${el.end})`\n );\n\n return [\n \"Node properties are the following:\",\n formattedNodeProps?.join(\", \"),\n \"Relationship properties are the following:\",\n formattedRelProps?.join(\", \"),\n \"The relationships are the following:\",\n formattedRels?.join(\", \"),\n ].join(\"\\n\");\n}\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jGraph {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private timeoutMs: number | undefined;\n\n private enhancedSchema: boolean;\n\n protected schema = \"\";\n\n protected structuredSchema: StructuredSchema = {\n nodeProps: {},\n relProps: {},\n relationships: [],\n metadata: {\n constraint: {},\n index: {},\n },\n };\n\n constructor({\n url,\n username,\n password,\n database = \"neo4j\",\n timeoutMs,\n enhancedSchema = false,\n }: Neo4jGraphConfig) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n this.timeoutMs = timeoutMs;\n this.enhancedSchema = enhancedSchema;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n static async initialize(config: Neo4jGraphConfig): Promise<Neo4jGraph> {\n const graph = new Neo4jGraph(config);\n\n await graph.verifyConnectivity();\n\n try {\n await graph.refreshSchema();\n } catch (error: Any) {\n if (error.code === \"Neo.ClientError.Procedure.ProcedureNotFound\") {\n throw new Error(\n \"Could not use APOC procedures. Please ensure the APOC plugin is installed in Neo4j and that 'apoc.meta.data()' is allowed in Neo4j configuration.\"\n );\n }\n\n throw error;\n } finally {\n console.log(\"Schema refreshed successfully.\");\n }\n\n return graph;\n }\n\n getSchema(): string {\n return this.schema;\n }\n\n getStructuredSchema() {\n return this.structuredSchema;\n }\n\n async query<RecordShape extends Record<string, Any> = Record<string, Any>>(\n query: string,\n params: Record<string, Any> = {},\n routing: RoutingControl = neo4j.routing.WRITE\n ): Promise<RecordShape[]> {\n const result = await this.driver.executeQuery<RecordShape>(query, params, {\n database: this.database,\n routing,\n transactionConfig: { timeout: this.timeoutMs },\n });\n return toObjects<RecordShape>(result.records);\n }\n\n async verifyConnectivity() {\n await this.driver.getServerInfo();\n }\n\n async refreshSchema() {\n const nodeProperties = (\n await this.query<{ output: NodeType }>(NODE_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const relationshipsProperties = (\n await this.query<{ output: RelType }>(REL_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_RELS,\n })\n )?.map((el) => el.output);\n\n const relationships: PathType[] = (\n await this.query<{ output: PathType }>(REL_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const constraint = await this.query(\"SHOW CONSTRAINTS\");\n\n const index = await this.query(\"SHOW INDEXES YIELD *\");\n\n this.structuredSchema = {\n nodeProps: Object.fromEntries(\n nodeProperties?.map((el) => [el.labels, el.properties]) || []\n ),\n relProps: Object.fromEntries(\n relationshipsProperties?.map((el) => [el.type, el.properties]) || []\n ),\n relationships: relationships || [],\n metadata: {\n constraint,\n index,\n },\n };\n\n if (this.enhancedSchema) {\n const schemaCounts = await this.query(\n `CALL apoc.meta.graphSample() YIELD nodes, relationships ` +\n `RETURN nodes, [rel in relationships | {name: apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships`\n );\n for (const node of schemaCounts[0].nodes) {\n if (EXCLUDED_LABELS.includes(node.name)) {\n continue;\n }\n\n const nodeProps = this.structuredSchema.nodeProps[node.name];\n\n if (!nodeProps) {\n continue;\n }\n\n const enhancedCypher = await this.enhancedSchemaCypher(\n node.name,\n nodeProps,\n node.count < EXHAUSTIVE_SEARCH_LIMIT\n );\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of nodeProps) {\n if (enhancedInfo[prop.property]) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n\n for (const rel of schemaCounts[0].relationships) {\n if (EXCLUDED_RELS.includes(rel.name)) {\n continue;\n }\n const relProps = this.structuredSchema.relProps[rel.name];\n\n if (!relProps) {\n continue;\n }\n const enhancedCypher = await this.enhancedSchemaCypher(\n rel.name,\n relProps,\n rel.count < EXHAUSTIVE_SEARCH_LIMIT,\n true\n );\n\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of relProps) {\n if (prop.property in enhancedInfo) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n }\n\n this.schema = formatSchema(this.structuredSchema, this.enhancedSchema);\n }\n\n async enhancedSchemaCypher(\n labelOrType: string,\n properties: { property: string; type: string }[],\n exhaustive: boolean,\n isRelationship = false\n ) {\n let matchClause = isRelationship\n ? `MATCH ()-[n:\\`${labelOrType}\\`]->()`\n : `MATCH (n:\\`${labelOrType}\\`)`;\n\n const withClauses: string[] = [];\n const returnClauses: string[] = [];\n const outputDict: { [key: string]: string } = {};\n\n if (exhaustive) {\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n if (propType === \"STRING\") {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(\n `values: \\`${propName}_values\\`[..${DISTINCT_VALUE_LIMIT}], distinct_count: size(\\`${propName}_values\\`)`\n );\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n } else {\n matchClause += ` WITH n LIMIT 5`;\n\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n const propIndex = this.structuredSchema?.metadata?.index.filter(\n (el: Any) =>\n el.label === labelOrType &&\n el.properties[0] === propName &&\n el.type === \"RANGE\"\n );\n\n if (propType === \"STRING\") {\n if (\n propIndex.length > 0 &&\n propIndex[0].size > 0 &&\n propIndex[0].distinctValues <= DISTINCT_VALUE_LIMIT\n ) {\n const distinctValuesPromise = await this.query(\n `CALL apoc.schema.properties.distinct('${labelOrType}', '${propName}') YIELD value`\n );\n const distinctValues = distinctValuesPromise[0].value;\n returnClauses.push(\n `values: ${distinctValues}, distinct_count: ${distinctValues.length}`\n );\n } else {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n if (!propIndex) {\n withClauses.push(\n `collect(distinct toString(n.\\`${propName}\\`)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n } else {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n }\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n }\n\n const withClause = `WITH ${withClauses.join(\", \")}`;\n const returnClause = `RETURN {${Object.entries(outputDict)\n .map(([k, v]) => `\\`${k}\\`: ${v}`)\n .join(\", \")}} AS output`;\n\n const cypherQuery = [matchClause, withClause, returnClause].join(\"\\n\");\n\n return cypherQuery;\n }\n\n async addGraphDocuments(\n graphDocuments: GraphDocument[],\n config: AddGraphDocumentsConfig = {}\n ): Promise<void> {\n const { baseEntityLabel } = config;\n\n if (baseEntityLabel) {\n const constraintExists =\n this.structuredSchema?.metadata?.constraint?.some(\n (el: Any) =>\n JSON.stringify(el.labelsOrTypes) ===\n JSON.stringify([BASE_ENTITY_LABEL]) &&\n JSON.stringify(el.properties) === JSON.stringify([\"id\"])\n ) ?? false;\n\n if (!constraintExists) {\n await this.query(`\n CREATE CONSTRAINT IF NOT EXISTS FOR (b:${BASE_ENTITY_LABEL})\n REQUIRE b.id IS UNIQUE;\n `);\n await this.refreshSchema();\n }\n }\n\n const nodeImportQuery = getNodeImportQuery(config);\n const relImportQuery = getRelImportQuery(config);\n\n for (const document of graphDocuments) {\n if (!document.source.metadata.id) {\n document.source.metadata.id = sha256(document.source.pageContent);\n }\n\n await this.query(nodeImportQuery, {\n data: document.nodes.map((el: Any) => ({ ...el })),\n document: { ...document.source },\n });\n\n await this.query(relImportQuery, {\n data: document.relationships.map((el: Any) => ({\n source: el.source.id,\n source_label: el.source.type,\n target: el.target.id,\n target_label: el.target.type,\n type: el.type.replace(/ /g, \"_\").toUpperCase(),\n properties: el.properties,\n })),\n });\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n\nfunction getNodeImportQuery({\n baseEntityLabel,\n includeSource,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n MERGE(source: \\`${BASE_ENTITY_LABEL}\\` {id: row.id})\n SET source += row.properties\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(source)\" : \"\"}\n WITH source, row\n CALL apoc.create.addLabels(source, [row.type]) YIELD node\n RETURN distinct 'done' AS result\n `;\n } else {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n CALL apoc.merge.node([row.type], {id: row.id},\n row.properties, {}) YIELD node\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(node)\" : \"\"}\n RETURN distinct 'done' AS result\n `;\n }\n}\n\nfunction getRelImportQuery({\n baseEntityLabel,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n UNWIND $data AS row\n MERGE (source:\\`${BASE_ENTITY_LABEL}\\` {id: row.source})\n MERGE (target:\\`${BASE_ENTITY_LABEL}\\` {id: row.target})\n WITH source, target, row\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n } else {\n return `\n UNWIND $data AS row\n CALL apoc.merge.node([row.source_label], {id: row.source},\n {}, {}) YIELD node as source\n CALL apoc.merge.node([row.target_label], {id: row.target},\n {}, {}) YIELD node as target\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n }\n}\n\nfunction toObjects<\n RecordShape extends Record<string, Any> = Record<string, Any>,\n>(records: Neo4jRecord<RecordShape>): RecordShape[] {\n return records.map((record: Any) => {\n const rObj = record.toObject();\n const out: Partial<RecordShape> = {};\n Object.keys(rObj).forEach((key: keyof RecordShape) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out as RecordShape;\n });\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nconst extractPathForRows = (path: Neo4jPath) => {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"neo4j_graph.js","names":[],"sources":["../../src/graphs/neo4j_graph.ts"],"sourcesContent":["import neo4j, {\n RoutingControl,\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport { sha256 } from \"@langchain/core/utils/hash\";\nimport { GraphDocument } from \"./document.js\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport interface Neo4jGraphConfig {\n url: string;\n username: string;\n password: string;\n database?: string;\n timeoutMs?: number;\n enhancedSchema?: boolean;\n}\n\nexport interface StructuredSchema {\n nodeProps: { [key: NodeType[\"labels\"]]: NodeType[\"properties\"] };\n relProps: { [key: RelType[\"type\"]]: RelType[\"properties\"] };\n relationships: PathType[];\n metadata?: {\n constraint: Record<string, Any>;\n index: Record<string, Any>;\n };\n}\n\nexport interface AddGraphDocumentsConfig {\n baseEntityLabel?: boolean;\n includeSource?: boolean;\n}\n\nexport type NodeType = {\n labels: string;\n properties: { property: string; type: string }[];\n};\n\nexport type RelType = {\n type: string;\n properties: { property: string; type: string }[];\n};\n\nexport type PathType = { start: string; type: string; end: string };\n\nexport const BASE_ENTITY_LABEL = \"__Entity__\";\n\nconst DISTINCT_VALUE_LIMIT = 10;\nconst LIST_LIMIT = 128;\nconst EXHAUSTIVE_SEARCH_LIMIT = 10000;\nconst EXCLUDED_LABELS = [\"Bloom_Perspective\", \"Bloom_Scene\"];\nconst EXCLUDED_RELS = [\"Bloom_HAS_SCENE\"];\n\nconst INCLUDE_DOCS_QUERY = `\nMERGE (d:Document {id:$document.metadata.id})\nSET d.text = $document.pageContent\nSET d += $document.metadata\nWITH d\n`;\n\nconst NODE_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"node\"\n AND NOT label IN $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {labels: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_PROPERTIES_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE NOT type = \"RELATIONSHIP\" AND elementType = \"relationship\"\n AND NOT label in $EXCLUDED_LABELS\nWITH label AS nodeLabels, collect({property:property, type:type}) AS properties\nRETURN {type: nodeLabels, properties: properties} AS output\n`;\n\nconst REL_QUERY = `\nCALL apoc.meta.data()\nYIELD label, other, elementType, type, property\nWHERE type = \"RELATIONSHIP\" AND elementType = \"node\"\nUNWIND other AS other_node\nWITH * WHERE NOT label IN $EXCLUDED_LABELS\n AND NOT other_node IN $EXCLUDED_LABELS\nRETURN {start: label, type: property, end: toString(other_node)} AS output\n`;\n\nfunction isDistinctMoreThanLimit(\n distinct_count = 11,\n limit = DISTINCT_VALUE_LIMIT\n): boolean {\n return distinct_count !== undefined && distinct_count > limit;\n}\n\nfunction cleanStringValues(text: string) {\n return text.replace(/\\n/g, \" \").replace(/\\r/g, \" \");\n}\n\nexport function formatSchema(\n schema: Record<string, Any>,\n isEnhanced: boolean\n): string {\n let formattedNodeProps: string[] = [];\n let formattedRelProps: string[] = [];\n\n if (isEnhanced) {\n for (const [nodeType, properties] of Object.entries(schema.nodeProps)) {\n formattedNodeProps.push(`- **${nodeType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min !== undefined) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values.length > 0) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (!prop.min_size || prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedNodeProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n\n for (const [relType, properties] of Object.entries(schema.relProps)) {\n formattedRelProps.push(`- **${relType}**`);\n\n for (const prop of properties as Array<Record<string, Any>>) {\n let example = \"\";\n\n if (prop.type === \"STRING\") {\n if (prop.values.length > 0) {\n if (isDistinctMoreThanLimit(prop.distinct_count)) {\n example = `Example: ${cleanStringValues(prop.values[0])}`;\n } else {\n example = `Available options: ${prop.values\n .map(cleanStringValues)\n .join(\", \")}`;\n }\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n prop.type\n )\n ) {\n if (prop.min) {\n example = `Min: ${prop.min}, Max: ${prop.max}`;\n } else {\n if (prop.values) {\n example = `Example: ${prop.values[0]}`;\n }\n }\n } else if (prop.type === \"LIST\") {\n if (prop.min_size > LIST_LIMIT) {\n continue;\n }\n example = `Min Size: ${prop.min_size}, Max Size: ${prop.max_size}`;\n }\n\n formattedRelProps.push(\n ` - \\`${prop.property}\\`: ${prop.type} ${example}`\n );\n }\n }\n } else {\n formattedNodeProps = Object.entries(schema.nodeProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type}`)\n .join(\", \");\n return `${key} {${propsStr}}`;\n }\n );\n\n formattedRelProps = Object.entries(schema.relProps).map(\n ([key, value]: [string, Any]) => {\n const propsStr = value\n .map((prop: Record<string, Any>) => `${prop.property}: ${prop.type} `)\n .join(\", \");\n return `${key} {${propsStr} } `;\n }\n );\n }\n\n const formattedRels = schema.relationships.map(\n (el: Record<string, string>) =>\n `(: ${el.start}) - [: ${el.type}] -> (:${el.end})`\n );\n\n return [\n \"Node properties are the following:\",\n formattedNodeProps?.join(\", \"),\n \"Relationship properties are the following:\",\n formattedRelProps?.join(\", \"),\n \"The relationships are the following:\",\n formattedRels?.join(\", \"),\n ].join(\"\\n\");\n}\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jGraph {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private timeoutMs: number | undefined;\n\n private enhancedSchema: boolean;\n\n protected schema = \"\";\n\n protected structuredSchema: StructuredSchema = {\n nodeProps: {},\n relProps: {},\n relationships: [],\n metadata: {\n constraint: {},\n index: {},\n },\n };\n\n constructor({\n url,\n username,\n password,\n database = \"neo4j\",\n timeoutMs,\n enhancedSchema = false,\n }: Neo4jGraphConfig) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n this.timeoutMs = timeoutMs;\n this.enhancedSchema = enhancedSchema;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n static async initialize(config: Neo4jGraphConfig): Promise<Neo4jGraph> {\n const graph = new Neo4jGraph(config);\n\n await graph.verifyConnectivity();\n\n try {\n await graph.refreshSchema();\n } catch (error: Any) {\n if (error.code === \"Neo.ClientError.Procedure.ProcedureNotFound\") {\n throw new Error(\n \"Could not use APOC procedures. Please ensure the APOC plugin is installed in Neo4j and that 'apoc.meta.data()' is allowed in Neo4j configuration.\"\n );\n }\n\n throw error;\n } finally {\n console.log(\"Schema refreshed successfully.\");\n }\n\n return graph;\n }\n\n getSchema(): string {\n return this.schema;\n }\n\n getStructuredSchema() {\n return this.structuredSchema;\n }\n\n async query<RecordShape extends Record<string, Any> = Record<string, Any>>(\n query: string,\n params: Record<string, Any> = {},\n routing: RoutingControl = neo4j.routing.WRITE\n ): Promise<RecordShape[]> {\n const result = await this.driver.executeQuery<RecordShape>(query, params, {\n database: this.database,\n routing,\n transactionConfig: { timeout: this.timeoutMs },\n });\n return toObjects<RecordShape>(result.records);\n }\n\n async verifyConnectivity() {\n await this.driver.getServerInfo();\n }\n\n async refreshSchema() {\n const nodeProperties = (\n await this.query<{ output: NodeType }>(NODE_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const relationshipsProperties = (\n await this.query<{ output: RelType }>(REL_PROPERTIES_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_RELS,\n })\n )?.map((el) => el.output);\n\n const relationships: PathType[] = (\n await this.query<{ output: PathType }>(REL_QUERY, {\n EXCLUDED_LABELS: EXCLUDED_LABELS.concat([BASE_ENTITY_LABEL]),\n })\n )?.map((el) => el.output);\n\n const constraint = await this.query(\"SHOW CONSTRAINTS\");\n\n const index = await this.query(\"SHOW INDEXES YIELD *\");\n\n this.structuredSchema = {\n nodeProps: Object.fromEntries(\n nodeProperties?.map((el) => [el.labels, el.properties]) || []\n ),\n relProps: Object.fromEntries(\n relationshipsProperties?.map((el) => [el.type, el.properties]) || []\n ),\n relationships: relationships || [],\n metadata: {\n constraint,\n index,\n },\n };\n\n if (this.enhancedSchema) {\n const schemaCounts = await this.query(\n `CALL apoc.meta.graphSample() YIELD nodes, relationships ` +\n `RETURN nodes, [rel in relationships | {name: apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships`\n );\n for (const node of schemaCounts[0].nodes) {\n if (EXCLUDED_LABELS.includes(node.name)) {\n continue;\n }\n\n const nodeProps = this.structuredSchema.nodeProps[node.name];\n\n if (!nodeProps) {\n continue;\n }\n\n const enhancedCypher = await this.enhancedSchemaCypher(\n node.name,\n nodeProps,\n node.count < EXHAUSTIVE_SEARCH_LIMIT\n );\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of nodeProps) {\n if (enhancedInfo[prop.property]) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n\n for (const rel of schemaCounts[0].relationships) {\n if (EXCLUDED_RELS.includes(rel.name)) {\n continue;\n }\n const relProps = this.structuredSchema.relProps[rel.name];\n\n if (!relProps) {\n continue;\n }\n const enhancedCypher = await this.enhancedSchemaCypher(\n rel.name,\n relProps,\n rel.count < EXHAUSTIVE_SEARCH_LIMIT,\n true\n );\n\n const enhancedInfoPromise = await this.query(enhancedCypher);\n const enhancedInfo = enhancedInfoPromise[0].output;\n\n for (const prop of relProps) {\n if (prop.property in enhancedInfo) {\n Object.assign(prop, enhancedInfo[prop.property]);\n }\n }\n }\n }\n\n this.schema = formatSchema(this.structuredSchema, this.enhancedSchema);\n }\n\n async enhancedSchemaCypher(\n labelOrType: string,\n properties: { property: string; type: string }[],\n exhaustive: boolean,\n isRelationship = false\n ) {\n let matchClause = isRelationship\n ? `MATCH ()-[n:\\`${labelOrType}\\`]->()`\n : `MATCH (n:\\`${labelOrType}\\`)`;\n\n const withClauses: string[] = [];\n const returnClauses: string[] = [];\n const outputDict: { [key: string]: string } = {};\n\n if (exhaustive) {\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n if (propType === \"STRING\") {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(\n `values: \\`${propName}_values\\`[..${DISTINCT_VALUE_LIMIT}], distinct_count: size(\\`${propName}_values\\`)`\n );\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n } else {\n matchClause += ` WITH n LIMIT 5`;\n\n for (const prop of properties) {\n const propName = prop.property;\n const propType = prop.type;\n\n const propIndex = this.structuredSchema?.metadata?.index.filter(\n (el: Any) =>\n el.label === labelOrType &&\n el.properties[0] === propName &&\n el.type === \"RANGE\"\n );\n\n if (propType === \"STRING\") {\n if (\n propIndex.length > 0 &&\n propIndex[0].size > 0 &&\n propIndex[0].distinctValues <= DISTINCT_VALUE_LIMIT\n ) {\n const distinctValuesPromise = await this.query(\n `CALL apoc.schema.properties.distinct('${labelOrType}', '${propName}') YIELD value`\n );\n const distinctValues = distinctValuesPromise[0].value;\n returnClauses.push(\n `values: ${distinctValues}, distinct_count: ${distinctValues.length}`\n );\n } else {\n withClauses.push(\n `collect(distinct substring(n.\\`${propName}\\`, 0, 50)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n }\n } else if (\n [\"INTEGER\", \"FLOAT\", \"DATE\", \"DATE_TIME\", \"LOCAL_DATE_TIME\"].includes(\n propType\n )\n ) {\n if (!propIndex) {\n withClauses.push(\n `collect(distinct toString(n.\\`${propName}\\`)) AS \\`${propName}_values\\``\n );\n returnClauses.push(`values: ${propName}_values`);\n } else {\n withClauses.push(`min(n.\\`${propName}\\`) AS \\`${propName}_min\\``);\n withClauses.push(`max(n.\\`${propName}\\`) AS \\`${propName}_max\\``);\n withClauses.push(\n `count(distinct n.\\`${propName}\\`) AS \\`${propName}_distinct\\``\n );\n returnClauses.push(\n `min: toString(\\`${propName}_min\\`), max: toString(\\`${propName}_max\\`), distinct_count: \\`${propName}_distinct\\``\n );\n }\n } else if (propType === \"LIST\") {\n withClauses.push(\n `min(size(n.\\`${propName}\\`)) AS \\`${propName}_size_min\\`, max(size(n.\\`${propName}\\`)) AS \\`${propName}_size_max\\``\n );\n returnClauses.push(\n `min_size: \\`${propName}_size_min\\`, max_size: \\`${propName}_size_max\\``\n );\n } else if ([\"BOOLEAN\", \"POINT\", \"DURATION\"].includes(propType)) {\n continue;\n }\n\n outputDict[propName] = `{${returnClauses.pop()}}`;\n }\n }\n\n const withClause = `WITH ${withClauses.join(\", \")}`;\n const returnClause = `RETURN {${Object.entries(outputDict)\n .map(([k, v]) => `\\`${k}\\`: ${v}`)\n .join(\", \")}} AS output`;\n\n const cypherQuery = [matchClause, withClause, returnClause].join(\"\\n\");\n\n return cypherQuery;\n }\n\n async addGraphDocuments(\n graphDocuments: GraphDocument[],\n config: AddGraphDocumentsConfig = {}\n ): Promise<void> {\n const { baseEntityLabel } = config;\n\n if (baseEntityLabel) {\n const constraintExists =\n this.structuredSchema?.metadata?.constraint?.some(\n (el: Any) =>\n JSON.stringify(el.labelsOrTypes) ===\n JSON.stringify([BASE_ENTITY_LABEL]) &&\n JSON.stringify(el.properties) === JSON.stringify([\"id\"])\n ) ?? false;\n\n if (!constraintExists) {\n await this.query(`\n CREATE CONSTRAINT IF NOT EXISTS FOR (b:${BASE_ENTITY_LABEL})\n REQUIRE b.id IS UNIQUE;\n `);\n await this.refreshSchema();\n }\n }\n\n const nodeImportQuery = getNodeImportQuery(config);\n const relImportQuery = getRelImportQuery(config);\n\n for (const document of graphDocuments) {\n if (!document.source.metadata.id) {\n document.source.metadata.id = sha256(document.source.pageContent);\n }\n\n await this.query(nodeImportQuery, {\n data: document.nodes.map((el: Any) => ({ ...el })),\n document: { ...document.source },\n });\n\n await this.query(relImportQuery, {\n data: document.relationships.map((el: Any) => ({\n source: el.source.id,\n source_label: el.source.type,\n target: el.target.id,\n target_label: el.target.type,\n type: el.type.replace(/ /g, \"_\").toUpperCase(),\n properties: el.properties,\n })),\n });\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n\nfunction getNodeImportQuery({\n baseEntityLabel,\n includeSource,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n MERGE(source: \\`${BASE_ENTITY_LABEL}\\` {id: row.id})\n SET source += row.properties\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(source)\" : \"\"}\n WITH source, row\n CALL apoc.create.addLabels(source, [row.type]) YIELD node\n RETURN distinct 'done' AS result\n `;\n } else {\n return `\n ${includeSource ? INCLUDE_DOCS_QUERY : \"\"}\n UNWIND $data AS row\n CALL apoc.merge.node([row.type], {id: row.id},\n row.properties, {}) YIELD node\n ${includeSource ? \"MERGE (d)-[:MENTIONS]->(node)\" : \"\"}\n RETURN distinct 'done' AS result\n `;\n }\n}\n\nfunction getRelImportQuery({\n baseEntityLabel,\n}: AddGraphDocumentsConfig): string {\n if (baseEntityLabel) {\n return `\n UNWIND $data AS row\n MERGE (source:\\`${BASE_ENTITY_LABEL}\\` {id: row.source})\n MERGE (target:\\`${BASE_ENTITY_LABEL}\\` {id: row.target})\n WITH source, target, row\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n } else {\n return `\n UNWIND $data AS row\n CALL apoc.merge.node([row.source_label], {id: row.source},\n {}, {}) YIELD node as source\n CALL apoc.merge.node([row.target_label], {id: row.target},\n {}, {}) YIELD node as target\n CALL apoc.merge.relationship(source, row.type,\n {}, row.properties, target) YIELD rel\n RETURN distinct 'done'\n `;\n }\n}\n\nfunction toObjects<\n RecordShape extends Record<string, Any> = Record<string, Any>,\n>(records: Neo4jRecord<RecordShape>): RecordShape[] {\n return records.map((record: Any) => {\n const rObj = record.toObject();\n const out: Partial<RecordShape> = {};\n Object.keys(rObj).forEach((key: keyof RecordShape) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out as RecordShape;\n });\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nconst extractPathForRows = (path: Neo4jPath) => {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.cjs","names":["BaseListChatMessageHistory","neo4j","auth"],"sources":["../../../src/stores/message/neo4j.ts"],"sourcesContent":["import neo4j, { Driver, Record, auth, type ServerInfo } from \"neo4j-driver\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { BaseListChatMessageHistory } from \"@langchain/core/chat_history\";\nimport {\n BaseMessage,\n mapStoredMessagesToChatMessages,\n} from \"@langchain/core/messages\";\n\nexport type Neo4jChatMessageHistoryConfigInput = {\n sessionId?: string | number;\n sessionNodeLabel?: string;\n messageNodeLabel?: string;\n url: string;\n username: string;\n password: string;\n windowSize?: number;\n};\n\nconst defaultConfig = {\n sessionNodeLabel: \"ChatSession\",\n messageNodeLabel: \"ChatMessage\",\n windowSize: 3,\n};\n\nexport class Neo4jChatMessageHistory extends BaseListChatMessageHistory {\n lc_namespace: string[] = [\"langchain\", \"stores\", \"message\", \"neo4j\"];\n\n sessionId: string | number;\n\n sessionNodeLabel: string;\n\n messageNodeLabel: string;\n\n windowSize: number;\n\n private driver: Driver;\n\n constructor({\n sessionId = uuidv4(),\n sessionNodeLabel = defaultConfig.sessionNodeLabel,\n messageNodeLabel = defaultConfig.messageNodeLabel,\n url,\n username,\n password,\n windowSize = defaultConfig.windowSize,\n }: Neo4jChatMessageHistoryConfigInput) {\n super();\n\n this.sessionId = sessionId;\n this.sessionNodeLabel = sessionNodeLabel;\n this.messageNodeLabel = messageNodeLabel;\n this.windowSize = windowSize;\n\n if (url && username && password) {\n try {\n this.driver = neo4j.driver(url, auth.basic(username, password));\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not create a Neo4j driver instance. Please check the connection details.\\nCause: ${error.message}`\n );\n }\n } else {\n throw new Error(\"Neo4j connection details not provided.\");\n }\n }\n\n static async initialize(\n props: Neo4jChatMessageHistoryConfigInput\n ): Promise<Neo4jChatMessageHistory> {\n const instance = new Neo4jChatMessageHistory(props);\n\n try {\n await instance.verifyConnectivity();\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not verify connection to the Neo4j database.\\nCause: ${error.message}`\n );\n }\n\n return instance;\n }\n\n async verifyConnectivity(): Promise<ServerInfo> {\n const connectivity = await this.driver.getServerInfo();\n return connectivity;\n }\n\n async getMessages(): Promise<BaseMessage[]> {\n const getMessagesCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n MATCH (chatSession)-[:LAST_MESSAGE]->(lastMessage)\n MATCH p=(lastMessage)<-[:NEXT*0..${this.windowSize * 2 - 1}]-()\n WITH p, length(p) AS length\n ORDER BY length DESC LIMIT 1\n UNWIND reverse(nodes(p)) AS node\n RETURN {data:{content: node.content}, type:node.type} AS result\n `;\n\n try {\n const { records } = await this.driver.executeQuery(\n getMessagesCypherQuery,\n {\n sessionId: this.sessionId,\n }\n );\n const results = records.map((record: Record) => record.get(\"result\"));\n\n return mapStoredMessagesToChatMessages(results);\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't get messages.\\nCause: ${error.message}`);\n }\n }\n\n async addMessage(message: BaseMessage): Promise<void> {\n const addMessageCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n OPTIONAL MATCH (chatSession)-[lastMessageRel:LAST_MESSAGE]->(lastMessage)\n CREATE (chatSession)-[:LAST_MESSAGE]->(newLastMessage:${this.messageNodeLabel})\n SET newLastMessage += {type:$type, content:$content}\n WITH newLastMessage, lastMessageRel, lastMessage\n WHERE lastMessage IS NOT NULL\n CREATE (lastMessage)-[:NEXT]->(newLastMessage)\n DELETE lastMessageRel\n `;\n\n try {\n await this.driver.executeQuery(addMessageCypherQuery, {\n sessionId: this.sessionId,\n type: message.getType(),\n content: message.content,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't add message.\\nCause: ${error.message}`);\n }\n }\n\n async clear() {\n const clearMessagesCypherQuery = `\n MATCH p=(chatSession:${this.sessionNodeLabel} {id: $sessionId})-[:LAST_MESSAGE]->(lastMessage)<-[:NEXT*0..]-()\n UNWIND nodes(p) as node\n DETACH DELETE node\n `;\n\n try {\n await this.driver.executeQuery(clearMessagesCypherQuery, {\n sessionId: this.sessionId,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Ohno! Couldn't clear chat history.\\nCause: ${error.message}`\n );\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n"],"mappings":";;;;;;;AAkBA,MAAM,gBAAgB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,YAAY;CACb;AAED,IAAa,0BAAb,MAAa,gCAAgCA,6BAAAA,2BAA2B;CACtE,eAAyB;EAAC;EAAa;EAAU;EAAW;EAAQ;CAEpE;CAEA;CAEA;CAEA;CAEA;CAEA,YAAY,EACV,aAAA,GAAA,2BAAA,KAAoB,EACpB,mBAAmB,cAAc,kBACjC,mBAAmB,cAAc,kBACjC,KACA,UACA,UACA,aAAa,cAAc,cACU;AACrC,SAAO;AAEP,OAAK,YAAY;AACjB,OAAK,mBAAmB;AACxB,OAAK,mBAAmB;AACxB,OAAK,aAAa;AAElB,MAAI,OAAO,YAAY,SACrB,KAAI;AACF,QAAK,SAASC,aAAAA,QAAM,OAAO,KAAKC,aAAAA,KAAK,MAAM,UAAU,SAAS,CAAC;WACxD,GAAG;AAEV,SAAM,IAAI,MACR,
|
|
1
|
+
{"version":3,"file":"neo4j.cjs","names":["BaseListChatMessageHistory","neo4j","auth","error"],"sources":["../../../src/stores/message/neo4j.ts"],"sourcesContent":["import neo4j, { Driver, Record, auth, type ServerInfo } from \"neo4j-driver\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { BaseListChatMessageHistory } from \"@langchain/core/chat_history\";\nimport {\n BaseMessage,\n mapStoredMessagesToChatMessages,\n} from \"@langchain/core/messages\";\n\nexport type Neo4jChatMessageHistoryConfigInput = {\n sessionId?: string | number;\n sessionNodeLabel?: string;\n messageNodeLabel?: string;\n url: string;\n username: string;\n password: string;\n windowSize?: number;\n};\n\nconst defaultConfig = {\n sessionNodeLabel: \"ChatSession\",\n messageNodeLabel: \"ChatMessage\",\n windowSize: 3,\n};\n\nexport class Neo4jChatMessageHistory extends BaseListChatMessageHistory {\n lc_namespace: string[] = [\"langchain\", \"stores\", \"message\", \"neo4j\"];\n\n sessionId: string | number;\n\n sessionNodeLabel: string;\n\n messageNodeLabel: string;\n\n windowSize: number;\n\n private driver: Driver;\n\n constructor({\n sessionId = uuidv4(),\n sessionNodeLabel = defaultConfig.sessionNodeLabel,\n messageNodeLabel = defaultConfig.messageNodeLabel,\n url,\n username,\n password,\n windowSize = defaultConfig.windowSize,\n }: Neo4jChatMessageHistoryConfigInput) {\n super();\n\n this.sessionId = sessionId;\n this.sessionNodeLabel = sessionNodeLabel;\n this.messageNodeLabel = messageNodeLabel;\n this.windowSize = windowSize;\n\n if (url && username && password) {\n try {\n this.driver = neo4j.driver(url, auth.basic(username, password));\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not create a Neo4j driver instance. Please check the connection details.\\nCause: ${error.message}`\n );\n }\n } else {\n throw new Error(\"Neo4j connection details not provided.\");\n }\n }\n\n static async initialize(\n props: Neo4jChatMessageHistoryConfigInput\n ): Promise<Neo4jChatMessageHistory> {\n const instance = new Neo4jChatMessageHistory(props);\n\n try {\n await instance.verifyConnectivity();\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not verify connection to the Neo4j database.\\nCause: ${error.message}`\n );\n }\n\n return instance;\n }\n\n async verifyConnectivity(): Promise<ServerInfo> {\n const connectivity = await this.driver.getServerInfo();\n return connectivity;\n }\n\n async getMessages(): Promise<BaseMessage[]> {\n const getMessagesCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n MATCH (chatSession)-[:LAST_MESSAGE]->(lastMessage)\n MATCH p=(lastMessage)<-[:NEXT*0..${this.windowSize * 2 - 1}]-()\n WITH p, length(p) AS length\n ORDER BY length DESC LIMIT 1\n UNWIND reverse(nodes(p)) AS node\n RETURN {data:{content: node.content}, type:node.type} AS result\n `;\n\n try {\n const { records } = await this.driver.executeQuery(\n getMessagesCypherQuery,\n {\n sessionId: this.sessionId,\n }\n );\n const results = records.map((record: Record) => record.get(\"result\"));\n\n return mapStoredMessagesToChatMessages(results);\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't get messages.\\nCause: ${error.message}`);\n }\n }\n\n async addMessage(message: BaseMessage): Promise<void> {\n const addMessageCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n OPTIONAL MATCH (chatSession)-[lastMessageRel:LAST_MESSAGE]->(lastMessage)\n CREATE (chatSession)-[:LAST_MESSAGE]->(newLastMessage:${this.messageNodeLabel})\n SET newLastMessage += {type:$type, content:$content}\n WITH newLastMessage, lastMessageRel, lastMessage\n WHERE lastMessage IS NOT NULL\n CREATE (lastMessage)-[:NEXT]->(newLastMessage)\n DELETE lastMessageRel\n `;\n\n try {\n await this.driver.executeQuery(addMessageCypherQuery, {\n sessionId: this.sessionId,\n type: message.getType(),\n content: message.content,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't add message.\\nCause: ${error.message}`);\n }\n }\n\n async clear() {\n const clearMessagesCypherQuery = `\n MATCH p=(chatSession:${this.sessionNodeLabel} {id: $sessionId})-[:LAST_MESSAGE]->(lastMessage)<-[:NEXT*0..]-()\n UNWIND nodes(p) as node\n DETACH DELETE node\n `;\n\n try {\n await this.driver.executeQuery(clearMessagesCypherQuery, {\n sessionId: this.sessionId,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Ohno! Couldn't clear chat history.\\nCause: ${error.message}`\n );\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n"],"mappings":";;;;;;;AAkBA,MAAM,gBAAgB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,YAAY;CACb;AAED,IAAa,0BAAb,MAAa,gCAAgCA,6BAAAA,2BAA2B;CACtE,eAAyB;EAAC;EAAa;EAAU;EAAW;EAAQ;CAEpE;CAEA;CAEA;CAEA;CAEA;CAEA,YAAY,EACV,aAAA,GAAA,2BAAA,KAAoB,EACpB,mBAAmB,cAAc,kBACjC,mBAAmB,cAAc,kBACjC,KACA,UACA,UACA,aAAa,cAAc,cACU;AACrC,SAAO;AAEP,OAAK,YAAY;AACjB,OAAK,mBAAmB;AACxB,OAAK,mBAAmB;AACxB,OAAK,aAAa;AAElB,MAAI,OAAO,YAAY,SACrB,KAAI;AACF,QAAK,SAASC,aAAAA,QAAM,OAAO,KAAKC,aAAAA,KAAK,MAAM,UAAU,SAAS,CAAC;WACxD,GAAG;AAEV,SAAM,IAAI,MACR,0FAA0FC,EAAM,UACjG;;MAGH,OAAM,IAAI,MAAM,yCAAyC;;CAI7D,aAAa,WACX,OACkC;EAClC,MAAM,WAAW,IAAI,wBAAwB,MAAM;AAEnD,MAAI;AACF,SAAM,SAAS,oBAAoB;WAC5B,GAAG;AAEV,SAAM,IAAI,MACR,8DAA8DA,EAAM,UACrE;;AAGH,SAAO;;CAGT,MAAM,qBAA0C;AAE9C,SAAO,MADoB,KAAK,OAAO,eAAe;;CAIxD,MAAM,cAAsC;EAC1C,MAAM,yBAAyB;2BACR,KAAK,iBAAiB;;;yCAGR,KAAK,aAAa,IAAI,EAAE;;;;;;AAO7D,MAAI;GACF,MAAM,EAAE,YAAY,MAAM,KAAK,OAAO,aACpC,wBACA,EACE,WAAW,KAAK,WACjB,CACF;AAGD,WAAA,GAAA,yBAAA,iCAFgB,QAAQ,KAAK,WAAmB,OAAO,IAAI,SAAS,CAEtB,CAAC;WACxC,GAAG;AAEV,SAAM,IAAI,MAAM,wCAAwCA,EAAM,UAAU;;;CAI5E,MAAM,WAAW,SAAqC;EACpD,MAAM,wBAAwB;2BACP,KAAK,iBAAiB;;;8DAGa,KAAK,iBAAiB;;;;;;;AAQhF,MAAI;AACF,SAAM,KAAK,OAAO,aAAa,uBAAuB;IACpD,WAAW,KAAK;IAChB,MAAM,QAAQ,SAAS;IACvB,SAAS,QAAQ;IAClB,CAAC;WACK,GAAG;AAEV,SAAM,IAAI,MAAM,uCAAuCA,EAAM,UAAU;;;CAI3E,MAAM,QAAQ;EACZ,MAAM,2BAA2B;6BACR,KAAK,iBAAiB;;;;AAK/C,MAAI;AACF,SAAM,KAAK,OAAO,aAAa,0BAA0B,EACvD,WAAW,KAAK,WACjB,CAAC;WACK,GAAG;AAEV,SAAM,IAAI,MACR,8CAA8CA,EAAM,UACrD;;;CAIL,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j.js","names":["uuidv4"],"sources":["../../../src/stores/message/neo4j.ts"],"sourcesContent":["import neo4j, { Driver, Record, auth, type ServerInfo } from \"neo4j-driver\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { BaseListChatMessageHistory } from \"@langchain/core/chat_history\";\nimport {\n BaseMessage,\n mapStoredMessagesToChatMessages,\n} from \"@langchain/core/messages\";\n\nexport type Neo4jChatMessageHistoryConfigInput = {\n sessionId?: string | number;\n sessionNodeLabel?: string;\n messageNodeLabel?: string;\n url: string;\n username: string;\n password: string;\n windowSize?: number;\n};\n\nconst defaultConfig = {\n sessionNodeLabel: \"ChatSession\",\n messageNodeLabel: \"ChatMessage\",\n windowSize: 3,\n};\n\nexport class Neo4jChatMessageHistory extends BaseListChatMessageHistory {\n lc_namespace: string[] = [\"langchain\", \"stores\", \"message\", \"neo4j\"];\n\n sessionId: string | number;\n\n sessionNodeLabel: string;\n\n messageNodeLabel: string;\n\n windowSize: number;\n\n private driver: Driver;\n\n constructor({\n sessionId = uuidv4(),\n sessionNodeLabel = defaultConfig.sessionNodeLabel,\n messageNodeLabel = defaultConfig.messageNodeLabel,\n url,\n username,\n password,\n windowSize = defaultConfig.windowSize,\n }: Neo4jChatMessageHistoryConfigInput) {\n super();\n\n this.sessionId = sessionId;\n this.sessionNodeLabel = sessionNodeLabel;\n this.messageNodeLabel = messageNodeLabel;\n this.windowSize = windowSize;\n\n if (url && username && password) {\n try {\n this.driver = neo4j.driver(url, auth.basic(username, password));\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not create a Neo4j driver instance. Please check the connection details.\\nCause: ${error.message}`\n );\n }\n } else {\n throw new Error(\"Neo4j connection details not provided.\");\n }\n }\n\n static async initialize(\n props: Neo4jChatMessageHistoryConfigInput\n ): Promise<Neo4jChatMessageHistory> {\n const instance = new Neo4jChatMessageHistory(props);\n\n try {\n await instance.verifyConnectivity();\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not verify connection to the Neo4j database.\\nCause: ${error.message}`\n );\n }\n\n return instance;\n }\n\n async verifyConnectivity(): Promise<ServerInfo> {\n const connectivity = await this.driver.getServerInfo();\n return connectivity;\n }\n\n async getMessages(): Promise<BaseMessage[]> {\n const getMessagesCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n MATCH (chatSession)-[:LAST_MESSAGE]->(lastMessage)\n MATCH p=(lastMessage)<-[:NEXT*0..${this.windowSize * 2 - 1}]-()\n WITH p, length(p) AS length\n ORDER BY length DESC LIMIT 1\n UNWIND reverse(nodes(p)) AS node\n RETURN {data:{content: node.content}, type:node.type} AS result\n `;\n\n try {\n const { records } = await this.driver.executeQuery(\n getMessagesCypherQuery,\n {\n sessionId: this.sessionId,\n }\n );\n const results = records.map((record: Record) => record.get(\"result\"));\n\n return mapStoredMessagesToChatMessages(results);\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't get messages.\\nCause: ${error.message}`);\n }\n }\n\n async addMessage(message: BaseMessage): Promise<void> {\n const addMessageCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n OPTIONAL MATCH (chatSession)-[lastMessageRel:LAST_MESSAGE]->(lastMessage)\n CREATE (chatSession)-[:LAST_MESSAGE]->(newLastMessage:${this.messageNodeLabel})\n SET newLastMessage += {type:$type, content:$content}\n WITH newLastMessage, lastMessageRel, lastMessage\n WHERE lastMessage IS NOT NULL\n CREATE (lastMessage)-[:NEXT]->(newLastMessage)\n DELETE lastMessageRel\n `;\n\n try {\n await this.driver.executeQuery(addMessageCypherQuery, {\n sessionId: this.sessionId,\n type: message.getType(),\n content: message.content,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't add message.\\nCause: ${error.message}`);\n }\n }\n\n async clear() {\n const clearMessagesCypherQuery = `\n MATCH p=(chatSession:${this.sessionNodeLabel} {id: $sessionId})-[:LAST_MESSAGE]->(lastMessage)<-[:NEXT*0..]-()\n UNWIND nodes(p) as node\n DETACH DELETE node\n `;\n\n try {\n await this.driver.executeQuery(clearMessagesCypherQuery, {\n sessionId: this.sessionId,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Ohno! Couldn't clear chat history.\\nCause: ${error.message}`\n );\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,gBAAgB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,YAAY;CACb;AAED,IAAa,0BAAb,MAAa,gCAAgC,2BAA2B;CACtE,eAAyB;EAAC;EAAa;EAAU;EAAW;EAAQ;CAEpE;CAEA;CAEA;CAEA;CAEA;CAEA,YAAY,EACV,YAAYA,IAAQ,EACpB,mBAAmB,cAAc,kBACjC,mBAAmB,cAAc,kBACjC,KACA,UACA,UACA,aAAa,cAAc,cACU;AACrC,SAAO;AAEP,OAAK,YAAY;AACjB,OAAK,mBAAmB;AACxB,OAAK,mBAAmB;AACxB,OAAK,aAAa;AAElB,MAAI,OAAO,YAAY,SACrB,KAAI;AACF,QAAK,SAAS,MAAM,OAAO,KAAK,KAAK,MAAM,UAAU,SAAS,CAAC;WACxD,GAAG;AAEV,SAAM,IAAI,MACR,
|
|
1
|
+
{"version":3,"file":"neo4j.js","names":["uuidv4","error"],"sources":["../../../src/stores/message/neo4j.ts"],"sourcesContent":["import neo4j, { Driver, Record, auth, type ServerInfo } from \"neo4j-driver\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { BaseListChatMessageHistory } from \"@langchain/core/chat_history\";\nimport {\n BaseMessage,\n mapStoredMessagesToChatMessages,\n} from \"@langchain/core/messages\";\n\nexport type Neo4jChatMessageHistoryConfigInput = {\n sessionId?: string | number;\n sessionNodeLabel?: string;\n messageNodeLabel?: string;\n url: string;\n username: string;\n password: string;\n windowSize?: number;\n};\n\nconst defaultConfig = {\n sessionNodeLabel: \"ChatSession\",\n messageNodeLabel: \"ChatMessage\",\n windowSize: 3,\n};\n\nexport class Neo4jChatMessageHistory extends BaseListChatMessageHistory {\n lc_namespace: string[] = [\"langchain\", \"stores\", \"message\", \"neo4j\"];\n\n sessionId: string | number;\n\n sessionNodeLabel: string;\n\n messageNodeLabel: string;\n\n windowSize: number;\n\n private driver: Driver;\n\n constructor({\n sessionId = uuidv4(),\n sessionNodeLabel = defaultConfig.sessionNodeLabel,\n messageNodeLabel = defaultConfig.messageNodeLabel,\n url,\n username,\n password,\n windowSize = defaultConfig.windowSize,\n }: Neo4jChatMessageHistoryConfigInput) {\n super();\n\n this.sessionId = sessionId;\n this.sessionNodeLabel = sessionNodeLabel;\n this.messageNodeLabel = messageNodeLabel;\n this.windowSize = windowSize;\n\n if (url && username && password) {\n try {\n this.driver = neo4j.driver(url, auth.basic(username, password));\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not create a Neo4j driver instance. Please check the connection details.\\nCause: ${error.message}`\n );\n }\n } else {\n throw new Error(\"Neo4j connection details not provided.\");\n }\n }\n\n static async initialize(\n props: Neo4jChatMessageHistoryConfigInput\n ): Promise<Neo4jChatMessageHistory> {\n const instance = new Neo4jChatMessageHistory(props);\n\n try {\n await instance.verifyConnectivity();\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Could not verify connection to the Neo4j database.\\nCause: ${error.message}`\n );\n }\n\n return instance;\n }\n\n async verifyConnectivity(): Promise<ServerInfo> {\n const connectivity = await this.driver.getServerInfo();\n return connectivity;\n }\n\n async getMessages(): Promise<BaseMessage[]> {\n const getMessagesCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n MATCH (chatSession)-[:LAST_MESSAGE]->(lastMessage)\n MATCH p=(lastMessage)<-[:NEXT*0..${this.windowSize * 2 - 1}]-()\n WITH p, length(p) AS length\n ORDER BY length DESC LIMIT 1\n UNWIND reverse(nodes(p)) AS node\n RETURN {data:{content: node.content}, type:node.type} AS result\n `;\n\n try {\n const { records } = await this.driver.executeQuery(\n getMessagesCypherQuery,\n {\n sessionId: this.sessionId,\n }\n );\n const results = records.map((record: Record) => record.get(\"result\"));\n\n return mapStoredMessagesToChatMessages(results);\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't get messages.\\nCause: ${error.message}`);\n }\n }\n\n async addMessage(message: BaseMessage): Promise<void> {\n const addMessageCypherQuery = `\n MERGE (chatSession:${this.sessionNodeLabel} {id: $sessionId})\n WITH chatSession\n OPTIONAL MATCH (chatSession)-[lastMessageRel:LAST_MESSAGE]->(lastMessage)\n CREATE (chatSession)-[:LAST_MESSAGE]->(newLastMessage:${this.messageNodeLabel})\n SET newLastMessage += {type:$type, content:$content}\n WITH newLastMessage, lastMessageRel, lastMessage\n WHERE lastMessage IS NOT NULL\n CREATE (lastMessage)-[:NEXT]->(newLastMessage)\n DELETE lastMessageRel\n `;\n\n try {\n await this.driver.executeQuery(addMessageCypherQuery, {\n sessionId: this.sessionId,\n type: message.getType(),\n content: message.content,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(`Ohno! Couldn't add message.\\nCause: ${error.message}`);\n }\n }\n\n async clear() {\n const clearMessagesCypherQuery = `\n MATCH p=(chatSession:${this.sessionNodeLabel} {id: $sessionId})-[:LAST_MESSAGE]->(lastMessage)<-[:NEXT*0..]-()\n UNWIND nodes(p) as node\n DETACH DELETE node\n `;\n\n try {\n await this.driver.executeQuery(clearMessagesCypherQuery, {\n sessionId: this.sessionId,\n });\n } catch (e) {\n const error = e as Error;\n throw new Error(\n `Ohno! Couldn't clear chat history.\\nCause: ${error.message}`\n );\n }\n }\n\n async close() {\n await this.driver.close();\n }\n}\n"],"mappings":";;;;;AAkBA,MAAM,gBAAgB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,YAAY;CACb;AAED,IAAa,0BAAb,MAAa,gCAAgC,2BAA2B;CACtE,eAAyB;EAAC;EAAa;EAAU;EAAW;EAAQ;CAEpE;CAEA;CAEA;CAEA;CAEA;CAEA,YAAY,EACV,YAAYA,IAAQ,EACpB,mBAAmB,cAAc,kBACjC,mBAAmB,cAAc,kBACjC,KACA,UACA,UACA,aAAa,cAAc,cACU;AACrC,SAAO;AAEP,OAAK,YAAY;AACjB,OAAK,mBAAmB;AACxB,OAAK,mBAAmB;AACxB,OAAK,aAAa;AAElB,MAAI,OAAO,YAAY,SACrB,KAAI;AACF,QAAK,SAAS,MAAM,OAAO,KAAK,KAAK,MAAM,UAAU,SAAS,CAAC;WACxD,GAAG;AAEV,SAAM,IAAI,MACR,0FAA0FC,EAAM,UACjG;;MAGH,OAAM,IAAI,MAAM,yCAAyC;;CAI7D,aAAa,WACX,OACkC;EAClC,MAAM,WAAW,IAAI,wBAAwB,MAAM;AAEnD,MAAI;AACF,SAAM,SAAS,oBAAoB;WAC5B,GAAG;AAEV,SAAM,IAAI,MACR,8DAA8DA,EAAM,UACrE;;AAGH,SAAO;;CAGT,MAAM,qBAA0C;AAE9C,SAAO,MADoB,KAAK,OAAO,eAAe;;CAIxD,MAAM,cAAsC;EAC1C,MAAM,yBAAyB;2BACR,KAAK,iBAAiB;;;yCAGR,KAAK,aAAa,IAAI,EAAE;;;;;;AAO7D,MAAI;GACF,MAAM,EAAE,YAAY,MAAM,KAAK,OAAO,aACpC,wBACA,EACE,WAAW,KAAK,WACjB,CACF;AAGD,UAAO,gCAFS,QAAQ,KAAK,WAAmB,OAAO,IAAI,SAAS,CAEtB,CAAC;WACxC,GAAG;AAEV,SAAM,IAAI,MAAM,wCAAwCA,EAAM,UAAU;;;CAI5E,MAAM,WAAW,SAAqC;EACpD,MAAM,wBAAwB;2BACP,KAAK,iBAAiB;;;8DAGa,KAAK,iBAAiB;;;;;;;AAQhF,MAAI;AACF,SAAM,KAAK,OAAO,aAAa,uBAAuB;IACpD,WAAW,KAAK;IAChB,MAAM,QAAQ,SAAS;IACvB,SAAS,QAAQ;IAClB,CAAC;WACK,GAAG;AAEV,SAAM,IAAI,MAAM,uCAAuCA,EAAM,UAAU;;;CAI3E,MAAM,QAAQ;EACZ,MAAM,2BAA2B;6BACR,KAAK,iBAAiB;;;;AAK/C,MAAI;AACF,SAAM,KAAK,OAAO,aAAa,0BAA0B,EACvD,WAAW,KAAK,WACjB,CAAC;WACK,GAAG;AAEV,SAAM,IAAI,MACR,8CAA8CA,EAAM,UACrD;;;CAIL,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j_vector.cjs","names":["VectorStore","neo4j","Document","uuid"],"sources":["../../src/vectorstores/neo4j_vector.ts"],"sourcesContent":["import neo4j, {\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport * as uuid from \"@langchain/core/utils/uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport type SearchType = \"vector\" | \"hybrid\";\n\nexport type IndexType = \"NODE\" | \"RELATIONSHIP\";\n\nexport type DistanceStrategy = \"euclidean\" | \"cosine\";\n\nexport type Metadata = Record<string, unknown>;\n\nexport interface Neo4jVectorStoreArgs {\n url: string;\n username: string;\n password: string;\n database?: string;\n preDeleteCollection?: boolean;\n textNodeProperty?: string;\n textNodeProperties?: string[];\n embeddingNodeProperty?: string;\n keywordIndexName?: string;\n indexName?: string;\n searchType?: SearchType;\n indexType?: IndexType;\n retrievalQuery?: string;\n nodeLabel?: string;\n createIdIndex?: boolean;\n}\n\nconst DEFAULT_SEARCH_TYPE = \"vector\";\nconst DEFAULT_INDEX_TYPE = \"NODE\";\nconst DEFAULT_DISTANCE_STRATEGY = \"cosine\";\nconst DEFAULT_NODE_EMBEDDING_PROPERTY = \"embedding\";\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jVectorStore extends VectorStore {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private preDeleteCollection: boolean;\n\n private nodeLabel: string;\n\n private embeddingNodeProperty: string;\n\n private embeddingDimension: number;\n\n private textNodeProperty: string;\n\n private keywordIndexName: string;\n\n private indexName: string;\n\n private retrievalQuery: string;\n\n private searchType: SearchType;\n\n private indexType: IndexType;\n\n private distanceStrategy: DistanceStrategy = DEFAULT_DISTANCE_STRATEGY;\n\n private supportMetadataFilter = true;\n\n private isEnterprise = false;\n\n _vectorstoreType(): string {\n return \"neo4jvector\";\n }\n\n constructor(embeddings: EmbeddingsInterface, config: Neo4jVectorStoreArgs) {\n super(embeddings, config);\n }\n\n static async initialize(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const store = new Neo4jVectorStore(embeddings, config);\n await store._initializeDriver(config);\n await store._verifyConnectivity();\n\n const {\n preDeleteCollection = false,\n nodeLabel = \"Chunk\",\n textNodeProperty = \"text\",\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n keywordIndexName = \"keyword\",\n indexName = \"vector\",\n retrievalQuery = \"\",\n searchType = DEFAULT_SEARCH_TYPE,\n indexType = DEFAULT_INDEX_TYPE,\n } = config;\n\n store.embeddingDimension = (await embeddings.embedQuery(\"foo\")).length;\n store.preDeleteCollection = preDeleteCollection;\n store.nodeLabel = nodeLabel;\n store.textNodeProperty = textNodeProperty;\n store.embeddingNodeProperty = embeddingNodeProperty;\n store.keywordIndexName = keywordIndexName;\n store.indexName = indexName;\n store.retrievalQuery = retrievalQuery;\n store.searchType = searchType;\n store.indexType = indexType;\n\n if (store.preDeleteCollection) {\n await store._dropIndex();\n }\n\n return store;\n }\n\n async _initializeDriver({\n url,\n username,\n password,\n database = \"neo4j\",\n }: Neo4jVectorStoreArgs) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n async _verifyConnectivity() {\n await this.driver.verifyAuthentication();\n }\n\n async _verifyVersion() {\n try {\n const data = await this.query(\"CALL dbms.components()\");\n const versionString: string = data[0].versions[0];\n const targetVersion = [5, 11, 0];\n\n let version: number[];\n\n if (versionString.includes(\"aura\")) {\n const baseVersion = versionString.split(\"-\")[0];\n version = baseVersion.split(\".\").map(Number);\n version.push(0);\n } else {\n version = versionString.split(\".\").map(Number);\n }\n\n if (isVersionLessThan(version, targetVersion)) {\n throw new Error(\n \"Version index is only supported in Neo4j version 5.11 or greater\"\n );\n }\n\n const metadataTargetVersion = [5, 18, 0];\n if (isVersionLessThan(version, metadataTargetVersion)) {\n this.supportMetadataFilter = false;\n }\n\n this.isEnterprise = data[0].edition === \"enterprise\";\n } catch (error) {\n console.error(\"Database version check failed:\", error);\n }\n }\n\n async close() {\n await this.driver.close();\n }\n\n async _dropIndex() {\n try {\n await this.query(`\n MATCH (n:\\`${this.nodeLabel}\\`)\n CALL {\n WITH n\n DETACH DELETE n\n }\n IN TRANSACTIONS OF 10000 ROWS;\n `);\n await this.query(`DROP INDEX ${this.indexName}`);\n } catch (error) {\n console.error(\"An error occurred while dropping the index:\", error);\n }\n }\n\n async query(query: string, params: Any = {}): Promise<Any[]> {\n const session = this.driver.session({ database: this.database });\n const result = await session.run(query, params);\n return toObjects(result.records);\n }\n\n static async fromTexts(\n texts: string[],\n metadatas: Any,\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const docs = [];\n\n for (let i = 0; i < texts.length; i += 1) {\n const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n const newDoc = new Document({\n pageContent: texts[i],\n metadata,\n });\n docs.push(newDoc);\n }\n\n return Neo4jVectorStore.fromDocuments(docs, embeddings, config);\n }\n\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const {\n searchType = DEFAULT_SEARCH_TYPE,\n createIdIndex = true,\n textNodeProperties = [],\n } = config;\n\n const store = await this.initialize(embeddings, config);\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name \"${store.indexName}\" already exists. The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n if (createIdIndex) {\n await store.query(\n `CREATE CONSTRAINT IF NOT EXISTS FOR (n:${store.nodeLabel}) REQUIRE n.id IS UNIQUE;`\n );\n }\n\n await store.addDocuments(docs);\n\n return store;\n }\n\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const { searchType = DEFAULT_SEARCH_TYPE, keywordIndexName = \"keyword\" } =\n config;\n\n if (searchType === \"hybrid\" && !keywordIndexName) {\n throw Error(\n \"keyword_index name has to be specified when using hybrid search option\"\n );\n }\n\n const store = await this.initialize(embeddings, config);\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n throw Error(\n \"The specified vector index name does not exist. Make sure to check if you spelled it correctly\"\n );\n }\n\n if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n throw Error(\n \"The specified keyword index name does not exist. Make sure to check if you spelled it correctly\"\n );\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n return store;\n }\n\n static async fromExistingGraph(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const {\n textNodeProperties = [],\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n searchType = DEFAULT_SEARCH_TYPE,\n retrievalQuery = \"\",\n nodeLabel,\n } = config;\n\n let _retrievalQuery = retrievalQuery;\n\n if (textNodeProperties.length === 0) {\n throw Error(\n \"Parameter `text_node_properties` must not be an empty array\"\n );\n }\n\n if (!retrievalQuery) {\n _retrievalQuery = `\n RETURN reduce(str='', k IN ${JSON.stringify(textNodeProperties)} |\n str + '\\\\n' + k + ': ' + coalesce(node[k], '')) AS text,\n node {.*, \\`${embeddingNodeProperty}\\`: Null, id: Null, ${textNodeProperties\n .map((prop) => `\\`${prop}\\`: Null`)\n .join(\", \")} } AS metadata, score\n `;\n }\n\n const store = await this.initialize(embeddings, {\n ...config,\n retrievalQuery: _retrievalQuery,\n });\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name ${store.indexName} already exists. The provided embedding function and vector index dimensions do not match.\\nEmbedding function dimension: ${store.embeddingDimension}\\nVector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel =\n await store.retrieveExistingFtsIndex(textNodeProperties);\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n while (true) {\n const fetchQuery = `\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE n.${embeddingNodeProperty} IS null\n AND any(k in $props WHERE n[k] IS NOT null)\n RETURN elementId(n) AS id, reduce(str='', k IN $props |\n str + '\\\\n' + k + ':' + coalesce(n[k], '')) AS text\n LIMIT 1000\n `;\n\n const data = await store.query(fetchQuery, {\n props: textNodeProperties,\n });\n\n if (!data) {\n break;\n }\n\n const textEmbeddings = await embeddings.embedDocuments(\n data.map((el) => el.text)\n );\n\n const params = {\n data: data.map((el, index) => ({\n id: el.id,\n embedding: textEmbeddings[index],\n })),\n };\n\n await store.query(\n `\n UNWIND $data AS row\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE elementId(n) = row.id\n CALL db.create.setVectorProperty(n, '${embeddingNodeProperty}', row.embedding)\n YIELD node RETURN count(*)\n `,\n params\n );\n\n if (data.length < 1000) {\n break;\n }\n }\n\n return store;\n }\n\n async createNewIndex(): Promise<void> {\n const indexQuery = `\n CALL db.index.vector.createNodeIndex(\n $index_name,\n $node_label,\n $embedding_node_property,\n toInteger($embedding_dimension),\n $similarity_metric\n )\n `;\n\n const parameters = {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n embedding_dimension: this.embeddingDimension,\n similarity_metric: this.distanceStrategy,\n };\n\n await this.query(indexQuery, parameters);\n }\n\n async retrieveExistingIndex() {\n let indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'VECTOR' AND (name = $index_name\n OR (labelsOrTypes[0] = $node_label AND\n properties[0] = $embedding_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n }\n );\n\n if (indexInformation) {\n indexInformation = this.sortByIndexName(indexInformation, this.indexName);\n\n try {\n const [index] = indexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.indexName = index.name;\n this.nodeLabel = labelOrType;\n this.embeddingNodeProperty = property;\n\n const embeddingDimension =\n index.options.indexConfig[\"vector.dimensions\"];\n return Number(embeddingDimension);\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async retrieveExistingFtsIndex(\n textNodeProperties: string[] = []\n ): Promise<string | null> {\n const indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'FULLTEXT' AND (name = $keyword_index_name\n OR (labelsOrTypes = [$node_label] AND\n properties = $text_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n keyword_index_name: this.keywordIndexName,\n node_label: this.nodeLabel,\n text_node_property:\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty],\n }\n );\n\n if (indexInformation) {\n const sortedIndexInformation = this.sortByIndexName(\n indexInformation,\n this.indexName\n );\n\n try {\n const [index] = sortedIndexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.keywordIndexName = index.name;\n this.textNodeProperty = property;\n this.nodeLabel = labelOrType;\n\n return labelOrType;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async createNewKeywordIndex(\n textNodeProperties: string[] = []\n ): Promise<void> {\n const nodeProps =\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty];\n\n const ftsIndexQuery = `\n CREATE FULLTEXT INDEX ${this.keywordIndexName}\n FOR (n:\\`${this.nodeLabel}\\`) ON EACH\n [${nodeProps.map((prop) => `n.\\`${prop}\\``).join(\", \")}]\n `;\n\n await this.query(ftsIndexQuery);\n }\n\n sortByIndexName(\n values: Array<{ [key: string]: Any }>,\n indexName: string\n ): Array<{ [key: string]: Any }> {\n return values.sort(\n (a, b) =>\n (a.name === indexName ? -1 : 0) - (b.name === indexName ? -1 : 0)\n );\n }\n\n async addVectors(\n vectors: number[][],\n documents: Document[],\n metadatas?: Record<string, Any>[],\n ids?: string[]\n ): Promise<string[]> {\n let _ids = ids;\n const _metadatas = metadatas;\n\n if (!_ids) {\n _ids = documents.map(() => uuid.v1());\n }\n\n const importQuery = `\n UNWIND $data AS row\n CALL {\n WITH row\n MERGE (c:\\`${this.nodeLabel}\\` {id: row.id})\n WITH c, row\n CALL db.create.setVectorProperty(c, '${this.embeddingNodeProperty}', row.embedding)\n YIELD node\n SET c.\\`${this.textNodeProperty}\\` = row.text\n SET c += row.metadata\n } IN TRANSACTIONS OF 1000 ROWS\n `;\n\n const parameters = {\n data: documents.map(({ pageContent, metadata }, index) => ({\n text: pageContent,\n metadata: _metadatas ? _metadatas[index] : metadata,\n embedding: vectors[index],\n id: _ids ? _ids[index] : null,\n })),\n };\n\n await this.query(importQuery, parameters);\n\n return _ids;\n }\n\n async addDocuments(documents: Document[]): Promise<string[]> {\n const texts = documents.map(({ pageContent }) => pageContent);\n\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents\n );\n }\n\n async similaritySearch(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<Document[]> {\n const embedding = await this.embeddings.embedQuery(query);\n\n const results = await this.similaritySearchVectorWithScore(\n embedding,\n k,\n query,\n params\n );\n\n return results.map((result) => result[0]);\n }\n\n async similaritySearchWithScore(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n const embedding = await this.embeddings.embedQuery(query);\n return this.similaritySearchVectorWithScore(embedding, k, query, params);\n }\n\n async similaritySearchVectorWithScore(\n vector: number[],\n k: number,\n query: string,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n let indexQuery: string;\n let filterParams: Record<string, Any>;\n\n const { filter } = params;\n\n if (filter) {\n if (!this.supportMetadataFilter) {\n throw new Error(\n \"Metadata filtering is only supported in Neo4j version 5.18 or greater.\"\n );\n }\n\n if (this.searchType === \"hybrid\") {\n throw new Error(\n \"Metadata filtering can't be use in combination with a hybrid search approach.\"\n );\n }\n\n const parallelQuery = this.isEnterprise\n ? \"CYPHER runtime = parallel parallelRuntimeSupport=all \"\n : \"\";\n\n const baseIndexQuery = `\n ${parallelQuery}\n MATCH (n:\\`${this.nodeLabel}\\`)\n WHERE n.\\`${this.embeddingNodeProperty}\\` IS NOT NULL\n AND size(n.\\`${this.embeddingNodeProperty}\\`) = toInteger(${this.embeddingDimension}) AND\n `;\n\n const baseCosineQuery = `\n WITH n as node, vector.similarity.cosine(\n n.\\`${this.embeddingNodeProperty}\\`,\n $embedding\n ) AS score ORDER BY score DESC LIMIT toInteger($k)\n `;\n const [fSnippets, fParams] = constructMetadataFilter(filter);\n\n indexQuery = baseIndexQuery + fSnippets + baseCosineQuery;\n filterParams = fParams;\n } else {\n indexQuery = getSearchIndexQuery(this.searchType, this.indexType);\n filterParams = {};\n }\n\n let defaultRetrieval: string;\n\n if (this.indexType === \"RELATIONSHIP\") {\n defaultRetrieval = `\n RETURN relationship.${this.textNodeProperty} AS text, score,\n relationship {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n } else {\n defaultRetrieval = `\n RETURN node.${this.textNodeProperty} AS text, score,\n node {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n }\n\n const retrievalQuery = this.retrievalQuery\n ? this.retrievalQuery\n : defaultRetrieval;\n const readQuery = `${indexQuery} ${retrievalQuery}`;\n\n const parameters = {\n index: this.indexName,\n k: Number(k),\n embedding: vector,\n keyword_index: this.keywordIndexName,\n query: removeLuceneChars(query),\n ...params,\n ...filterParams,\n };\n\n const results = await this.query(readQuery, parameters);\n\n if (results) {\n if (results.some((result) => result.text == null)) {\n if (!this.retrievalQuery) {\n throw new Error(\n `Make sure that none of the '${this.textNodeProperty}' properties on nodes with label '${this.nodeLabel}' are missing or empty`\n );\n } else {\n throw new Error(\n \"Inspect the 'retrievalQuery' and ensure it doesn't return null for the 'text' column\"\n );\n }\n }\n\n const docs: [Document, number][] = results.map((result: Any) => [\n new Document({\n pageContent: result.text,\n metadata: Object.fromEntries(\n Object.entries(result.metadata).filter(([_, v]) => v !== null)\n ),\n }),\n result.score,\n ]);\n\n return docs;\n }\n\n return [];\n }\n}\n\nfunction toObjects(records: Neo4jRecord[]) {\n const recordValues: Record<string, Any>[] = records.map((record) => {\n const rObj = record.toObject();\n const out: { [key: string]: Any } = {};\n Object.keys(rObj).forEach((key) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out;\n });\n return recordValues;\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nfunction extractPathForRows(path: Neo4jPath) {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n}\n\nfunction getSearchIndexQuery(\n searchType: SearchType,\n indexType: IndexType = DEFAULT_INDEX_TYPE\n): string {\n if (indexType === \"NODE\") {\n const typeToQueryMap: { [key in SearchType]: string } = {\n vector:\n \"CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\",\n hybrid: `\n CALL {\n CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\n WITH collect({node:node, score:score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n // We use 0 as min\n RETURN n.node AS node, (n.score / max) AS score UNION\n CALL db.index.fulltext.queryNodes($keyword_index, $query, {limit: $k}) YIELD node, score\n WITH collect({node: node, score: score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n RETURN n.node AS node, (n.score / max) AS score\n }\n WITH node, max(score) AS score ORDER BY score DESC LIMIT toInteger($k)\n `,\n };\n\n return typeToQueryMap[searchType];\n } else {\n return `\n CALL db.index.vector.queryRelationships($index, $k, $embedding)\n YIELD relationship, score\n `;\n }\n}\n\nexport function removeLuceneChars(text: string | null) {\n if (text === undefined || text === null) {\n return null;\n }\n\n const specialChars = [\n \"+\",\n \"-\",\n \"&\",\n \"|\",\n \"!\",\n \"(\",\n \")\",\n \"{\",\n \"}\",\n \"[\",\n \"]\",\n \"^\",\n '\"',\n \"~\",\n \"*\",\n \"?\",\n \":\",\n \"\\\\\",\n ];\n let modifiedText = text;\n for (const char of specialChars) {\n modifiedText = modifiedText.split(char).join(\" \");\n }\n return modifiedText.trim();\n}\n\nexport function isVersionLessThan(v1: number[], v2: number[]): boolean {\n for (let i = 0; i < Math.min(v1.length, v2.length); i += 1) {\n if (v1[i] < v2[i]) {\n return true;\n } else if (v1[i] > v2[i]) {\n return false;\n }\n }\n return v1.length < v2.length;\n}\n\nconst COMPARISONS_TO_NATIVE: Record<string, string> = {\n $eq: \"=\",\n $ne: \"<>\",\n $lt: \"<\",\n $lte: \"<=\",\n $gt: \">\",\n $gte: \">=\",\n};\n\nconst COMPARISONS_TO_NATIVE_OPERATORS = new Set(\n Object.keys(COMPARISONS_TO_NATIVE)\n);\n\nconst TEXT_OPERATORS = new Set([\"$like\", \"$ilike\"]);\n\nconst LOGICAL_OPERATORS = new Set([\"$and\", \"$or\"]);\n\nconst SPECIAL_CASED_OPERATORS = new Set([\"$in\", \"$nin\", \"$between\"]);\n\nconst SUPPORTED_OPERATORS = new Set([\n ...COMPARISONS_TO_NATIVE_OPERATORS,\n ...TEXT_OPERATORS,\n ...LOGICAL_OPERATORS,\n ...SPECIAL_CASED_OPERATORS,\n]);\n\nconst IS_IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction combineQueries(\n inputQueries: [string, Record<string, Any>][],\n operator: string\n): [string, Record<string, Any>] {\n let combinedQuery = \"\";\n const combinedParams: Record<string, Any> = {};\n const paramCounter: Record<string, number> = {};\n\n for (const [query, params] of inputQueries) {\n let newQuery = query;\n for (const [param, value] of Object.entries(params)) {\n if (param in paramCounter) {\n paramCounter[param] += 1;\n } else {\n paramCounter[param] = 1;\n }\n const newParamName = `${param}_${paramCounter[param]}`;\n\n newQuery = newQuery.replace(`$${param}`, `$${newParamName}`);\n combinedParams[newParamName] = value;\n }\n\n if (combinedQuery) {\n combinedQuery += ` ${operator} `;\n }\n combinedQuery += `(${newQuery})`;\n }\n\n return [combinedQuery, combinedParams];\n}\n\nfunction collectParams(\n inputData: [string, Record<string, string>][]\n): [string[], Record<string, Any>] {\n const queryParts: string[] = [];\n const params: Record<string, Any> = {};\n\n for (const [queryPart, param] of inputData) {\n queryParts.push(queryPart);\n Object.assign(params, param);\n }\n\n return [queryParts, params];\n}\n\nfunction handleFieldFilter(\n field: string,\n value: Any,\n paramNumber = 1\n): [string, Record<string, Any>] {\n if (typeof field !== \"string\") {\n throw new Error(\n `field should be a string but got: ${typeof field} with value: ${field}`\n );\n }\n\n if (field.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${field}`\n );\n }\n\n if (!IS_IDENTIFIER_REGEX.test(field)) {\n throw new Error(\n `Invalid field name: ${field}. Expected a valid identifier.`\n );\n }\n\n let operator: string;\n let filterValue: Any;\n\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n const keys = Object.keys(value);\n\n if (keys.length !== 1) {\n throw new Error(`Invalid filter condition. Expected a value which is a dictionary\n with a single key that corresponds to an operator but got a dictionary\n with ${keys.length} keys. The first few keys are: ${keys\n .slice(0, 3)\n .join(\", \")}\n `);\n }\n\n operator = keys[0];\n filterValue = value[operator];\n\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw new Error(\n `Invalid operator: ${operator}. Expected one of ${SUPPORTED_OPERATORS}`\n );\n }\n } else {\n operator = \"$eq\";\n filterValue = value;\n }\n\n if (COMPARISONS_TO_NATIVE_OPERATORS.has(operator)) {\n const native = COMPARISONS_TO_NATIVE[operator];\n const querySnippet = `n.${field} ${native} $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n\n return [querySnippet, queryParam];\n } else if (operator === \"$between\") {\n const [low, high] = filterValue;\n const querySnippet = `$param_${paramNumber}_low <= n.${field} <= $param_${paramNumber}_high`;\n const queryParam = {\n [`param_${paramNumber}_low`]: low,\n [`param_${paramNumber}_high`]: high,\n };\n\n return [querySnippet, queryParam];\n } else if ([\"$in\", \"$nin\", \"$like\", \"$ilike\"].includes(operator)) {\n if ([\"$in\", \"$nin\"].includes(operator)) {\n filterValue.forEach((val: Any) => {\n if (\n typeof val !== \"string\" &&\n typeof val !== \"number\" &&\n typeof val !== \"boolean\"\n ) {\n throw new Error(`Unsupported type: ${typeof val} for value: ${val}`);\n }\n });\n }\n\n if (operator === \"$in\") {\n const querySnippet = `n.${field} IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$nin\") {\n const querySnippet = `n.${field} NOT IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$like\") {\n const querySnippet = `n.${field} CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else if (operator === \"$ilike\") {\n const querySnippet = `toLower(n.${field}) CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else {\n throw new Error(\"Not Implemented\");\n }\n } else {\n throw new Error(\"Not Implemented\");\n }\n}\n\nexport function constructMetadataFilter(\n filter: Record<string, Any>\n): [string, Record<string, Any>] {\n if (typeof filter !== \"object\" || filter === null) {\n throw new Error(\"Expected a dictionary representing the filter condition.\");\n }\n\n const entries = Object.entries(filter);\n\n if (entries.length === 1) {\n const [key, value] = entries[0];\n\n if (key.startsWith(\"$\")) {\n if (![\"$and\", \"$or\"].includes(key.toLowerCase())) {\n throw new Error(\n `Invalid filter condition. Expected $and or $or but got: ${key}`\n );\n }\n\n if (!Array.isArray(value)) {\n throw new Error(\n `Expected an array for logical conditions, but got ${typeof value} for value: ${value}`\n );\n }\n\n const operation = key.toLowerCase() === \"$and\" ? \"AND\" : \"OR\";\n const combinedQueries = combineQueries(\n value.map((v) => constructMetadataFilter(v)),\n operation\n );\n\n return combinedQueries;\n } else {\n return handleFieldFilter(key, value);\n }\n } else if (entries.length > 1) {\n for (const [key] of entries) {\n if (key.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${key}`\n );\n }\n }\n\n const and_multiple = collectParams(\n entries.map(([field, val], index) =>\n handleFieldFilter(field, val, index + 1)\n )\n );\n\n if (and_multiple.length >= 1) {\n return [and_multiple[0].join(\" AND \"), and_multiple[1]];\n } else {\n throw Error(\n \"Invalid filter condition. Expected a dictionary but got an empty dictionary\"\n );\n }\n } else {\n throw new Error(\"Filter condition contains no entries.\");\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"neo4j_vector.cjs","names":["VectorStore","neo4j","Document","uuid"],"sources":["../../src/vectorstores/neo4j_vector.ts"],"sourcesContent":["import neo4j, {\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport * as uuid from \"@langchain/core/utils/uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport type SearchType = \"vector\" | \"hybrid\";\n\nexport type IndexType = \"NODE\" | \"RELATIONSHIP\";\n\nexport type DistanceStrategy = \"euclidean\" | \"cosine\";\n\nexport type Metadata = Record<string, unknown>;\n\nexport interface Neo4jVectorStoreArgs {\n url: string;\n username: string;\n password: string;\n database?: string;\n preDeleteCollection?: boolean;\n textNodeProperty?: string;\n textNodeProperties?: string[];\n embeddingNodeProperty?: string;\n keywordIndexName?: string;\n indexName?: string;\n searchType?: SearchType;\n indexType?: IndexType;\n retrievalQuery?: string;\n nodeLabel?: string;\n createIdIndex?: boolean;\n}\n\nconst DEFAULT_SEARCH_TYPE = \"vector\";\nconst DEFAULT_INDEX_TYPE = \"NODE\";\nconst DEFAULT_DISTANCE_STRATEGY = \"cosine\";\nconst DEFAULT_NODE_EMBEDDING_PROPERTY = \"embedding\";\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jVectorStore extends VectorStore {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private preDeleteCollection: boolean;\n\n private nodeLabel: string;\n\n private embeddingNodeProperty: string;\n\n private embeddingDimension: number;\n\n private textNodeProperty: string;\n\n private keywordIndexName: string;\n\n private indexName: string;\n\n private retrievalQuery: string;\n\n private searchType: SearchType;\n\n private indexType: IndexType;\n\n private distanceStrategy: DistanceStrategy = DEFAULT_DISTANCE_STRATEGY;\n\n private supportMetadataFilter = true;\n\n private isEnterprise = false;\n\n _vectorstoreType(): string {\n return \"neo4jvector\";\n }\n\n constructor(embeddings: EmbeddingsInterface, config: Neo4jVectorStoreArgs) {\n super(embeddings, config);\n }\n\n static async initialize(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const store = new Neo4jVectorStore(embeddings, config);\n await store._initializeDriver(config);\n await store._verifyConnectivity();\n\n const {\n preDeleteCollection = false,\n nodeLabel = \"Chunk\",\n textNodeProperty = \"text\",\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n keywordIndexName = \"keyword\",\n indexName = \"vector\",\n retrievalQuery = \"\",\n searchType = DEFAULT_SEARCH_TYPE,\n indexType = DEFAULT_INDEX_TYPE,\n } = config;\n\n store.embeddingDimension = (await embeddings.embedQuery(\"foo\")).length;\n store.preDeleteCollection = preDeleteCollection;\n store.nodeLabel = nodeLabel;\n store.textNodeProperty = textNodeProperty;\n store.embeddingNodeProperty = embeddingNodeProperty;\n store.keywordIndexName = keywordIndexName;\n store.indexName = indexName;\n store.retrievalQuery = retrievalQuery;\n store.searchType = searchType;\n store.indexType = indexType;\n\n if (store.preDeleteCollection) {\n await store._dropIndex();\n }\n\n return store;\n }\n\n async _initializeDriver({\n url,\n username,\n password,\n database = \"neo4j\",\n }: Neo4jVectorStoreArgs) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n async _verifyConnectivity() {\n await this.driver.verifyAuthentication();\n }\n\n async _verifyVersion() {\n try {\n const data = await this.query(\"CALL dbms.components()\");\n const versionString: string = data[0].versions[0];\n const targetVersion = [5, 11, 0];\n\n let version: number[];\n\n if (versionString.includes(\"aura\")) {\n const baseVersion = versionString.split(\"-\")[0];\n version = baseVersion.split(\".\").map(Number);\n version.push(0);\n } else {\n version = versionString.split(\".\").map(Number);\n }\n\n if (isVersionLessThan(version, targetVersion)) {\n throw new Error(\n \"Version index is only supported in Neo4j version 5.11 or greater\"\n );\n }\n\n const metadataTargetVersion = [5, 18, 0];\n if (isVersionLessThan(version, metadataTargetVersion)) {\n this.supportMetadataFilter = false;\n }\n\n this.isEnterprise = data[0].edition === \"enterprise\";\n } catch (error) {\n console.error(\"Database version check failed:\", error);\n }\n }\n\n async close() {\n await this.driver.close();\n }\n\n async _dropIndex() {\n try {\n await this.query(`\n MATCH (n:\\`${this.nodeLabel}\\`)\n CALL {\n WITH n\n DETACH DELETE n\n }\n IN TRANSACTIONS OF 10000 ROWS;\n `);\n await this.query(`DROP INDEX ${this.indexName}`);\n } catch (error) {\n console.error(\"An error occurred while dropping the index:\", error);\n }\n }\n\n async query(query: string, params: Any = {}): Promise<Any[]> {\n const session = this.driver.session({ database: this.database });\n const result = await session.run(query, params);\n return toObjects(result.records);\n }\n\n static async fromTexts(\n texts: string[],\n metadatas: Any,\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const docs = [];\n\n for (let i = 0; i < texts.length; i += 1) {\n const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n const newDoc = new Document({\n pageContent: texts[i],\n metadata,\n });\n docs.push(newDoc);\n }\n\n return Neo4jVectorStore.fromDocuments(docs, embeddings, config);\n }\n\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const {\n searchType = DEFAULT_SEARCH_TYPE,\n createIdIndex = true,\n textNodeProperties = [],\n } = config;\n\n const store = await this.initialize(embeddings, config);\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name \"${store.indexName}\" already exists. The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n if (createIdIndex) {\n await store.query(\n `CREATE CONSTRAINT IF NOT EXISTS FOR (n:${store.nodeLabel}) REQUIRE n.id IS UNIQUE;`\n );\n }\n\n await store.addDocuments(docs);\n\n return store;\n }\n\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const { searchType = DEFAULT_SEARCH_TYPE, keywordIndexName = \"keyword\" } =\n config;\n\n if (searchType === \"hybrid\" && !keywordIndexName) {\n throw Error(\n \"keyword_index name has to be specified when using hybrid search option\"\n );\n }\n\n const store = await this.initialize(embeddings, config);\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n throw Error(\n \"The specified vector index name does not exist. Make sure to check if you spelled it correctly\"\n );\n }\n\n if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n throw Error(\n \"The specified keyword index name does not exist. Make sure to check if you spelled it correctly\"\n );\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n return store;\n }\n\n static async fromExistingGraph(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const {\n textNodeProperties = [],\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n searchType = DEFAULT_SEARCH_TYPE,\n retrievalQuery = \"\",\n nodeLabel,\n } = config;\n\n let _retrievalQuery = retrievalQuery;\n\n if (textNodeProperties.length === 0) {\n throw Error(\n \"Parameter `text_node_properties` must not be an empty array\"\n );\n }\n\n if (!retrievalQuery) {\n _retrievalQuery = `\n RETURN reduce(str='', k IN ${JSON.stringify(textNodeProperties)} |\n str + '\\\\n' + k + ': ' + coalesce(node[k], '')) AS text,\n node {.*, \\`${embeddingNodeProperty}\\`: Null, id: Null, ${textNodeProperties\n .map((prop) => `\\`${prop}\\`: Null`)\n .join(\", \")} } AS metadata, score\n `;\n }\n\n const store = await this.initialize(embeddings, {\n ...config,\n retrievalQuery: _retrievalQuery,\n });\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name ${store.indexName} already exists. The provided embedding function and vector index dimensions do not match.\\nEmbedding function dimension: ${store.embeddingDimension}\\nVector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel =\n await store.retrieveExistingFtsIndex(textNodeProperties);\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n while (true) {\n const fetchQuery = `\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE n.${embeddingNodeProperty} IS null\n AND any(k in $props WHERE n[k] IS NOT null)\n RETURN elementId(n) AS id, reduce(str='', k IN $props |\n str + '\\\\n' + k + ':' + coalesce(n[k], '')) AS text\n LIMIT 1000\n `;\n\n const data = await store.query(fetchQuery, {\n props: textNodeProperties,\n });\n\n if (!data) {\n break;\n }\n\n const textEmbeddings = await embeddings.embedDocuments(\n data.map((el) => el.text)\n );\n\n const params = {\n data: data.map((el, index) => ({\n id: el.id,\n embedding: textEmbeddings[index],\n })),\n };\n\n await store.query(\n `\n UNWIND $data AS row\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE elementId(n) = row.id\n CALL db.create.setVectorProperty(n, '${embeddingNodeProperty}', row.embedding)\n YIELD node RETURN count(*)\n `,\n params\n );\n\n if (data.length < 1000) {\n break;\n }\n }\n\n return store;\n }\n\n async createNewIndex(): Promise<void> {\n const indexQuery = `\n CALL db.index.vector.createNodeIndex(\n $index_name,\n $node_label,\n $embedding_node_property,\n toInteger($embedding_dimension),\n $similarity_metric\n )\n `;\n\n const parameters = {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n embedding_dimension: this.embeddingDimension,\n similarity_metric: this.distanceStrategy,\n };\n\n await this.query(indexQuery, parameters);\n }\n\n async retrieveExistingIndex() {\n let indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'VECTOR' AND (name = $index_name\n OR (labelsOrTypes[0] = $node_label AND\n properties[0] = $embedding_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n }\n );\n\n if (indexInformation) {\n indexInformation = this.sortByIndexName(indexInformation, this.indexName);\n\n try {\n const [index] = indexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.indexName = index.name;\n this.nodeLabel = labelOrType;\n this.embeddingNodeProperty = property;\n\n const embeddingDimension =\n index.options.indexConfig[\"vector.dimensions\"];\n return Number(embeddingDimension);\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async retrieveExistingFtsIndex(\n textNodeProperties: string[] = []\n ): Promise<string | null> {\n const indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'FULLTEXT' AND (name = $keyword_index_name\n OR (labelsOrTypes = [$node_label] AND\n properties = $text_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n keyword_index_name: this.keywordIndexName,\n node_label: this.nodeLabel,\n text_node_property:\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty],\n }\n );\n\n if (indexInformation) {\n const sortedIndexInformation = this.sortByIndexName(\n indexInformation,\n this.indexName\n );\n\n try {\n const [index] = sortedIndexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.keywordIndexName = index.name;\n this.textNodeProperty = property;\n this.nodeLabel = labelOrType;\n\n return labelOrType;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async createNewKeywordIndex(\n textNodeProperties: string[] = []\n ): Promise<void> {\n const nodeProps =\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty];\n\n const ftsIndexQuery = `\n CREATE FULLTEXT INDEX ${this.keywordIndexName}\n FOR (n:\\`${this.nodeLabel}\\`) ON EACH\n [${nodeProps.map((prop) => `n.\\`${prop}\\``).join(\", \")}]\n `;\n\n await this.query(ftsIndexQuery);\n }\n\n sortByIndexName(\n values: Array<{ [key: string]: Any }>,\n indexName: string\n ): Array<{ [key: string]: Any }> {\n return values.sort(\n (a, b) =>\n (a.name === indexName ? -1 : 0) - (b.name === indexName ? -1 : 0)\n );\n }\n\n async addVectors(\n vectors: number[][],\n documents: Document[],\n metadatas?: Record<string, Any>[],\n ids?: string[]\n ): Promise<string[]> {\n let _ids = ids;\n const _metadatas = metadatas;\n\n if (!_ids) {\n _ids = documents.map(() => uuid.v1());\n }\n\n const importQuery = `\n UNWIND $data AS row\n CALL {\n WITH row\n MERGE (c:\\`${this.nodeLabel}\\` {id: row.id})\n WITH c, row\n CALL db.create.setVectorProperty(c, '${this.embeddingNodeProperty}', row.embedding)\n YIELD node\n SET c.\\`${this.textNodeProperty}\\` = row.text\n SET c += row.metadata\n } IN TRANSACTIONS OF 1000 ROWS\n `;\n\n const parameters = {\n data: documents.map(({ pageContent, metadata }, index) => ({\n text: pageContent,\n metadata: _metadatas ? _metadatas[index] : metadata,\n embedding: vectors[index],\n id: _ids ? _ids[index] : null,\n })),\n };\n\n await this.query(importQuery, parameters);\n\n return _ids;\n }\n\n async addDocuments(documents: Document[]): Promise<string[]> {\n const texts = documents.map(({ pageContent }) => pageContent);\n\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents\n );\n }\n\n async similaritySearch(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<Document[]> {\n const embedding = await this.embeddings.embedQuery(query);\n\n const results = await this.similaritySearchVectorWithScore(\n embedding,\n k,\n query,\n params\n );\n\n return results.map((result) => result[0]);\n }\n\n async similaritySearchWithScore(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n const embedding = await this.embeddings.embedQuery(query);\n return this.similaritySearchVectorWithScore(embedding, k, query, params);\n }\n\n async similaritySearchVectorWithScore(\n vector: number[],\n k: number,\n query: string,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n let indexQuery: string;\n let filterParams: Record<string, Any>;\n\n const { filter } = params;\n\n if (filter) {\n if (!this.supportMetadataFilter) {\n throw new Error(\n \"Metadata filtering is only supported in Neo4j version 5.18 or greater.\"\n );\n }\n\n if (this.searchType === \"hybrid\") {\n throw new Error(\n \"Metadata filtering can't be use in combination with a hybrid search approach.\"\n );\n }\n\n const parallelQuery = this.isEnterprise\n ? \"CYPHER runtime = parallel parallelRuntimeSupport=all \"\n : \"\";\n\n const baseIndexQuery = `\n ${parallelQuery}\n MATCH (n:\\`${this.nodeLabel}\\`)\n WHERE n.\\`${this.embeddingNodeProperty}\\` IS NOT NULL\n AND size(n.\\`${this.embeddingNodeProperty}\\`) = toInteger(${this.embeddingDimension}) AND\n `;\n\n const baseCosineQuery = `\n WITH n as node, vector.similarity.cosine(\n n.\\`${this.embeddingNodeProperty}\\`,\n $embedding\n ) AS score ORDER BY score DESC LIMIT toInteger($k)\n `;\n const [fSnippets, fParams] = constructMetadataFilter(filter);\n\n indexQuery = baseIndexQuery + fSnippets + baseCosineQuery;\n filterParams = fParams;\n } else {\n indexQuery = getSearchIndexQuery(this.searchType, this.indexType);\n filterParams = {};\n }\n\n let defaultRetrieval: string;\n\n if (this.indexType === \"RELATIONSHIP\") {\n defaultRetrieval = `\n RETURN relationship.${this.textNodeProperty} AS text, score,\n relationship {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n } else {\n defaultRetrieval = `\n RETURN node.${this.textNodeProperty} AS text, score,\n node {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n }\n\n const retrievalQuery = this.retrievalQuery\n ? this.retrievalQuery\n : defaultRetrieval;\n const readQuery = `${indexQuery} ${retrievalQuery}`;\n\n const parameters = {\n index: this.indexName,\n k: Number(k),\n embedding: vector,\n keyword_index: this.keywordIndexName,\n query: removeLuceneChars(query),\n ...params,\n ...filterParams,\n };\n\n const results = await this.query(readQuery, parameters);\n\n if (results) {\n if (results.some((result) => result.text == null)) {\n if (!this.retrievalQuery) {\n throw new Error(\n `Make sure that none of the '${this.textNodeProperty}' properties on nodes with label '${this.nodeLabel}' are missing or empty`\n );\n } else {\n throw new Error(\n \"Inspect the 'retrievalQuery' and ensure it doesn't return null for the 'text' column\"\n );\n }\n }\n\n const docs: [Document, number][] = results.map((result: Any) => [\n new Document({\n pageContent: result.text,\n metadata: Object.fromEntries(\n Object.entries(result.metadata).filter(([_, v]) => v !== null)\n ),\n }),\n result.score,\n ]);\n\n return docs;\n }\n\n return [];\n }\n}\n\nfunction toObjects(records: Neo4jRecord[]) {\n const recordValues: Record<string, Any>[] = records.map((record) => {\n const rObj = record.toObject();\n const out: { [key: string]: Any } = {};\n Object.keys(rObj).forEach((key) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out;\n });\n return recordValues;\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nfunction extractPathForRows(path: Neo4jPath) {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n}\n\nfunction getSearchIndexQuery(\n searchType: SearchType,\n indexType: IndexType = DEFAULT_INDEX_TYPE\n): string {\n if (indexType === \"NODE\") {\n const typeToQueryMap: { [key in SearchType]: string } = {\n vector:\n \"CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\",\n hybrid: `\n CALL {\n CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\n WITH collect({node:node, score:score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n // We use 0 as min\n RETURN n.node AS node, (n.score / max) AS score UNION\n CALL db.index.fulltext.queryNodes($keyword_index, $query, {limit: $k}) YIELD node, score\n WITH collect({node: node, score: score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n RETURN n.node AS node, (n.score / max) AS score\n }\n WITH node, max(score) AS score ORDER BY score DESC LIMIT toInteger($k)\n `,\n };\n\n return typeToQueryMap[searchType];\n } else {\n return `\n CALL db.index.vector.queryRelationships($index, $k, $embedding)\n YIELD relationship, score\n `;\n }\n}\n\nexport function removeLuceneChars(text: string | null) {\n if (text === undefined || text === null) {\n return null;\n }\n\n const specialChars = [\n \"+\",\n \"-\",\n \"&\",\n \"|\",\n \"!\",\n \"(\",\n \")\",\n \"{\",\n \"}\",\n \"[\",\n \"]\",\n \"^\",\n '\"',\n \"~\",\n \"*\",\n \"?\",\n \":\",\n \"\\\\\",\n ];\n let modifiedText = text;\n for (const char of specialChars) {\n modifiedText = modifiedText.split(char).join(\" \");\n }\n return modifiedText.trim();\n}\n\nexport function isVersionLessThan(v1: number[], v2: number[]): boolean {\n for (let i = 0; i < Math.min(v1.length, v2.length); i += 1) {\n if (v1[i] < v2[i]) {\n return true;\n } else if (v1[i] > v2[i]) {\n return false;\n }\n }\n return v1.length < v2.length;\n}\n\nconst COMPARISONS_TO_NATIVE: Record<string, string> = {\n $eq: \"=\",\n $ne: \"<>\",\n $lt: \"<\",\n $lte: \"<=\",\n $gt: \">\",\n $gte: \">=\",\n};\n\nconst COMPARISONS_TO_NATIVE_OPERATORS = new Set(\n Object.keys(COMPARISONS_TO_NATIVE)\n);\n\nconst TEXT_OPERATORS = new Set([\"$like\", \"$ilike\"]);\n\nconst LOGICAL_OPERATORS = new Set([\"$and\", \"$or\"]);\n\nconst SPECIAL_CASED_OPERATORS = new Set([\"$in\", \"$nin\", \"$between\"]);\n\nconst SUPPORTED_OPERATORS = new Set([\n ...COMPARISONS_TO_NATIVE_OPERATORS,\n ...TEXT_OPERATORS,\n ...LOGICAL_OPERATORS,\n ...SPECIAL_CASED_OPERATORS,\n]);\n\nconst IS_IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction combineQueries(\n inputQueries: [string, Record<string, Any>][],\n operator: string\n): [string, Record<string, Any>] {\n let combinedQuery = \"\";\n const combinedParams: Record<string, Any> = {};\n const paramCounter: Record<string, number> = {};\n\n for (const [query, params] of inputQueries) {\n let newQuery = query;\n for (const [param, value] of Object.entries(params)) {\n if (param in paramCounter) {\n paramCounter[param] += 1;\n } else {\n paramCounter[param] = 1;\n }\n const newParamName = `${param}_${paramCounter[param]}`;\n\n newQuery = newQuery.replace(`$${param}`, `$${newParamName}`);\n combinedParams[newParamName] = value;\n }\n\n if (combinedQuery) {\n combinedQuery += ` ${operator} `;\n }\n combinedQuery += `(${newQuery})`;\n }\n\n return [combinedQuery, combinedParams];\n}\n\nfunction collectParams(\n inputData: [string, Record<string, string>][]\n): [string[], Record<string, Any>] {\n const queryParts: string[] = [];\n const params: Record<string, Any> = {};\n\n for (const [queryPart, param] of inputData) {\n queryParts.push(queryPart);\n Object.assign(params, param);\n }\n\n return [queryParts, params];\n}\n\nfunction handleFieldFilter(\n field: string,\n value: Any,\n paramNumber = 1\n): [string, Record<string, Any>] {\n if (typeof field !== \"string\") {\n throw new Error(\n `field should be a string but got: ${typeof field} with value: ${field}`\n );\n }\n\n if (field.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${field}`\n );\n }\n\n if (!IS_IDENTIFIER_REGEX.test(field)) {\n throw new Error(\n `Invalid field name: ${field}. Expected a valid identifier.`\n );\n }\n\n let operator: string;\n let filterValue: Any;\n\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n const keys = Object.keys(value);\n\n if (keys.length !== 1) {\n throw new Error(`Invalid filter condition. Expected a value which is a dictionary\n with a single key that corresponds to an operator but got a dictionary\n with ${keys.length} keys. The first few keys are: ${keys\n .slice(0, 3)\n .join(\", \")}\n `);\n }\n\n operator = keys[0];\n filterValue = value[operator];\n\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw new Error(\n `Invalid operator: ${operator}. Expected one of ${SUPPORTED_OPERATORS}`\n );\n }\n } else {\n operator = \"$eq\";\n filterValue = value;\n }\n\n if (COMPARISONS_TO_NATIVE_OPERATORS.has(operator)) {\n const native = COMPARISONS_TO_NATIVE[operator];\n const querySnippet = `n.${field} ${native} $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n\n return [querySnippet, queryParam];\n } else if (operator === \"$between\") {\n const [low, high] = filterValue;\n const querySnippet = `$param_${paramNumber}_low <= n.${field} <= $param_${paramNumber}_high`;\n const queryParam = {\n [`param_${paramNumber}_low`]: low,\n [`param_${paramNumber}_high`]: high,\n };\n\n return [querySnippet, queryParam];\n } else if ([\"$in\", \"$nin\", \"$like\", \"$ilike\"].includes(operator)) {\n if ([\"$in\", \"$nin\"].includes(operator)) {\n filterValue.forEach((val: Any) => {\n if (\n typeof val !== \"string\" &&\n typeof val !== \"number\" &&\n typeof val !== \"boolean\"\n ) {\n throw new Error(`Unsupported type: ${typeof val} for value: ${val}`);\n }\n });\n }\n\n if (operator === \"$in\") {\n const querySnippet = `n.${field} IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$nin\") {\n const querySnippet = `n.${field} NOT IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$like\") {\n const querySnippet = `n.${field} CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else if (operator === \"$ilike\") {\n const querySnippet = `toLower(n.${field}) CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else {\n throw new Error(\"Not Implemented\");\n }\n } else {\n throw new Error(\"Not Implemented\");\n }\n}\n\nexport function constructMetadataFilter(\n filter: Record<string, Any>\n): [string, Record<string, Any>] {\n if (typeof filter !== \"object\" || filter === null) {\n throw new Error(\"Expected a dictionary representing the filter condition.\");\n }\n\n const entries = Object.entries(filter);\n\n if (entries.length === 1) {\n const [key, value] = entries[0];\n\n if (key.startsWith(\"$\")) {\n if (![\"$and\", \"$or\"].includes(key.toLowerCase())) {\n throw new Error(\n `Invalid filter condition. Expected $and or $or but got: ${key}`\n );\n }\n\n if (!Array.isArray(value)) {\n throw new Error(\n `Expected an array for logical conditions, but got ${typeof value} for value: ${value}`\n );\n }\n\n const operation = key.toLowerCase() === \"$and\" ? \"AND\" : \"OR\";\n const combinedQueries = combineQueries(\n value.map((v) => constructMetadataFilter(v)),\n operation\n );\n\n return combinedQueries;\n } else {\n return handleFieldFilter(key, value);\n }\n } else if (entries.length > 1) {\n for (const [key] of entries) {\n if (key.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${key}`\n );\n }\n }\n\n const and_multiple = collectParams(\n entries.map(([field, val], index) =>\n handleFieldFilter(field, val, index + 1)\n )\n );\n\n if (and_multiple.length >= 1) {\n return [and_multiple[0].join(\" AND \"), and_multiple[1]];\n } else {\n throw Error(\n \"Invalid filter condition. Expected a dictionary but got an empty dictionary\"\n );\n }\n } else {\n throw new Error(\"Filter condition contains no entries.\");\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neo4j_vector.js","names":[],"sources":["../../src/vectorstores/neo4j_vector.ts"],"sourcesContent":["import neo4j, {\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport * as uuid from \"@langchain/core/utils/uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport type SearchType = \"vector\" | \"hybrid\";\n\nexport type IndexType = \"NODE\" | \"RELATIONSHIP\";\n\nexport type DistanceStrategy = \"euclidean\" | \"cosine\";\n\nexport type Metadata = Record<string, unknown>;\n\nexport interface Neo4jVectorStoreArgs {\n url: string;\n username: string;\n password: string;\n database?: string;\n preDeleteCollection?: boolean;\n textNodeProperty?: string;\n textNodeProperties?: string[];\n embeddingNodeProperty?: string;\n keywordIndexName?: string;\n indexName?: string;\n searchType?: SearchType;\n indexType?: IndexType;\n retrievalQuery?: string;\n nodeLabel?: string;\n createIdIndex?: boolean;\n}\n\nconst DEFAULT_SEARCH_TYPE = \"vector\";\nconst DEFAULT_INDEX_TYPE = \"NODE\";\nconst DEFAULT_DISTANCE_STRATEGY = \"cosine\";\nconst DEFAULT_NODE_EMBEDDING_PROPERTY = \"embedding\";\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jVectorStore extends VectorStore {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private preDeleteCollection: boolean;\n\n private nodeLabel: string;\n\n private embeddingNodeProperty: string;\n\n private embeddingDimension: number;\n\n private textNodeProperty: string;\n\n private keywordIndexName: string;\n\n private indexName: string;\n\n private retrievalQuery: string;\n\n private searchType: SearchType;\n\n private indexType: IndexType;\n\n private distanceStrategy: DistanceStrategy = DEFAULT_DISTANCE_STRATEGY;\n\n private supportMetadataFilter = true;\n\n private isEnterprise = false;\n\n _vectorstoreType(): string {\n return \"neo4jvector\";\n }\n\n constructor(embeddings: EmbeddingsInterface, config: Neo4jVectorStoreArgs) {\n super(embeddings, config);\n }\n\n static async initialize(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const store = new Neo4jVectorStore(embeddings, config);\n await store._initializeDriver(config);\n await store._verifyConnectivity();\n\n const {\n preDeleteCollection = false,\n nodeLabel = \"Chunk\",\n textNodeProperty = \"text\",\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n keywordIndexName = \"keyword\",\n indexName = \"vector\",\n retrievalQuery = \"\",\n searchType = DEFAULT_SEARCH_TYPE,\n indexType = DEFAULT_INDEX_TYPE,\n } = config;\n\n store.embeddingDimension = (await embeddings.embedQuery(\"foo\")).length;\n store.preDeleteCollection = preDeleteCollection;\n store.nodeLabel = nodeLabel;\n store.textNodeProperty = textNodeProperty;\n store.embeddingNodeProperty = embeddingNodeProperty;\n store.keywordIndexName = keywordIndexName;\n store.indexName = indexName;\n store.retrievalQuery = retrievalQuery;\n store.searchType = searchType;\n store.indexType = indexType;\n\n if (store.preDeleteCollection) {\n await store._dropIndex();\n }\n\n return store;\n }\n\n async _initializeDriver({\n url,\n username,\n password,\n database = \"neo4j\",\n }: Neo4jVectorStoreArgs) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n async _verifyConnectivity() {\n await this.driver.verifyAuthentication();\n }\n\n async _verifyVersion() {\n try {\n const data = await this.query(\"CALL dbms.components()\");\n const versionString: string = data[0].versions[0];\n const targetVersion = [5, 11, 0];\n\n let version: number[];\n\n if (versionString.includes(\"aura\")) {\n const baseVersion = versionString.split(\"-\")[0];\n version = baseVersion.split(\".\").map(Number);\n version.push(0);\n } else {\n version = versionString.split(\".\").map(Number);\n }\n\n if (isVersionLessThan(version, targetVersion)) {\n throw new Error(\n \"Version index is only supported in Neo4j version 5.11 or greater\"\n );\n }\n\n const metadataTargetVersion = [5, 18, 0];\n if (isVersionLessThan(version, metadataTargetVersion)) {\n this.supportMetadataFilter = false;\n }\n\n this.isEnterprise = data[0].edition === \"enterprise\";\n } catch (error) {\n console.error(\"Database version check failed:\", error);\n }\n }\n\n async close() {\n await this.driver.close();\n }\n\n async _dropIndex() {\n try {\n await this.query(`\n MATCH (n:\\`${this.nodeLabel}\\`)\n CALL {\n WITH n\n DETACH DELETE n\n }\n IN TRANSACTIONS OF 10000 ROWS;\n `);\n await this.query(`DROP INDEX ${this.indexName}`);\n } catch (error) {\n console.error(\"An error occurred while dropping the index:\", error);\n }\n }\n\n async query(query: string, params: Any = {}): Promise<Any[]> {\n const session = this.driver.session({ database: this.database });\n const result = await session.run(query, params);\n return toObjects(result.records);\n }\n\n static async fromTexts(\n texts: string[],\n metadatas: Any,\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const docs = [];\n\n for (let i = 0; i < texts.length; i += 1) {\n const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n const newDoc = new Document({\n pageContent: texts[i],\n metadata,\n });\n docs.push(newDoc);\n }\n\n return Neo4jVectorStore.fromDocuments(docs, embeddings, config);\n }\n\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const {\n searchType = DEFAULT_SEARCH_TYPE,\n createIdIndex = true,\n textNodeProperties = [],\n } = config;\n\n const store = await this.initialize(embeddings, config);\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name \"${store.indexName}\" already exists. The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n if (createIdIndex) {\n await store.query(\n `CREATE CONSTRAINT IF NOT EXISTS FOR (n:${store.nodeLabel}) REQUIRE n.id IS UNIQUE;`\n );\n }\n\n await store.addDocuments(docs);\n\n return store;\n }\n\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const { searchType = DEFAULT_SEARCH_TYPE, keywordIndexName = \"keyword\" } =\n config;\n\n if (searchType === \"hybrid\" && !keywordIndexName) {\n throw Error(\n \"keyword_index name has to be specified when using hybrid search option\"\n );\n }\n\n const store = await this.initialize(embeddings, config);\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n throw Error(\n \"The specified vector index name does not exist. Make sure to check if you spelled it correctly\"\n );\n }\n\n if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n throw Error(\n \"The specified keyword index name does not exist. Make sure to check if you spelled it correctly\"\n );\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n return store;\n }\n\n static async fromExistingGraph(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const {\n textNodeProperties = [],\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n searchType = DEFAULT_SEARCH_TYPE,\n retrievalQuery = \"\",\n nodeLabel,\n } = config;\n\n let _retrievalQuery = retrievalQuery;\n\n if (textNodeProperties.length === 0) {\n throw Error(\n \"Parameter `text_node_properties` must not be an empty array\"\n );\n }\n\n if (!retrievalQuery) {\n _retrievalQuery = `\n RETURN reduce(str='', k IN ${JSON.stringify(textNodeProperties)} |\n str + '\\\\n' + k + ': ' + coalesce(node[k], '')) AS text,\n node {.*, \\`${embeddingNodeProperty}\\`: Null, id: Null, ${textNodeProperties\n .map((prop) => `\\`${prop}\\`: Null`)\n .join(\", \")} } AS metadata, score\n `;\n }\n\n const store = await this.initialize(embeddings, {\n ...config,\n retrievalQuery: _retrievalQuery,\n });\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name ${store.indexName} already exists. The provided embedding function and vector index dimensions do not match.\\nEmbedding function dimension: ${store.embeddingDimension}\\nVector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel =\n await store.retrieveExistingFtsIndex(textNodeProperties);\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n while (true) {\n const fetchQuery = `\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE n.${embeddingNodeProperty} IS null\n AND any(k in $props WHERE n[k] IS NOT null)\n RETURN elementId(n) AS id, reduce(str='', k IN $props |\n str + '\\\\n' + k + ':' + coalesce(n[k], '')) AS text\n LIMIT 1000\n `;\n\n const data = await store.query(fetchQuery, {\n props: textNodeProperties,\n });\n\n if (!data) {\n break;\n }\n\n const textEmbeddings = await embeddings.embedDocuments(\n data.map((el) => el.text)\n );\n\n const params = {\n data: data.map((el, index) => ({\n id: el.id,\n embedding: textEmbeddings[index],\n })),\n };\n\n await store.query(\n `\n UNWIND $data AS row\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE elementId(n) = row.id\n CALL db.create.setVectorProperty(n, '${embeddingNodeProperty}', row.embedding)\n YIELD node RETURN count(*)\n `,\n params\n );\n\n if (data.length < 1000) {\n break;\n }\n }\n\n return store;\n }\n\n async createNewIndex(): Promise<void> {\n const indexQuery = `\n CALL db.index.vector.createNodeIndex(\n $index_name,\n $node_label,\n $embedding_node_property,\n toInteger($embedding_dimension),\n $similarity_metric\n )\n `;\n\n const parameters = {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n embedding_dimension: this.embeddingDimension,\n similarity_metric: this.distanceStrategy,\n };\n\n await this.query(indexQuery, parameters);\n }\n\n async retrieveExistingIndex() {\n let indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'VECTOR' AND (name = $index_name\n OR (labelsOrTypes[0] = $node_label AND\n properties[0] = $embedding_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n }\n );\n\n if (indexInformation) {\n indexInformation = this.sortByIndexName(indexInformation, this.indexName);\n\n try {\n const [index] = indexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.indexName = index.name;\n this.nodeLabel = labelOrType;\n this.embeddingNodeProperty = property;\n\n const embeddingDimension =\n index.options.indexConfig[\"vector.dimensions\"];\n return Number(embeddingDimension);\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async retrieveExistingFtsIndex(\n textNodeProperties: string[] = []\n ): Promise<string | null> {\n const indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'FULLTEXT' AND (name = $keyword_index_name\n OR (labelsOrTypes = [$node_label] AND\n properties = $text_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n keyword_index_name: this.keywordIndexName,\n node_label: this.nodeLabel,\n text_node_property:\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty],\n }\n );\n\n if (indexInformation) {\n const sortedIndexInformation = this.sortByIndexName(\n indexInformation,\n this.indexName\n );\n\n try {\n const [index] = sortedIndexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.keywordIndexName = index.name;\n this.textNodeProperty = property;\n this.nodeLabel = labelOrType;\n\n return labelOrType;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async createNewKeywordIndex(\n textNodeProperties: string[] = []\n ): Promise<void> {\n const nodeProps =\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty];\n\n const ftsIndexQuery = `\n CREATE FULLTEXT INDEX ${this.keywordIndexName}\n FOR (n:\\`${this.nodeLabel}\\`) ON EACH\n [${nodeProps.map((prop) => `n.\\`${prop}\\``).join(\", \")}]\n `;\n\n await this.query(ftsIndexQuery);\n }\n\n sortByIndexName(\n values: Array<{ [key: string]: Any }>,\n indexName: string\n ): Array<{ [key: string]: Any }> {\n return values.sort(\n (a, b) =>\n (a.name === indexName ? -1 : 0) - (b.name === indexName ? -1 : 0)\n );\n }\n\n async addVectors(\n vectors: number[][],\n documents: Document[],\n metadatas?: Record<string, Any>[],\n ids?: string[]\n ): Promise<string[]> {\n let _ids = ids;\n const _metadatas = metadatas;\n\n if (!_ids) {\n _ids = documents.map(() => uuid.v1());\n }\n\n const importQuery = `\n UNWIND $data AS row\n CALL {\n WITH row\n MERGE (c:\\`${this.nodeLabel}\\` {id: row.id})\n WITH c, row\n CALL db.create.setVectorProperty(c, '${this.embeddingNodeProperty}', row.embedding)\n YIELD node\n SET c.\\`${this.textNodeProperty}\\` = row.text\n SET c += row.metadata\n } IN TRANSACTIONS OF 1000 ROWS\n `;\n\n const parameters = {\n data: documents.map(({ pageContent, metadata }, index) => ({\n text: pageContent,\n metadata: _metadatas ? _metadatas[index] : metadata,\n embedding: vectors[index],\n id: _ids ? _ids[index] : null,\n })),\n };\n\n await this.query(importQuery, parameters);\n\n return _ids;\n }\n\n async addDocuments(documents: Document[]): Promise<string[]> {\n const texts = documents.map(({ pageContent }) => pageContent);\n\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents\n );\n }\n\n async similaritySearch(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<Document[]> {\n const embedding = await this.embeddings.embedQuery(query);\n\n const results = await this.similaritySearchVectorWithScore(\n embedding,\n k,\n query,\n params\n );\n\n return results.map((result) => result[0]);\n }\n\n async similaritySearchWithScore(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n const embedding = await this.embeddings.embedQuery(query);\n return this.similaritySearchVectorWithScore(embedding, k, query, params);\n }\n\n async similaritySearchVectorWithScore(\n vector: number[],\n k: number,\n query: string,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n let indexQuery: string;\n let filterParams: Record<string, Any>;\n\n const { filter } = params;\n\n if (filter) {\n if (!this.supportMetadataFilter) {\n throw new Error(\n \"Metadata filtering is only supported in Neo4j version 5.18 or greater.\"\n );\n }\n\n if (this.searchType === \"hybrid\") {\n throw new Error(\n \"Metadata filtering can't be use in combination with a hybrid search approach.\"\n );\n }\n\n const parallelQuery = this.isEnterprise\n ? \"CYPHER runtime = parallel parallelRuntimeSupport=all \"\n : \"\";\n\n const baseIndexQuery = `\n ${parallelQuery}\n MATCH (n:\\`${this.nodeLabel}\\`)\n WHERE n.\\`${this.embeddingNodeProperty}\\` IS NOT NULL\n AND size(n.\\`${this.embeddingNodeProperty}\\`) = toInteger(${this.embeddingDimension}) AND\n `;\n\n const baseCosineQuery = `\n WITH n as node, vector.similarity.cosine(\n n.\\`${this.embeddingNodeProperty}\\`,\n $embedding\n ) AS score ORDER BY score DESC LIMIT toInteger($k)\n `;\n const [fSnippets, fParams] = constructMetadataFilter(filter);\n\n indexQuery = baseIndexQuery + fSnippets + baseCosineQuery;\n filterParams = fParams;\n } else {\n indexQuery = getSearchIndexQuery(this.searchType, this.indexType);\n filterParams = {};\n }\n\n let defaultRetrieval: string;\n\n if (this.indexType === \"RELATIONSHIP\") {\n defaultRetrieval = `\n RETURN relationship.${this.textNodeProperty} AS text, score,\n relationship {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n } else {\n defaultRetrieval = `\n RETURN node.${this.textNodeProperty} AS text, score,\n node {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n }\n\n const retrievalQuery = this.retrievalQuery\n ? this.retrievalQuery\n : defaultRetrieval;\n const readQuery = `${indexQuery} ${retrievalQuery}`;\n\n const parameters = {\n index: this.indexName,\n k: Number(k),\n embedding: vector,\n keyword_index: this.keywordIndexName,\n query: removeLuceneChars(query),\n ...params,\n ...filterParams,\n };\n\n const results = await this.query(readQuery, parameters);\n\n if (results) {\n if (results.some((result) => result.text == null)) {\n if (!this.retrievalQuery) {\n throw new Error(\n `Make sure that none of the '${this.textNodeProperty}' properties on nodes with label '${this.nodeLabel}' are missing or empty`\n );\n } else {\n throw new Error(\n \"Inspect the 'retrievalQuery' and ensure it doesn't return null for the 'text' column\"\n );\n }\n }\n\n const docs: [Document, number][] = results.map((result: Any) => [\n new Document({\n pageContent: result.text,\n metadata: Object.fromEntries(\n Object.entries(result.metadata).filter(([_, v]) => v !== null)\n ),\n }),\n result.score,\n ]);\n\n return docs;\n }\n\n return [];\n }\n}\n\nfunction toObjects(records: Neo4jRecord[]) {\n const recordValues: Record<string, Any>[] = records.map((record) => {\n const rObj = record.toObject();\n const out: { [key: string]: Any } = {};\n Object.keys(rObj).forEach((key) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out;\n });\n return recordValues;\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nfunction extractPathForRows(path: Neo4jPath) {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n}\n\nfunction getSearchIndexQuery(\n searchType: SearchType,\n indexType: IndexType = DEFAULT_INDEX_TYPE\n): string {\n if (indexType === \"NODE\") {\n const typeToQueryMap: { [key in SearchType]: string } = {\n vector:\n \"CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\",\n hybrid: `\n CALL {\n CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\n WITH collect({node:node, score:score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n // We use 0 as min\n RETURN n.node AS node, (n.score / max) AS score UNION\n CALL db.index.fulltext.queryNodes($keyword_index, $query, {limit: $k}) YIELD node, score\n WITH collect({node: node, score: score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n RETURN n.node AS node, (n.score / max) AS score\n }\n WITH node, max(score) AS score ORDER BY score DESC LIMIT toInteger($k)\n `,\n };\n\n return typeToQueryMap[searchType];\n } else {\n return `\n CALL db.index.vector.queryRelationships($index, $k, $embedding)\n YIELD relationship, score\n `;\n }\n}\n\nexport function removeLuceneChars(text: string | null) {\n if (text === undefined || text === null) {\n return null;\n }\n\n const specialChars = [\n \"+\",\n \"-\",\n \"&\",\n \"|\",\n \"!\",\n \"(\",\n \")\",\n \"{\",\n \"}\",\n \"[\",\n \"]\",\n \"^\",\n '\"',\n \"~\",\n \"*\",\n \"?\",\n \":\",\n \"\\\\\",\n ];\n let modifiedText = text;\n for (const char of specialChars) {\n modifiedText = modifiedText.split(char).join(\" \");\n }\n return modifiedText.trim();\n}\n\nexport function isVersionLessThan(v1: number[], v2: number[]): boolean {\n for (let i = 0; i < Math.min(v1.length, v2.length); i += 1) {\n if (v1[i] < v2[i]) {\n return true;\n } else if (v1[i] > v2[i]) {\n return false;\n }\n }\n return v1.length < v2.length;\n}\n\nconst COMPARISONS_TO_NATIVE: Record<string, string> = {\n $eq: \"=\",\n $ne: \"<>\",\n $lt: \"<\",\n $lte: \"<=\",\n $gt: \">\",\n $gte: \">=\",\n};\n\nconst COMPARISONS_TO_NATIVE_OPERATORS = new Set(\n Object.keys(COMPARISONS_TO_NATIVE)\n);\n\nconst TEXT_OPERATORS = new Set([\"$like\", \"$ilike\"]);\n\nconst LOGICAL_OPERATORS = new Set([\"$and\", \"$or\"]);\n\nconst SPECIAL_CASED_OPERATORS = new Set([\"$in\", \"$nin\", \"$between\"]);\n\nconst SUPPORTED_OPERATORS = new Set([\n ...COMPARISONS_TO_NATIVE_OPERATORS,\n ...TEXT_OPERATORS,\n ...LOGICAL_OPERATORS,\n ...SPECIAL_CASED_OPERATORS,\n]);\n\nconst IS_IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction combineQueries(\n inputQueries: [string, Record<string, Any>][],\n operator: string\n): [string, Record<string, Any>] {\n let combinedQuery = \"\";\n const combinedParams: Record<string, Any> = {};\n const paramCounter: Record<string, number> = {};\n\n for (const [query, params] of inputQueries) {\n let newQuery = query;\n for (const [param, value] of Object.entries(params)) {\n if (param in paramCounter) {\n paramCounter[param] += 1;\n } else {\n paramCounter[param] = 1;\n }\n const newParamName = `${param}_${paramCounter[param]}`;\n\n newQuery = newQuery.replace(`$${param}`, `$${newParamName}`);\n combinedParams[newParamName] = value;\n }\n\n if (combinedQuery) {\n combinedQuery += ` ${operator} `;\n }\n combinedQuery += `(${newQuery})`;\n }\n\n return [combinedQuery, combinedParams];\n}\n\nfunction collectParams(\n inputData: [string, Record<string, string>][]\n): [string[], Record<string, Any>] {\n const queryParts: string[] = [];\n const params: Record<string, Any> = {};\n\n for (const [queryPart, param] of inputData) {\n queryParts.push(queryPart);\n Object.assign(params, param);\n }\n\n return [queryParts, params];\n}\n\nfunction handleFieldFilter(\n field: string,\n value: Any,\n paramNumber = 1\n): [string, Record<string, Any>] {\n if (typeof field !== \"string\") {\n throw new Error(\n `field should be a string but got: ${typeof field} with value: ${field}`\n );\n }\n\n if (field.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${field}`\n );\n }\n\n if (!IS_IDENTIFIER_REGEX.test(field)) {\n throw new Error(\n `Invalid field name: ${field}. Expected a valid identifier.`\n );\n }\n\n let operator: string;\n let filterValue: Any;\n\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n const keys = Object.keys(value);\n\n if (keys.length !== 1) {\n throw new Error(`Invalid filter condition. Expected a value which is a dictionary\n with a single key that corresponds to an operator but got a dictionary\n with ${keys.length} keys. The first few keys are: ${keys\n .slice(0, 3)\n .join(\", \")}\n `);\n }\n\n operator = keys[0];\n filterValue = value[operator];\n\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw new Error(\n `Invalid operator: ${operator}. Expected one of ${SUPPORTED_OPERATORS}`\n );\n }\n } else {\n operator = \"$eq\";\n filterValue = value;\n }\n\n if (COMPARISONS_TO_NATIVE_OPERATORS.has(operator)) {\n const native = COMPARISONS_TO_NATIVE[operator];\n const querySnippet = `n.${field} ${native} $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n\n return [querySnippet, queryParam];\n } else if (operator === \"$between\") {\n const [low, high] = filterValue;\n const querySnippet = `$param_${paramNumber}_low <= n.${field} <= $param_${paramNumber}_high`;\n const queryParam = {\n [`param_${paramNumber}_low`]: low,\n [`param_${paramNumber}_high`]: high,\n };\n\n return [querySnippet, queryParam];\n } else if ([\"$in\", \"$nin\", \"$like\", \"$ilike\"].includes(operator)) {\n if ([\"$in\", \"$nin\"].includes(operator)) {\n filterValue.forEach((val: Any) => {\n if (\n typeof val !== \"string\" &&\n typeof val !== \"number\" &&\n typeof val !== \"boolean\"\n ) {\n throw new Error(`Unsupported type: ${typeof val} for value: ${val}`);\n }\n });\n }\n\n if (operator === \"$in\") {\n const querySnippet = `n.${field} IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$nin\") {\n const querySnippet = `n.${field} NOT IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$like\") {\n const querySnippet = `n.${field} CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else if (operator === \"$ilike\") {\n const querySnippet = `toLower(n.${field}) CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else {\n throw new Error(\"Not Implemented\");\n }\n } else {\n throw new Error(\"Not Implemented\");\n }\n}\n\nexport function constructMetadataFilter(\n filter: Record<string, Any>\n): [string, Record<string, Any>] {\n if (typeof filter !== \"object\" || filter === null) {\n throw new Error(\"Expected a dictionary representing the filter condition.\");\n }\n\n const entries = Object.entries(filter);\n\n if (entries.length === 1) {\n const [key, value] = entries[0];\n\n if (key.startsWith(\"$\")) {\n if (![\"$and\", \"$or\"].includes(key.toLowerCase())) {\n throw new Error(\n `Invalid filter condition. Expected $and or $or but got: ${key}`\n );\n }\n\n if (!Array.isArray(value)) {\n throw new Error(\n `Expected an array for logical conditions, but got ${typeof value} for value: ${value}`\n );\n }\n\n const operation = key.toLowerCase() === \"$and\" ? \"AND\" : \"OR\";\n const combinedQueries = combineQueries(\n value.map((v) => constructMetadataFilter(v)),\n operation\n );\n\n return combinedQueries;\n } else {\n return handleFieldFilter(key, value);\n }\n } else if (entries.length > 1) {\n for (const [key] of entries) {\n if (key.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${key}`\n );\n }\n }\n\n const and_multiple = collectParams(\n entries.map(([field, val], index) =>\n handleFieldFilter(field, val, index + 1)\n )\n );\n\n if (and_multiple.length >= 1) {\n return [and_multiple[0].join(\" AND \"), and_multiple[1]];\n } else {\n throw Error(\n \"Invalid filter condition. Expected a dictionary but got an empty dictionary\"\n );\n }\n } else {\n throw new Error(\"Filter condition contains no entries.\");\n }\n}\n"],"mappings":";;;;;AAuCA,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAC3B,MAAM,4BAA4B;AAClC,MAAM,kCAAkC;;;;;;;;;;;;;;;AAgBxC,IAAa,mBAAb,MAAa,yBAAyB,YAAY;CAChD;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA;CAEA,mBAA6C;CAE7C,wBAAgC;CAEhC,eAAuB;CAEvB,mBAA2B;AACzB,SAAO;;CAGT,YAAY,YAAiC,QAA8B;AACzE,QAAM,YAAY,OAAO;;CAG3B,aAAa,WACX,YACA,QACA;EACA,MAAM,QAAQ,IAAI,iBAAiB,YAAY,OAAO;AACtD,QAAM,MAAM,kBAAkB,OAAO;AACrC,QAAM,MAAM,qBAAqB;EAEjC,MAAM,EACJ,sBAAsB,OACtB,YAAY,SACZ,mBAAmB,QACnB,wBAAwB,iCACxB,mBAAmB,WACnB,YAAY,UACZ,iBAAiB,IACjB,aAAa,qBACb,YAAY,uBACV;AAEJ,QAAM,sBAAsB,MAAM,WAAW,WAAW,MAAM,EAAE;AAChE,QAAM,sBAAsB;AAC5B,QAAM,YAAY;AAClB,QAAM,mBAAmB;AACzB,QAAM,wBAAwB;AAC9B,QAAM,mBAAmB;AACzB,QAAM,YAAY;AAClB,QAAM,iBAAiB;AACvB,QAAM,aAAa;AACnB,QAAM,YAAY;AAElB,MAAI,MAAM,oBACR,OAAM,MAAM,YAAY;AAG1B,SAAO;;CAGT,MAAM,kBAAkB,EACtB,KACA,UACA,UACA,WAAW,WACY;AACvB,MAAI;AACF,QAAK,SAAS,MAAM,OAAO,KAAK,MAAM,KAAK,MAAM,UAAU,SAAS,CAAC;AACrE,QAAK,WAAW;UACV;AACN,SAAM,IAAI,MACR,iFACD;;;CAIL,MAAM,sBAAsB;AAC1B,QAAM,KAAK,OAAO,sBAAsB;;CAG1C,MAAM,iBAAiB;AACrB,MAAI;GACF,MAAM,OAAO,MAAM,KAAK,MAAM,yBAAyB;GACvD,MAAM,gBAAwB,KAAK,GAAG,SAAS;GAC/C,MAAM,gBAAgB;IAAC;IAAG;IAAI;IAAE;GAEhC,IAAI;AAEJ,OAAI,cAAc,SAAS,OAAO,EAAE;AAElC,cADoB,cAAc,MAAM,IAAI,CAAC,GACvB,MAAM,IAAI,CAAC,IAAI,OAAO;AAC5C,YAAQ,KAAK,EAAE;SAEf,WAAU,cAAc,MAAM,IAAI,CAAC,IAAI,OAAO;AAGhD,OAAI,kBAAkB,SAAS,cAAc,CAC3C,OAAM,IAAI,MACR,mEACD;AAIH,OAAI,kBAAkB,SADQ;IAAC;IAAG;IAAI;IAAE,CACa,CACnD,MAAK,wBAAwB;AAG/B,QAAK,eAAe,KAAK,GAAG,YAAY;WACjC,OAAO;AACd,WAAQ,MAAM,kCAAkC,MAAM;;;CAI1D,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO;;CAG3B,MAAM,aAAa;AACjB,MAAI;AACF,SAAM,KAAK,MAAM;qBACF,KAAK,UAAU;;;;;;QAM5B;AACF,SAAM,KAAK,MAAM,cAAc,KAAK,YAAY;WACzC,OAAO;AACd,WAAQ,MAAM,+CAA+C,MAAM;;;CAIvE,MAAM,MAAM,OAAe,SAAc,EAAE,EAAkB;AAG3D,SAAO,WADQ,MADC,KAAK,OAAO,QAAQ,EAAE,UAAU,KAAK,UAAU,CAAC,CACnC,IAAI,OAAO,OAAO,EACvB,QAAQ;;CAGlC,aAAa,UACX,OACA,WACA,YACA,QAC2B;EAC3B,MAAM,OAAO,EAAE;AAEf,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;GACxC,MAAM,WAAW,MAAM,QAAQ,UAAU,GAAG,UAAU,KAAK;GAC3D,MAAM,SAAS,IAAI,SAAS;IAC1B,aAAa,MAAM;IACnB;IACD,CAAC;AACF,QAAK,KAAK,OAAO;;AAGnB,SAAO,iBAAiB,cAAc,MAAM,YAAY,OAAO;;CAGjE,aAAa,cACX,MACA,YACA,QAC2B;EAC3B,MAAM,EACJ,aAAa,qBACb,gBAAgB,MAChB,qBAAqB,EAAE,KACrB;EAEJ,MAAM,QAAQ,MAAM,KAAK,WAAW,YAAY,OAAO;EAEvD,MAAM,qBAAqB,MAAM,MAAM,uBAAuB;AAE9D,MAAI,CAAC,mBACH,OAAM,MAAM,gBAAgB;WACnB,MAAM,uBAAuB,mBACtC,OAAM,IAAI,MACR,oBAAoB,MAAM,UAAU;wCACJ,MAAM,mBAAmB;kCAC/B,qBAC3B;AAGH,MAAI,eAAe,UAAU;GAC3B,MAAM,eAAe,MAAM,MAAM,0BAA0B;AAE3D,OAAI,CAAC,aACH,OAAM,MAAM,sBAAsB,mBAAmB;YAEjD,iBAAiB,MAAM,UACzB,OAAM,MACJ,2DACD;;AAKP,MAAI,cACF,OAAM,MAAM,MACV,0CAA0C,MAAM,UAAU,2BAC3D;AAGH,QAAM,MAAM,aAAa,KAAK;AAE9B,SAAO;;CAGT,aAAa,kBACX,YACA,QACA;EACA,MAAM,EAAE,aAAa,qBAAqB,mBAAmB,cAC3D;AAEF,MAAI,eAAe,YAAY,CAAC,iBAC9B,OAAM,MACJ,yEACD;EAGH,MAAM,QAAQ,MAAM,KAAK,WAAW,YAAY,OAAO;EACvD,MAAM,qBAAqB,MAAM,MAAM,uBAAuB;AAE9D,MAAI,CAAC,mBACH,OAAM,MACJ,iGACD;AAGH,MAAI,MAAM,uBAAuB,mBAC/B,OAAM,IAAI,MACR;yCACiC,MAAM,mBAAmB;mCAC/B,qBAC5B;AAGH,MAAI,eAAe,UAAU;GAC3B,MAAM,eAAe,MAAM,MAAM,0BAA0B;AAE3D,OAAI,CAAC,aACH,OAAM,MACJ,kGACD;YAEG,iBAAiB,MAAM,UACzB,OAAM,MACJ,2DACD;;AAKP,SAAO;;CAGT,aAAa,kBACX,YACA,QACA;EACA,MAAM,EACJ,qBAAqB,EAAE,EACvB,wBAAwB,iCACxB,aAAa,qBACb,iBAAiB,IACjB,cACE;EAEJ,IAAI,kBAAkB;AAEtB,MAAI,mBAAmB,WAAW,EAChC,OAAM,MACJ,8DACD;AAGH,MAAI,CAAC,eACH,mBAAkB;qCACa,KAAK,UAAU,mBAAmB,CAAC;;sBAElD,sBAAsB,sBAAsB,mBACvD,KAAK,SAAS,KAAK,KAAK,UAAU,CAClC,KAAK,KAAK,CAAC;;EAIlB,MAAM,QAAQ,MAAM,KAAK,WAAW,YAAY;GAC9C,GAAG;GACH,gBAAgB;GACjB,CAAC;EAEF,MAAM,qBAAqB,MAAM,MAAM,uBAAuB;AAE9D,MAAI,CAAC,mBACH,OAAM,MAAM,gBAAgB;WACnB,MAAM,uBAAuB,mBACtC,OAAM,IAAI,MACR,mBAAmB,MAAM,UAAU,4HAA4H,MAAM,mBAAmB,4BAA4B,qBACrN;AAGH,MAAI,eAAe,UAAU;GAC3B,MAAM,eACJ,MAAM,MAAM,yBAAyB,mBAAmB;AAE1D,OAAI,CAAC,aACH,OAAM,MAAM,sBAAsB,mBAAmB;YAEjD,iBAAiB,MAAM,UACzB,OAAM,MACJ,2DACD;;AAKP,SAAO,MAAM;GACX,MAAM,aAAa;qBACJ,UAAU;kBACb,sBAAsB;;;;;;GAOlC,MAAM,OAAO,MAAM,MAAM,MAAM,YAAY,EACzC,OAAO,oBACR,CAAC;AAEF,OAAI,CAAC,KACH;GAGF,MAAM,iBAAiB,MAAM,WAAW,eACtC,KAAK,KAAK,OAAO,GAAG,KAAK,CAC1B;GAED,MAAM,SAAS,EACb,MAAM,KAAK,KAAK,IAAI,WAAW;IAC7B,IAAI,GAAG;IACP,WAAW,eAAe;IAC3B,EAAE,EACJ;AAED,SAAM,MAAM,MACV;;qBAEa,UAAU;;+CAEgB,sBAAsB;;SAG7D,OACD;AAED,OAAI,KAAK,SAAS,IAChB;;AAIJ,SAAO;;CAGT,MAAM,iBAAgC;EACpC,MAAM,aAAa;;;;;;;;;EAUnB,MAAM,aAAa;GACjB,YAAY,KAAK;GACjB,YAAY,KAAK;GACjB,yBAAyB,KAAK;GAC9B,qBAAqB,KAAK;GAC1B,mBAAmB,KAAK;GACzB;AAED,QAAM,KAAK,MAAM,YAAY,WAAW;;CAG1C,MAAM,wBAAwB;EAC5B,IAAI,mBAAmB,MAAM,KAAK,MAChC;;;;;;SAOA;GACE,YAAY,KAAK;GACjB,YAAY,KAAK;GACjB,yBAAyB,KAAK;GAC/B,CACF;AAED,MAAI,kBAAkB;AACpB,sBAAmB,KAAK,gBAAgB,kBAAkB,KAAK,UAAU;AAEzE,OAAI;IACF,MAAM,CAAC,SAAS;IAChB,MAAM,CAAC,eAAe,MAAM;IAC5B,MAAM,CAAC,YAAY,MAAM;AAEzB,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY;AACjB,SAAK,wBAAwB;IAE7B,MAAM,qBACJ,MAAM,QAAQ,YAAY;AAC5B,WAAO,OAAO,mBAAmB;WAC3B;AACN,WAAO;;;AAIX,SAAO;;CAGT,MAAM,yBACJ,qBAA+B,EAAE,EACT;EACxB,MAAM,mBAAmB,MAAM,KAAK,MAClC;;;;;;OAOA;GACE,oBAAoB,KAAK;GACzB,YAAY,KAAK;GACjB,oBACE,mBAAmB,SAAS,IACxB,qBACA,CAAC,KAAK,iBAAiB;GAC9B,CACF;AAED,MAAI,kBAAkB;GACpB,MAAM,yBAAyB,KAAK,gBAClC,kBACA,KAAK,UACN;AAED,OAAI;IACF,MAAM,CAAC,SAAS;IAChB,MAAM,CAAC,eAAe,MAAM;IAC5B,MAAM,CAAC,YAAY,MAAM;AAEzB,SAAK,mBAAmB,MAAM;AAC9B,SAAK,mBAAmB;AACxB,SAAK,YAAY;AAEjB,WAAO;WACD;AACN,WAAO;;;AAIX,SAAO;;CAGT,MAAM,sBACJ,qBAA+B,EAAE,EAClB;EACf,MAAM,YACJ,mBAAmB,SAAS,IACxB,qBACA,CAAC,KAAK,iBAAiB;EAE7B,MAAM,gBAAgB;8BACI,KAAK,iBAAiB;iBACnC,KAAK,UAAU;SACvB,UAAU,KAAK,SAAS,OAAO,KAAK,IAAI,CAAC,KAAK,KAAK,CAAC;;AAGzD,QAAM,KAAK,MAAM,cAAc;;CAGjC,gBACE,QACA,WAC+B;AAC/B,SAAO,OAAO,MACX,GAAG,OACD,EAAE,SAAS,YAAY,KAAK,MAAM,EAAE,SAAS,YAAY,KAAK,GAClE;;CAGH,MAAM,WACJ,SACA,WACA,WACA,KACmB;EACnB,IAAI,OAAO;EACX,MAAM,aAAa;AAEnB,MAAI,CAAC,KACH,QAAO,UAAU,UAAU,KAAK,IAAI,CAAC;EAGvC,MAAM,cAAc;;;;qBAIH,KAAK,UAAU;;+CAEW,KAAK,sBAAsB;;kBAExD,KAAK,iBAAiB;;;;EAKpC,MAAM,aAAa,EACjB,MAAM,UAAU,KAAK,EAAE,aAAa,YAAY,WAAW;GACzD,MAAM;GACN,UAAU,aAAa,WAAW,SAAS;GAC3C,WAAW,QAAQ;GACnB,IAAI,OAAO,KAAK,SAAS;GAC1B,EAAE,EACJ;AAED,QAAM,KAAK,MAAM,aAAa,WAAW;AAEzC,SAAO;;CAGT,MAAM,aAAa,WAA0C;EAC3D,MAAM,QAAQ,UAAU,KAAK,EAAE,kBAAkB,YAAY;AAE7D,SAAO,KAAK,WACV,MAAM,KAAK,WAAW,eAAe,MAAM,EAC3C,UACD;;CAGH,MAAM,iBACJ,OACA,IAAI,GACJ,SAA8B,EAAE,EACX;EACrB,MAAM,YAAY,MAAM,KAAK,WAAW,WAAW,MAAM;AASzD,UAPgB,MAAM,KAAK,gCACzB,WACA,GACA,OACA,OACD,EAEc,KAAK,WAAW,OAAO,GAAG;;CAG3C,MAAM,0BACJ,OACA,IAAI,GACJ,SAA8B,EAAE,EACD;EAC/B,MAAM,YAAY,MAAM,KAAK,WAAW,WAAW,MAAM;AACzD,SAAO,KAAK,gCAAgC,WAAW,GAAG,OAAO,OAAO;;CAG1E,MAAM,gCACJ,QACA,GACA,OACA,SAA8B,EAAE,EACD;EAC/B,IAAI;EACJ,IAAI;EAEJ,MAAM,EAAE,WAAW;AAEnB,MAAI,QAAQ;AACV,OAAI,CAAC,KAAK,sBACR,OAAM,IAAI,MACR,yEACD;AAGH,OAAI,KAAK,eAAe,SACtB,OAAM,IAAI,MACR,gFACD;GAOH,MAAM,iBAAiB;UAJD,KAAK,eACvB,0DACA,GAGc;qBACH,KAAK,UAAU;oBAChB,KAAK,sBAAsB;uBACxB,KAAK,sBAAsB,kBAAkB,KAAK,mBAAmB;;GAGtF,MAAM,kBAAkB;;gBAEd,KAAK,sBAAsB;;;;GAIrC,MAAM,CAAC,WAAW,WAAW,wBAAwB,OAAO;AAE5D,gBAAa,iBAAiB,YAAY;AAC1C,kBAAe;SACV;AACL,gBAAa,oBAAoB,KAAK,YAAY,KAAK,UAAU;AACjE,kBAAe,EAAE;;EAGnB,IAAI;AAEJ,MAAI,KAAK,cAAc,eACrB,oBAAmB;8BACK,KAAK,iBAAiB;4BACxB,KAAK,iBAAiB;UACxC,KAAK,sBAAsB;;MAG/B,oBAAmB;sBACH,KAAK,iBAAiB;oBACxB,KAAK,iBAAiB;UAChC,KAAK,sBAAsB;;EAIjC,MAAM,iBAAiB,KAAK,iBACxB,KAAK,iBACL;EACJ,MAAM,YAAY,GAAG,WAAW,GAAG;EAEnC,MAAM,aAAa;GACjB,OAAO,KAAK;GACZ,GAAG,OAAO,EAAE;GACZ,WAAW;GACX,eAAe,KAAK;GACpB,OAAO,kBAAkB,MAAM;GAC/B,GAAG;GACH,GAAG;GACJ;EAED,MAAM,UAAU,MAAM,KAAK,MAAM,WAAW,WAAW;AAEvD,MAAI,SAAS;AACX,OAAI,QAAQ,MAAM,WAAW,OAAO,QAAQ,KAAK,CAC/C,KAAI,CAAC,KAAK,eACR,OAAM,IAAI,MACR,+BAA+B,KAAK,iBAAiB,oCAAoC,KAAK,UAAU,wBACzG;OAED,OAAM,IAAI,MACR,uFACD;AAcL,UAVmC,QAAQ,KAAK,WAAgB,CAC9D,IAAI,SAAS;IACX,aAAa,OAAO;IACpB,UAAU,OAAO,YACf,OAAO,QAAQ,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,OAAO,MAAM,KAAK,CAC/D;IACF,CAAC,EACF,OAAO,MACR,CAAC;;AAKJ,SAAO,EAAE;;;AAIb,SAAS,UAAU,SAAwB;AASzC,QAR4C,QAAQ,KAAK,WAAW;EAClE,MAAM,OAAO,OAAO,UAAU;EAC9B,MAAM,MAA8B,EAAE;AACtC,SAAO,KAAK,KAAK,CAAC,SAAS,QAAQ;AACjC,OAAI,OAAO,gBAAgB,KAAK,KAAK;IACrC;AACF,SAAO;GACP;;AAIJ,SAAS,gBAAgB,MAAgB;AACvC,KAAI,MAAM,MAAM,KAAK,CAAE,QAAO,KAAK,UAAU;AAC7C,KAAI,MAAM,QAAQ,KAAK,CAAE,QAAO,KAAK,KAAK,OAAO,gBAAgB,GAAG,CAAC;AACrE,KAAI;EAAC;EAAU;EAAU;EAAU,CAAC,QAAQ,OAAO,KAAK,KAAK,GAAI,QAAO;AACxE,KAAI,SAAS,KAAM,QAAO;AAC1B,KAAI,OAAO,SAAS,SAAU,QAAO,eAAe,KAAK;;AAG3D,SAAS,eAAe,KAAU;CAChC,MAAM,QAAQ,sBAAsB,IAAI;CACxC,IAAI,SAAc;AAClB,KAAI,MAAM,QAAQ,MAAM,CACtB,UAAS,MAAM,KAAK,SAAS,gBAAgB,KAAK,CAAC;UAC1C,UAAU,QAAQ,OAAO,UAAU,UAAU;AACtD,WAAS,EAAE;AACX,SAAO,KAAK,MAAM,CAAC,SAAS,QAAQ;AAClC,UAAO,OAAO,gBAAgB,MAAM,KAAK;IACzC;;AAEJ,QAAO;;AAGT,SAAS,sBAAsB,KAAU;AACvC,KAEE,eAAgB,MAAM,MAAM,QAE5B,eAAgB,MAAM,MAAM,aAE5B,QAAO,IAAI;UAEF,eAAgB,MAAM,MAAM,KAErC,QAAO,EAAE,CAAC,OAAO,MAA2B,EAAE,EAAE,mBAAmB,IAAI,CAAC;AAE1E,QAAO;;AAGT,SAAS,mBAAmB,MAAiB;CAC3C,IAAI,EAAE,aAAa;AACnB,KAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,SAAS,SAAS,EAC1D,YAAW,CAAC;EAAE,GAAG;EAAM,KAAK;EAAM,CAAQ;AAG5C,QAAO,SAAS,KAAK,YACnB;EACE,eAAe,QAAQ,MAAM;EAC7B,eAAe,QAAQ,aAAa;EACpC,eAAe,QAAQ,IAAI;EAC5B,CAAC,QAAQ,SAAS,SAAS,KAAK,CAClC;;AAGH,SAAS,oBACP,YACA,YAAuB,oBACf;AACR,KAAI,cAAc,OAoBhB,QAnBwD;EACtD,QACE;EACF,QAAQ;;;;;;;;;;;;;;EAcT,CAEqB;KAEtB,QAAO;;;;;AAOX,SAAgB,kBAAkB,MAAqB;AACrD,KAAI,SAAS,KAAA,KAAa,SAAS,KACjC,QAAO;CAGT,MAAM,eAAe;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,IAAI,eAAe;AACnB,MAAK,MAAM,QAAQ,aACjB,gBAAe,aAAa,MAAM,KAAK,CAAC,KAAK,IAAI;AAEnD,QAAO,aAAa,MAAM;;AAG5B,SAAgB,kBAAkB,IAAc,IAAuB;AACrE,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,QAAQ,GAAG,OAAO,EAAE,KAAK,EACvD,KAAI,GAAG,KAAK,GAAG,GACb,QAAO;UACE,GAAG,KAAK,GAAG,GACpB,QAAO;AAGX,QAAO,GAAG,SAAS,GAAG;;AAGxB,MAAM,wBAAgD;CACpD,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACP;AAED,MAAM,kCAAkC,IAAI,IAC1C,OAAO,KAAK,sBAAsB,CACnC;AAED,MAAM,iBAAiB,IAAI,IAAI,CAAC,SAAS,SAAS,CAAC;AAEnD,MAAM,oBAAoB,IAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAElD,MAAM,0BAA0B,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAW,CAAC;AAEpE,MAAM,sBAAsB,IAAI,IAAI;CAClC,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACJ,CAAC;AAEF,MAAM,sBAAsB;AAE5B,SAAS,eACP,cACA,UAC+B;CAC/B,IAAI,gBAAgB;CACpB,MAAM,iBAAsC,EAAE;CAC9C,MAAM,eAAuC,EAAE;AAE/C,MAAK,MAAM,CAAC,OAAO,WAAW,cAAc;EAC1C,IAAI,WAAW;AACf,OAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,OAAO,EAAE;AACnD,OAAI,SAAS,aACX,cAAa,UAAU;OAEvB,cAAa,SAAS;GAExB,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa;AAE9C,cAAW,SAAS,QAAQ,IAAI,SAAS,IAAI,eAAe;AAC5D,kBAAe,gBAAgB;;AAGjC,MAAI,cACF,kBAAiB,IAAI,SAAS;AAEhC,mBAAiB,IAAI,SAAS;;AAGhC,QAAO,CAAC,eAAe,eAAe;;AAGxC,SAAS,cACP,WACiC;CACjC,MAAM,aAAuB,EAAE;CAC/B,MAAM,SAA8B,EAAE;AAEtC,MAAK,MAAM,CAAC,WAAW,UAAU,WAAW;AAC1C,aAAW,KAAK,UAAU;AAC1B,SAAO,OAAO,QAAQ,MAAM;;AAG9B,QAAO,CAAC,YAAY,OAAO;;AAG7B,SAAS,kBACP,OACA,OACA,cAAc,GACiB;AAC/B,KAAI,OAAO,UAAU,SACnB,OAAM,IAAI,MACR,qCAAqC,OAAO,MAAM,eAAe,QAClE;AAGH,KAAI,MAAM,WAAW,IAAI,CACvB,OAAM,IAAI,MACR,mEAAmE,QACpE;AAGH,KAAI,CAAC,oBAAoB,KAAK,MAAM,CAClC,OAAM,IAAI,MACR,uBAAuB,MAAM,gCAC9B;CAGH,IAAI;CACJ,IAAI;AAEJ,KAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM,EAAE;EACxE,MAAM,OAAO,OAAO,KAAK,MAAM;AAE/B,MAAI,KAAK,WAAW,EAClB,OAAM,IAAI,MAAM;;eAEP,KAAK,OAAO,iCAAiC,KACjD,MAAM,GAAG,EAAE,CACX,KAAK,KAAK,CAAC;QACd;AAGJ,aAAW,KAAK;AAChB,gBAAc,MAAM;AAEpB,MAAI,CAAC,oBAAoB,IAAI,SAAS,CACpC,OAAM,IAAI,MACR,qBAAqB,SAAS,oBAAoB,sBACnD;QAEE;AACL,aAAW;AACX,gBAAc;;AAGhB,KAAI,gCAAgC,IAAI,SAAS,CAK/C,QAAO,CAHc,KAAK,MAAM,GADjB,sBAAsB,UACK,UAAU,eACjC,GAAG,SAAS,gBAAgB,aAAa,CAE3B;UACxB,aAAa,YAAY;EAClC,MAAM,CAAC,KAAK,QAAQ;AAOpB,SAAO,CANc,UAAU,YAAY,YAAY,MAAM,aAAa,YAAY,QACnE;IAChB,SAAS,YAAY,QAAQ;IAC7B,SAAS,YAAY,SAAS;GAChC,CAEgC;YACxB;EAAC;EAAO;EAAQ;EAAS;EAAS,CAAC,SAAS,SAAS,EAAE;AAChE,MAAI,CAAC,OAAO,OAAO,CAAC,SAAS,SAAS,CACpC,aAAY,SAAS,QAAa;AAChC,OACE,OAAO,QAAQ,YACf,OAAO,QAAQ,YACf,OAAO,QAAQ,UAEf,OAAM,IAAI,MAAM,qBAAqB,OAAO,IAAI,cAAc,MAAM;IAEtE;AAGJ,MAAI,aAAa,MAGf,QAAO,CAFc,KAAK,MAAM,aAAa,eAC1B,GAAG,SAAS,gBAAgB,aAAa,CAC3B;WACxB,aAAa,OAGtB,QAAO,CAFc,KAAK,MAAM,iBAAiB,eAC9B,GAAG,SAAS,gBAAgB,aAAa,CAC3B;WACxB,aAAa,QAKtB,QAAO,CAJc,KAAK,MAAM,mBAAmB,eAChC,GAChB,SAAS,gBAAgB,YAAY,MAAM,GAAG,GAAG,EACnD,CACgC;WACxB,aAAa,SAKtB,QAAO,CAJc,aAAa,MAAM,oBAAoB,eACzC,GAChB,SAAS,gBAAgB,YAAY,MAAM,GAAG,GAAG,EACnD,CACgC;MAEjC,OAAM,IAAI,MAAM,kBAAkB;OAGpC,OAAM,IAAI,MAAM,kBAAkB;;AAItC,SAAgB,wBACd,QAC+B;AAC/B,KAAI,OAAO,WAAW,YAAY,WAAW,KAC3C,OAAM,IAAI,MAAM,2DAA2D;CAG7E,MAAM,UAAU,OAAO,QAAQ,OAAO;AAEtC,KAAI,QAAQ,WAAW,GAAG;EACxB,MAAM,CAAC,KAAK,SAAS,QAAQ;AAE7B,MAAI,IAAI,WAAW,IAAI,EAAE;AACvB,OAAI,CAAC,CAAC,QAAQ,MAAM,CAAC,SAAS,IAAI,aAAa,CAAC,CAC9C,OAAM,IAAI,MACR,2DAA2D,MAC5D;AAGH,OAAI,CAAC,MAAM,QAAQ,MAAM,CACvB,OAAM,IAAI,MACR,qDAAqD,OAAO,MAAM,cAAc,QACjF;GAGH,MAAM,YAAY,IAAI,aAAa,KAAK,SAAS,QAAQ;AAMzD,UALwB,eACtB,MAAM,KAAK,MAAM,wBAAwB,EAAE,CAAC,EAC5C,UACD;QAID,QAAO,kBAAkB,KAAK,MAAM;YAE7B,QAAQ,SAAS,GAAG;AAC7B,OAAK,MAAM,CAAC,QAAQ,QAClB,KAAI,IAAI,WAAW,IAAI,CACrB,OAAM,IAAI,MACR,mEAAmE,MACpE;EAIL,MAAM,eAAe,cACnB,QAAQ,KAAK,CAAC,OAAO,MAAM,UACzB,kBAAkB,OAAO,KAAK,QAAQ,EAAE,CACzC,CACF;AAED,MAAI,aAAa,UAAU,EACzB,QAAO,CAAC,aAAa,GAAG,KAAK,QAAQ,EAAE,aAAa,GAAG;MAEvD,OAAM,MACJ,8EACD;OAGH,OAAM,IAAI,MAAM,wCAAwC"}
|
|
1
|
+
{"version":3,"file":"neo4j_vector.js","names":[],"sources":["../../src/vectorstores/neo4j_vector.ts"],"sourcesContent":["import neo4j, {\n type Driver as Neo4jDriver,\n type Record as Neo4jRecord,\n type Path as Neo4jPath,\n} from \"neo4j-driver\";\nimport * as uuid from \"@langchain/core/utils/uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\nexport type SearchType = \"vector\" | \"hybrid\";\n\nexport type IndexType = \"NODE\" | \"RELATIONSHIP\";\n\nexport type DistanceStrategy = \"euclidean\" | \"cosine\";\n\nexport type Metadata = Record<string, unknown>;\n\nexport interface Neo4jVectorStoreArgs {\n url: string;\n username: string;\n password: string;\n database?: string;\n preDeleteCollection?: boolean;\n textNodeProperty?: string;\n textNodeProperties?: string[];\n embeddingNodeProperty?: string;\n keywordIndexName?: string;\n indexName?: string;\n searchType?: SearchType;\n indexType?: IndexType;\n retrievalQuery?: string;\n nodeLabel?: string;\n createIdIndex?: boolean;\n}\n\nconst DEFAULT_SEARCH_TYPE = \"vector\";\nconst DEFAULT_INDEX_TYPE = \"NODE\";\nconst DEFAULT_DISTANCE_STRATEGY = \"cosine\";\nconst DEFAULT_NODE_EMBEDDING_PROPERTY = \"embedding\";\n\n/**\n * @security *Security note*: Make sure that the database connection uses credentials\n * that are narrowly-scoped to only include necessary permissions.\n * Failure to do so may result in data corruption or loss, since the calling\n * code may attempt commands that would result in deletion, mutation\n * of data if appropriately prompted or reading sensitive data if such\n * data is present in the database.\n * The best way to guard against such negative outcomes is to (as appropriate)\n * limit the permissions granted to the credentials used with this tool.\n * For example, creating read only users for the database is a good way to\n * ensure that the calling code cannot mutate or delete data.\n *\n * @link See https://js.langchain.com/docs/security for more information.\n */\nexport class Neo4jVectorStore extends VectorStore {\n private driver: Neo4jDriver;\n\n private database: string;\n\n private preDeleteCollection: boolean;\n\n private nodeLabel: string;\n\n private embeddingNodeProperty: string;\n\n private embeddingDimension: number;\n\n private textNodeProperty: string;\n\n private keywordIndexName: string;\n\n private indexName: string;\n\n private retrievalQuery: string;\n\n private searchType: SearchType;\n\n private indexType: IndexType;\n\n private distanceStrategy: DistanceStrategy = DEFAULT_DISTANCE_STRATEGY;\n\n private supportMetadataFilter = true;\n\n private isEnterprise = false;\n\n _vectorstoreType(): string {\n return \"neo4jvector\";\n }\n\n constructor(embeddings: EmbeddingsInterface, config: Neo4jVectorStoreArgs) {\n super(embeddings, config);\n }\n\n static async initialize(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const store = new Neo4jVectorStore(embeddings, config);\n await store._initializeDriver(config);\n await store._verifyConnectivity();\n\n const {\n preDeleteCollection = false,\n nodeLabel = \"Chunk\",\n textNodeProperty = \"text\",\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n keywordIndexName = \"keyword\",\n indexName = \"vector\",\n retrievalQuery = \"\",\n searchType = DEFAULT_SEARCH_TYPE,\n indexType = DEFAULT_INDEX_TYPE,\n } = config;\n\n store.embeddingDimension = (await embeddings.embedQuery(\"foo\")).length;\n store.preDeleteCollection = preDeleteCollection;\n store.nodeLabel = nodeLabel;\n store.textNodeProperty = textNodeProperty;\n store.embeddingNodeProperty = embeddingNodeProperty;\n store.keywordIndexName = keywordIndexName;\n store.indexName = indexName;\n store.retrievalQuery = retrievalQuery;\n store.searchType = searchType;\n store.indexType = indexType;\n\n if (store.preDeleteCollection) {\n await store._dropIndex();\n }\n\n return store;\n }\n\n async _initializeDriver({\n url,\n username,\n password,\n database = \"neo4j\",\n }: Neo4jVectorStoreArgs) {\n try {\n this.driver = neo4j.driver(url, neo4j.auth.basic(username, password));\n this.database = database;\n } catch {\n throw new Error(\n \"Could not create a Neo4j driver instance. Please check the connection details.\"\n );\n }\n }\n\n async _verifyConnectivity() {\n await this.driver.verifyAuthentication();\n }\n\n async _verifyVersion() {\n try {\n const data = await this.query(\"CALL dbms.components()\");\n const versionString: string = data[0].versions[0];\n const targetVersion = [5, 11, 0];\n\n let version: number[];\n\n if (versionString.includes(\"aura\")) {\n const baseVersion = versionString.split(\"-\")[0];\n version = baseVersion.split(\".\").map(Number);\n version.push(0);\n } else {\n version = versionString.split(\".\").map(Number);\n }\n\n if (isVersionLessThan(version, targetVersion)) {\n throw new Error(\n \"Version index is only supported in Neo4j version 5.11 or greater\"\n );\n }\n\n const metadataTargetVersion = [5, 18, 0];\n if (isVersionLessThan(version, metadataTargetVersion)) {\n this.supportMetadataFilter = false;\n }\n\n this.isEnterprise = data[0].edition === \"enterprise\";\n } catch (error) {\n console.error(\"Database version check failed:\", error);\n }\n }\n\n async close() {\n await this.driver.close();\n }\n\n async _dropIndex() {\n try {\n await this.query(`\n MATCH (n:\\`${this.nodeLabel}\\`)\n CALL {\n WITH n\n DETACH DELETE n\n }\n IN TRANSACTIONS OF 10000 ROWS;\n `);\n await this.query(`DROP INDEX ${this.indexName}`);\n } catch (error) {\n console.error(\"An error occurred while dropping the index:\", error);\n }\n }\n\n async query(query: string, params: Any = {}): Promise<Any[]> {\n const session = this.driver.session({ database: this.database });\n const result = await session.run(query, params);\n return toObjects(result.records);\n }\n\n static async fromTexts(\n texts: string[],\n metadatas: Any,\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const docs = [];\n\n for (let i = 0; i < texts.length; i += 1) {\n const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;\n const newDoc = new Document({\n pageContent: texts[i],\n metadata,\n });\n docs.push(newDoc);\n }\n\n return Neo4jVectorStore.fromDocuments(docs, embeddings, config);\n }\n\n static async fromDocuments(\n docs: Document[],\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ): Promise<Neo4jVectorStore> {\n const {\n searchType = DEFAULT_SEARCH_TYPE,\n createIdIndex = true,\n textNodeProperties = [],\n } = config;\n\n const store = await this.initialize(embeddings, config);\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name \"${store.indexName}\" already exists. The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n if (createIdIndex) {\n await store.query(\n `CREATE CONSTRAINT IF NOT EXISTS FOR (n:${store.nodeLabel}) REQUIRE n.id IS UNIQUE;`\n );\n }\n\n await store.addDocuments(docs);\n\n return store;\n }\n\n static async fromExistingIndex(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const { searchType = DEFAULT_SEARCH_TYPE, keywordIndexName = \"keyword\" } =\n config;\n\n if (searchType === \"hybrid\" && !keywordIndexName) {\n throw Error(\n \"keyword_index name has to be specified when using hybrid search option\"\n );\n }\n\n const store = await this.initialize(embeddings, config);\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n throw Error(\n \"The specified vector index name does not exist. Make sure to check if you spelled it correctly\"\n );\n }\n\n if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `The provided embedding function and vector index dimensions do not match.\n Embedding function dimension: ${store.embeddingDimension}\n Vector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel = await store.retrieveExistingFtsIndex();\n\n if (!ftsNodeLabel) {\n throw Error(\n \"The specified keyword index name does not exist. Make sure to check if you spelled it correctly\"\n );\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n return store;\n }\n\n static async fromExistingGraph(\n embeddings: EmbeddingsInterface,\n config: Neo4jVectorStoreArgs\n ) {\n const {\n textNodeProperties = [],\n embeddingNodeProperty = DEFAULT_NODE_EMBEDDING_PROPERTY,\n searchType = DEFAULT_SEARCH_TYPE,\n retrievalQuery = \"\",\n nodeLabel,\n } = config;\n\n let _retrievalQuery = retrievalQuery;\n\n if (textNodeProperties.length === 0) {\n throw Error(\n \"Parameter `text_node_properties` must not be an empty array\"\n );\n }\n\n if (!retrievalQuery) {\n _retrievalQuery = `\n RETURN reduce(str='', k IN ${JSON.stringify(textNodeProperties)} |\n str + '\\\\n' + k + ': ' + coalesce(node[k], '')) AS text,\n node {.*, \\`${embeddingNodeProperty}\\`: Null, id: Null, ${textNodeProperties\n .map((prop) => `\\`${prop}\\`: Null`)\n .join(\", \")} } AS metadata, score\n `;\n }\n\n const store = await this.initialize(embeddings, {\n ...config,\n retrievalQuery: _retrievalQuery,\n });\n\n const embeddingDimension = await store.retrieveExistingIndex();\n\n if (!embeddingDimension) {\n await store.createNewIndex();\n } else if (store.embeddingDimension !== embeddingDimension) {\n throw new Error(\n `Index with name ${store.indexName} already exists. The provided embedding function and vector index dimensions do not match.\\nEmbedding function dimension: ${store.embeddingDimension}\\nVector index dimension: ${embeddingDimension}`\n );\n }\n\n if (searchType === \"hybrid\") {\n const ftsNodeLabel =\n await store.retrieveExistingFtsIndex(textNodeProperties);\n\n if (!ftsNodeLabel) {\n await store.createNewKeywordIndex(textNodeProperties);\n } else {\n if (ftsNodeLabel !== store.nodeLabel) {\n throw Error(\n \"Vector and keyword index don't index the same node label\"\n );\n }\n }\n }\n\n while (true) {\n const fetchQuery = `\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE n.${embeddingNodeProperty} IS null\n AND any(k in $props WHERE n[k] IS NOT null)\n RETURN elementId(n) AS id, reduce(str='', k IN $props |\n str + '\\\\n' + k + ':' + coalesce(n[k], '')) AS text\n LIMIT 1000\n `;\n\n const data = await store.query(fetchQuery, {\n props: textNodeProperties,\n });\n\n if (!data) {\n break;\n }\n\n const textEmbeddings = await embeddings.embedDocuments(\n data.map((el) => el.text)\n );\n\n const params = {\n data: data.map((el, index) => ({\n id: el.id,\n embedding: textEmbeddings[index],\n })),\n };\n\n await store.query(\n `\n UNWIND $data AS row\n MATCH (n:\\`${nodeLabel}\\`)\n WHERE elementId(n) = row.id\n CALL db.create.setVectorProperty(n, '${embeddingNodeProperty}', row.embedding)\n YIELD node RETURN count(*)\n `,\n params\n );\n\n if (data.length < 1000) {\n break;\n }\n }\n\n return store;\n }\n\n async createNewIndex(): Promise<void> {\n const indexQuery = `\n CALL db.index.vector.createNodeIndex(\n $index_name,\n $node_label,\n $embedding_node_property,\n toInteger($embedding_dimension),\n $similarity_metric\n )\n `;\n\n const parameters = {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n embedding_dimension: this.embeddingDimension,\n similarity_metric: this.distanceStrategy,\n };\n\n await this.query(indexQuery, parameters);\n }\n\n async retrieveExistingIndex() {\n let indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'VECTOR' AND (name = $index_name\n OR (labelsOrTypes[0] = $node_label AND\n properties[0] = $embedding_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n index_name: this.indexName,\n node_label: this.nodeLabel,\n embedding_node_property: this.embeddingNodeProperty,\n }\n );\n\n if (indexInformation) {\n indexInformation = this.sortByIndexName(indexInformation, this.indexName);\n\n try {\n const [index] = indexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.indexName = index.name;\n this.nodeLabel = labelOrType;\n this.embeddingNodeProperty = property;\n\n const embeddingDimension =\n index.options.indexConfig[\"vector.dimensions\"];\n return Number(embeddingDimension);\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async retrieveExistingFtsIndex(\n textNodeProperties: string[] = []\n ): Promise<string | null> {\n const indexInformation = await this.query(\n `\n SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options\n WHERE type = 'FULLTEXT' AND (name = $keyword_index_name\n OR (labelsOrTypes = [$node_label] AND\n properties = $text_node_property))\n RETURN name, labelsOrTypes, properties, options\n `,\n {\n keyword_index_name: this.keywordIndexName,\n node_label: this.nodeLabel,\n text_node_property:\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty],\n }\n );\n\n if (indexInformation) {\n const sortedIndexInformation = this.sortByIndexName(\n indexInformation,\n this.indexName\n );\n\n try {\n const [index] = sortedIndexInformation;\n const [labelOrType] = index.labelsOrTypes;\n const [property] = index.properties;\n\n this.keywordIndexName = index.name;\n this.textNodeProperty = property;\n this.nodeLabel = labelOrType;\n\n return labelOrType;\n } catch {\n return null;\n }\n }\n\n return null;\n }\n\n async createNewKeywordIndex(\n textNodeProperties: string[] = []\n ): Promise<void> {\n const nodeProps =\n textNodeProperties.length > 0\n ? textNodeProperties\n : [this.textNodeProperty];\n\n const ftsIndexQuery = `\n CREATE FULLTEXT INDEX ${this.keywordIndexName}\n FOR (n:\\`${this.nodeLabel}\\`) ON EACH\n [${nodeProps.map((prop) => `n.\\`${prop}\\``).join(\", \")}]\n `;\n\n await this.query(ftsIndexQuery);\n }\n\n sortByIndexName(\n values: Array<{ [key: string]: Any }>,\n indexName: string\n ): Array<{ [key: string]: Any }> {\n return values.sort(\n (a, b) =>\n (a.name === indexName ? -1 : 0) - (b.name === indexName ? -1 : 0)\n );\n }\n\n async addVectors(\n vectors: number[][],\n documents: Document[],\n metadatas?: Record<string, Any>[],\n ids?: string[]\n ): Promise<string[]> {\n let _ids = ids;\n const _metadatas = metadatas;\n\n if (!_ids) {\n _ids = documents.map(() => uuid.v1());\n }\n\n const importQuery = `\n UNWIND $data AS row\n CALL {\n WITH row\n MERGE (c:\\`${this.nodeLabel}\\` {id: row.id})\n WITH c, row\n CALL db.create.setVectorProperty(c, '${this.embeddingNodeProperty}', row.embedding)\n YIELD node\n SET c.\\`${this.textNodeProperty}\\` = row.text\n SET c += row.metadata\n } IN TRANSACTIONS OF 1000 ROWS\n `;\n\n const parameters = {\n data: documents.map(({ pageContent, metadata }, index) => ({\n text: pageContent,\n metadata: _metadatas ? _metadatas[index] : metadata,\n embedding: vectors[index],\n id: _ids ? _ids[index] : null,\n })),\n };\n\n await this.query(importQuery, parameters);\n\n return _ids;\n }\n\n async addDocuments(documents: Document[]): Promise<string[]> {\n const texts = documents.map(({ pageContent }) => pageContent);\n\n return this.addVectors(\n await this.embeddings.embedDocuments(texts),\n documents\n );\n }\n\n async similaritySearch(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<Document[]> {\n const embedding = await this.embeddings.embedQuery(query);\n\n const results = await this.similaritySearchVectorWithScore(\n embedding,\n k,\n query,\n params\n );\n\n return results.map((result) => result[0]);\n }\n\n async similaritySearchWithScore(\n query: string,\n k = 4,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n const embedding = await this.embeddings.embedQuery(query);\n return this.similaritySearchVectorWithScore(embedding, k, query, params);\n }\n\n async similaritySearchVectorWithScore(\n vector: number[],\n k: number,\n query: string,\n params: Record<string, Any> = {}\n ): Promise<[Document, number][]> {\n let indexQuery: string;\n let filterParams: Record<string, Any>;\n\n const { filter } = params;\n\n if (filter) {\n if (!this.supportMetadataFilter) {\n throw new Error(\n \"Metadata filtering is only supported in Neo4j version 5.18 or greater.\"\n );\n }\n\n if (this.searchType === \"hybrid\") {\n throw new Error(\n \"Metadata filtering can't be use in combination with a hybrid search approach.\"\n );\n }\n\n const parallelQuery = this.isEnterprise\n ? \"CYPHER runtime = parallel parallelRuntimeSupport=all \"\n : \"\";\n\n const baseIndexQuery = `\n ${parallelQuery}\n MATCH (n:\\`${this.nodeLabel}\\`)\n WHERE n.\\`${this.embeddingNodeProperty}\\` IS NOT NULL\n AND size(n.\\`${this.embeddingNodeProperty}\\`) = toInteger(${this.embeddingDimension}) AND\n `;\n\n const baseCosineQuery = `\n WITH n as node, vector.similarity.cosine(\n n.\\`${this.embeddingNodeProperty}\\`,\n $embedding\n ) AS score ORDER BY score DESC LIMIT toInteger($k)\n `;\n const [fSnippets, fParams] = constructMetadataFilter(filter);\n\n indexQuery = baseIndexQuery + fSnippets + baseCosineQuery;\n filterParams = fParams;\n } else {\n indexQuery = getSearchIndexQuery(this.searchType, this.indexType);\n filterParams = {};\n }\n\n let defaultRetrieval: string;\n\n if (this.indexType === \"RELATIONSHIP\") {\n defaultRetrieval = `\n RETURN relationship.${this.textNodeProperty} AS text, score,\n relationship {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n } else {\n defaultRetrieval = `\n RETURN node.${this.textNodeProperty} AS text, score,\n node {.*, ${this.textNodeProperty}: Null,\n ${this.embeddingNodeProperty}: Null, id: Null } AS metadata\n `;\n }\n\n const retrievalQuery = this.retrievalQuery\n ? this.retrievalQuery\n : defaultRetrieval;\n const readQuery = `${indexQuery} ${retrievalQuery}`;\n\n const parameters = {\n index: this.indexName,\n k: Number(k),\n embedding: vector,\n keyword_index: this.keywordIndexName,\n query: removeLuceneChars(query),\n ...params,\n ...filterParams,\n };\n\n const results = await this.query(readQuery, parameters);\n\n if (results) {\n if (results.some((result) => result.text == null)) {\n if (!this.retrievalQuery) {\n throw new Error(\n `Make sure that none of the '${this.textNodeProperty}' properties on nodes with label '${this.nodeLabel}' are missing or empty`\n );\n } else {\n throw new Error(\n \"Inspect the 'retrievalQuery' and ensure it doesn't return null for the 'text' column\"\n );\n }\n }\n\n const docs: [Document, number][] = results.map((result: Any) => [\n new Document({\n pageContent: result.text,\n metadata: Object.fromEntries(\n Object.entries(result.metadata).filter(([_, v]) => v !== null)\n ),\n }),\n result.score,\n ]);\n\n return docs;\n }\n\n return [];\n }\n}\n\nfunction toObjects(records: Neo4jRecord[]) {\n const recordValues: Record<string, Any>[] = records.map((record) => {\n const rObj = record.toObject();\n const out: { [key: string]: Any } = {};\n Object.keys(rObj).forEach((key) => {\n out[key] = itemIntToString(rObj[key]);\n });\n return out;\n });\n return recordValues;\n}\n\nfunction itemIntToString(item: Any): Any {\n if (neo4j.isInt(item)) return item.toString();\n if (Array.isArray(item)) return item.map((ii) => itemIntToString(ii));\n if ([\"number\", \"string\", \"boolean\"].indexOf(typeof item) !== -1) return item;\n if (item === null) return item;\n if (typeof item === \"object\") return objIntToString(item);\n}\n\nfunction objIntToString(obj: Any) {\n const entry = extractFromNeoObjects(obj);\n let newObj: Any = null;\n if (Array.isArray(entry)) {\n newObj = entry.map((item) => itemIntToString(item));\n } else if (entry !== null && typeof entry === \"object\") {\n newObj = {};\n Object.keys(entry).forEach((key) => {\n newObj[key] = itemIntToString(entry[key]);\n });\n }\n return newObj;\n}\n\nfunction extractFromNeoObjects(obj: Any) {\n if (\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // oxlint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // oxlint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // oxlint-disable-next-line\n return [].concat.apply<any[], any[], any[]>([], extractPathForRows(obj));\n }\n return obj;\n}\n\nfunction extractPathForRows(path: Neo4jPath) {\n let { segments } = path;\n if (!Array.isArray(path.segments) || path.segments.length < 1) {\n segments = [{ ...path, end: null } as Any];\n }\n\n return segments.map((segment: Any) =>\n [\n objIntToString(segment.start),\n objIntToString(segment.relationship),\n objIntToString(segment.end),\n ].filter((part) => part !== null)\n );\n}\n\nfunction getSearchIndexQuery(\n searchType: SearchType,\n indexType: IndexType = DEFAULT_INDEX_TYPE\n): string {\n if (indexType === \"NODE\") {\n const typeToQueryMap: { [key in SearchType]: string } = {\n vector:\n \"CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\",\n hybrid: `\n CALL {\n CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score\n WITH collect({node:node, score:score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n // We use 0 as min\n RETURN n.node AS node, (n.score / max) AS score UNION\n CALL db.index.fulltext.queryNodes($keyword_index, $query, {limit: $k}) YIELD node, score\n WITH collect({node: node, score: score}) AS nodes, max(score) AS max\n UNWIND nodes AS n\n RETURN n.node AS node, (n.score / max) AS score\n }\n WITH node, max(score) AS score ORDER BY score DESC LIMIT toInteger($k)\n `,\n };\n\n return typeToQueryMap[searchType];\n } else {\n return `\n CALL db.index.vector.queryRelationships($index, $k, $embedding)\n YIELD relationship, score\n `;\n }\n}\n\nexport function removeLuceneChars(text: string | null) {\n if (text === undefined || text === null) {\n return null;\n }\n\n const specialChars = [\n \"+\",\n \"-\",\n \"&\",\n \"|\",\n \"!\",\n \"(\",\n \")\",\n \"{\",\n \"}\",\n \"[\",\n \"]\",\n \"^\",\n '\"',\n \"~\",\n \"*\",\n \"?\",\n \":\",\n \"\\\\\",\n ];\n let modifiedText = text;\n for (const char of specialChars) {\n modifiedText = modifiedText.split(char).join(\" \");\n }\n return modifiedText.trim();\n}\n\nexport function isVersionLessThan(v1: number[], v2: number[]): boolean {\n for (let i = 0; i < Math.min(v1.length, v2.length); i += 1) {\n if (v1[i] < v2[i]) {\n return true;\n } else if (v1[i] > v2[i]) {\n return false;\n }\n }\n return v1.length < v2.length;\n}\n\nconst COMPARISONS_TO_NATIVE: Record<string, string> = {\n $eq: \"=\",\n $ne: \"<>\",\n $lt: \"<\",\n $lte: \"<=\",\n $gt: \">\",\n $gte: \">=\",\n};\n\nconst COMPARISONS_TO_NATIVE_OPERATORS = new Set(\n Object.keys(COMPARISONS_TO_NATIVE)\n);\n\nconst TEXT_OPERATORS = new Set([\"$like\", \"$ilike\"]);\n\nconst LOGICAL_OPERATORS = new Set([\"$and\", \"$or\"]);\n\nconst SPECIAL_CASED_OPERATORS = new Set([\"$in\", \"$nin\", \"$between\"]);\n\nconst SUPPORTED_OPERATORS = new Set([\n ...COMPARISONS_TO_NATIVE_OPERATORS,\n ...TEXT_OPERATORS,\n ...LOGICAL_OPERATORS,\n ...SPECIAL_CASED_OPERATORS,\n]);\n\nconst IS_IDENTIFIER_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction combineQueries(\n inputQueries: [string, Record<string, Any>][],\n operator: string\n): [string, Record<string, Any>] {\n let combinedQuery = \"\";\n const combinedParams: Record<string, Any> = {};\n const paramCounter: Record<string, number> = {};\n\n for (const [query, params] of inputQueries) {\n let newQuery = query;\n for (const [param, value] of Object.entries(params)) {\n if (param in paramCounter) {\n paramCounter[param] += 1;\n } else {\n paramCounter[param] = 1;\n }\n const newParamName = `${param}_${paramCounter[param]}`;\n\n newQuery = newQuery.replace(`$${param}`, `$${newParamName}`);\n combinedParams[newParamName] = value;\n }\n\n if (combinedQuery) {\n combinedQuery += ` ${operator} `;\n }\n combinedQuery += `(${newQuery})`;\n }\n\n return [combinedQuery, combinedParams];\n}\n\nfunction collectParams(\n inputData: [string, Record<string, string>][]\n): [string[], Record<string, Any>] {\n const queryParts: string[] = [];\n const params: Record<string, Any> = {};\n\n for (const [queryPart, param] of inputData) {\n queryParts.push(queryPart);\n Object.assign(params, param);\n }\n\n return [queryParts, params];\n}\n\nfunction handleFieldFilter(\n field: string,\n value: Any,\n paramNumber = 1\n): [string, Record<string, Any>] {\n if (typeof field !== \"string\") {\n throw new Error(\n `field should be a string but got: ${typeof field} with value: ${field}`\n );\n }\n\n if (field.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${field}`\n );\n }\n\n if (!IS_IDENTIFIER_REGEX.test(field)) {\n throw new Error(\n `Invalid field name: ${field}. Expected a valid identifier.`\n );\n }\n\n let operator: string;\n let filterValue: Any;\n\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n const keys = Object.keys(value);\n\n if (keys.length !== 1) {\n throw new Error(`Invalid filter condition. Expected a value which is a dictionary\n with a single key that corresponds to an operator but got a dictionary\n with ${keys.length} keys. The first few keys are: ${keys\n .slice(0, 3)\n .join(\", \")}\n `);\n }\n\n operator = keys[0];\n filterValue = value[operator];\n\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw new Error(\n `Invalid operator: ${operator}. Expected one of ${SUPPORTED_OPERATORS}`\n );\n }\n } else {\n operator = \"$eq\";\n filterValue = value;\n }\n\n if (COMPARISONS_TO_NATIVE_OPERATORS.has(operator)) {\n const native = COMPARISONS_TO_NATIVE[operator];\n const querySnippet = `n.${field} ${native} $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n\n return [querySnippet, queryParam];\n } else if (operator === \"$between\") {\n const [low, high] = filterValue;\n const querySnippet = `$param_${paramNumber}_low <= n.${field} <= $param_${paramNumber}_high`;\n const queryParam = {\n [`param_${paramNumber}_low`]: low,\n [`param_${paramNumber}_high`]: high,\n };\n\n return [querySnippet, queryParam];\n } else if ([\"$in\", \"$nin\", \"$like\", \"$ilike\"].includes(operator)) {\n if ([\"$in\", \"$nin\"].includes(operator)) {\n filterValue.forEach((val: Any) => {\n if (\n typeof val !== \"string\" &&\n typeof val !== \"number\" &&\n typeof val !== \"boolean\"\n ) {\n throw new Error(`Unsupported type: ${typeof val} for value: ${val}`);\n }\n });\n }\n\n if (operator === \"$in\") {\n const querySnippet = `n.${field} IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$nin\") {\n const querySnippet = `n.${field} NOT IN $param_${paramNumber}`;\n const queryParam = { [`param_${paramNumber}`]: filterValue };\n return [querySnippet, queryParam];\n } else if (operator === \"$like\") {\n const querySnippet = `n.${field} CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else if (operator === \"$ilike\") {\n const querySnippet = `toLower(n.${field}) CONTAINS $param_${paramNumber}`;\n const queryParam = {\n [`param_${paramNumber}`]: filterValue.slice(0, -1),\n };\n return [querySnippet, queryParam];\n } else {\n throw new Error(\"Not Implemented\");\n }\n } else {\n throw new Error(\"Not Implemented\");\n }\n}\n\nexport function constructMetadataFilter(\n filter: Record<string, Any>\n): [string, Record<string, Any>] {\n if (typeof filter !== \"object\" || filter === null) {\n throw new Error(\"Expected a dictionary representing the filter condition.\");\n }\n\n const entries = Object.entries(filter);\n\n if (entries.length === 1) {\n const [key, value] = entries[0];\n\n if (key.startsWith(\"$\")) {\n if (![\"$and\", \"$or\"].includes(key.toLowerCase())) {\n throw new Error(\n `Invalid filter condition. Expected $and or $or but got: ${key}`\n );\n }\n\n if (!Array.isArray(value)) {\n throw new Error(\n `Expected an array for logical conditions, but got ${typeof value} for value: ${value}`\n );\n }\n\n const operation = key.toLowerCase() === \"$and\" ? \"AND\" : \"OR\";\n const combinedQueries = combineQueries(\n value.map((v) => constructMetadataFilter(v)),\n operation\n );\n\n return combinedQueries;\n } else {\n return handleFieldFilter(key, value);\n }\n } else if (entries.length > 1) {\n for (const [key] of entries) {\n if (key.startsWith(\"$\")) {\n throw new Error(\n `Invalid filter condition. Expected a field but got an operator: ${key}`\n );\n }\n }\n\n const and_multiple = collectParams(\n entries.map(([field, val], index) =>\n handleFieldFilter(field, val, index + 1)\n )\n );\n\n if (and_multiple.length >= 1) {\n return [and_multiple[0].join(\" AND \"), and_multiple[1]];\n } else {\n throw Error(\n \"Invalid filter condition. Expected a dictionary but got an empty dictionary\"\n );\n }\n } else {\n throw new Error(\"Filter condition contains no entries.\");\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/neo4j",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Neo4j integrations for LangChain.js",
|
|
5
5
|
"author": "LangChain",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"neo4j-driver": "^6.0.1"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@langchain/core": "^1.
|
|
21
|
-
"@langchain/classic": "1.0.
|
|
20
|
+
"@langchain/core": "^1.2.0",
|
|
21
|
+
"@langchain/classic": "1.0.35"
|
|
22
22
|
},
|
|
23
23
|
"peerDependenciesMeta": {
|
|
24
24
|
"@langchain/classic": {
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@tsconfig/recommended": "^1.0.10",
|
|
30
|
-
"@types/node": "^25.
|
|
31
|
-
"@vitest/coverage-v8": "^
|
|
30
|
+
"@types/node": "^25.9.1",
|
|
31
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
32
32
|
"dotenv": "^17.4.0",
|
|
33
33
|
"dpdm": "^3.14.0",
|
|
34
34
|
"rimraf": "^6.1.3",
|
|
35
|
-
"typescript": "~
|
|
36
|
-
"vitest": "^4.1.
|
|
37
|
-
"@langchain/classic": "1.0.
|
|
38
|
-
"@langchain/core": "^1.
|
|
35
|
+
"typescript": "~6.0.3",
|
|
36
|
+
"vitest": "^4.1.8",
|
|
37
|
+
"@langchain/classic": "1.0.35",
|
|
38
|
+
"@langchain/core": "^1.2.0",
|
|
39
39
|
"@langchain/standard-tests": "0.0.23",
|
|
40
40
|
"@langchain/tsconfig": "0.0.1"
|
|
41
41
|
},
|