@langchain/neo4j 0.0.1 → 0.1.0

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.
@@ -29,8 +29,7 @@ var Neo4jChatMessageHistory = class Neo4jChatMessageHistory extends BaseListChat
29
29
  if (url && username && password) try {
30
30
  this.driver = neo4j.driver(url, auth.basic(username, password));
31
31
  } catch (e) {
32
- const error = e;
33
- throw new Error(`Could not create a Neo4j driver instance. Please check the connection details.\nCause: ${error.message}`);
32
+ throw new Error(`Could not create a Neo4j driver instance. Please check the connection details.\nCause: ${e.message}`);
34
33
  }
35
34
  else throw new Error("Neo4j connection details not provided.");
36
35
  }
@@ -39,8 +38,7 @@ var Neo4jChatMessageHistory = class Neo4jChatMessageHistory extends BaseListChat
39
38
  try {
40
39
  await instance.verifyConnectivity();
41
40
  } catch (e) {
42
- const error = e;
43
- throw new Error(`Could not verify connection to the Neo4j database.\nCause: ${error.message}`);
41
+ throw new Error(`Could not verify connection to the Neo4j database.\nCause: ${e.message}`);
44
42
  }
45
43
  return instance;
46
44
  }
@@ -62,8 +60,7 @@ var Neo4jChatMessageHistory = class Neo4jChatMessageHistory extends BaseListChat
62
60
  const { records } = await this.driver.executeQuery(getMessagesCypherQuery, { sessionId: this.sessionId });
63
61
  return mapStoredMessagesToChatMessages(records.map((record) => record.get("result")));
64
62
  } catch (e) {
65
- const error = e;
66
- throw new Error(`Ohno! Couldn't get messages.\nCause: ${error.message}`);
63
+ throw new Error(`Ohno! Couldn't get messages.\nCause: ${e.message}`);
67
64
  }
68
65
  }
69
66
  async addMessage(message) {
@@ -85,8 +82,7 @@ var Neo4jChatMessageHistory = class Neo4jChatMessageHistory extends BaseListChat
85
82
  content: message.content
86
83
  });
87
84
  } catch (e) {
88
- const error = e;
89
- throw new Error(`Ohno! Couldn't add message.\nCause: ${error.message}`);
85
+ throw new Error(`Ohno! Couldn't add message.\nCause: ${e.message}`);
90
86
  }
91
87
  }
92
88
  async clear() {
@@ -98,8 +94,7 @@ var Neo4jChatMessageHistory = class Neo4jChatMessageHistory extends BaseListChat
98
94
  try {
99
95
  await this.driver.executeQuery(clearMessagesCypherQuery, { sessionId: this.sessionId });
100
96
  } catch (e) {
101
- const error = e;
102
- throw new Error(`Ohno! Couldn't clear chat history.\nCause: ${error.message}`);
97
+ throw new Error(`Ohno! Couldn't clear chat history.\nCause: ${e.message}`);
103
98
  }
104
99
  }
105
100
  async close() {
@@ -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 \"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;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MACR,0FAA0F,MAAM,UACjG;;MAGH,OAAM,IAAI,MAAM,yCAAyC;;CAI7D,aAAa,WACX,OACkC;EAClC,MAAM,WAAW,IAAI,wBAAwB,MAAM;AAEnD,MAAI;AACF,SAAM,SAAS,oBAAoB;WAC5B,GAAG;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MACR,8DAA8D,MAAM,UACrE;;AAGH,SAAO;;CAGT,MAAM,qBAA0C;AAE9C,SADqB,MAAM,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,CAAC,CAEtB;WACxC,GAAG;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MAAM,wCAAwC,MAAM,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;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MAAM,uCAAuC,MAAM,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;GACV,MAAM,QAAQ;AACd,SAAM,IAAI,MACR,8CAA8C,MAAM,UACrD;;;CAIL,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO"}
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 \"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,0FAFY,EAEoF,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,8DAFY,EAEwD,UACrE;;AAGH,SAAO;;CAGT,MAAM,qBAA0C;AAE9C,SADqB,MAAM,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,CAAC,CAEtB;WACxC,GAAG;AAEV,SAAM,IAAI,MAAM,wCADF,EACgD,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,uCADF,EAC+C,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,8CAFY,EAEwC,UACrD;;;CAIL,MAAM,QAAQ;AACZ,QAAM,KAAK,OAAO,OAAO"}
@@ -0,0 +1 @@
1
+ import { DistanceStrategy, IndexType, Metadata, Neo4jVectorStore, Neo4jVectorStoreArgs, SearchType, constructMetadataFilter, isVersionLessThan, removeLuceneChars } from "./neo4j_vector.cjs";
@@ -1 +1 @@
1
- {"version":3,"file":"neo4j_vector.cjs","names":["VectorStore","neo4j","Document"],"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 \"uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// eslint-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 // eslint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // eslint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // eslint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // eslint-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"],"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 \"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 \"uuid\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\n\n// eslint-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 // eslint-disable-next-line\n obj instanceof (neo4j.types.Node as any) ||\n // eslint-disable-next-line\n obj instanceof (neo4j.types.Relationship as any)\n ) {\n return obj.properties;\n // eslint-disable-next-line\n } else if (obj instanceof (neo4j.types.Path as any)) {\n // eslint-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.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 \"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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"}