langchain 0.0.166 → 0.0.167

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.
Files changed (49) hide show
  1. package/dist/embeddings/bedrock.cjs +43 -22
  2. package/dist/embeddings/bedrock.d.ts +11 -4
  3. package/dist/embeddings/bedrock.js +43 -22
  4. package/dist/llms/yandex.cjs +100 -0
  5. package/dist/llms/yandex.d.ts +40 -0
  6. package/dist/llms/yandex.js +96 -0
  7. package/dist/load/import_constants.cjs +2 -0
  8. package/dist/load/import_constants.js +2 -0
  9. package/dist/load/import_map.cjs +4 -2
  10. package/dist/load/import_map.d.ts +2 -0
  11. package/dist/load/import_map.js +2 -0
  12. package/dist/retrievers/multi_vector.d.ts +3 -3
  13. package/dist/retrievers/parent_document.cjs +6 -16
  14. package/dist/retrievers/parent_document.d.ts +5 -12
  15. package/dist/retrievers/parent_document.js +6 -16
  16. package/dist/schema/storage.d.ts +28 -1
  17. package/dist/storage/encoder_backed.cjs +14 -2
  18. package/dist/storage/encoder_backed.d.ts +2 -0
  19. package/dist/storage/encoder_backed.js +12 -1
  20. package/dist/storage/in_memory.cjs +1 -1
  21. package/dist/storage/in_memory.js +1 -1
  22. package/dist/storage/ioredis.cjs +4 -4
  23. package/dist/storage/ioredis.js +4 -4
  24. package/dist/storage/vercel_kv.cjs +146 -0
  25. package/dist/storage/vercel_kv.d.ts +46 -0
  26. package/dist/storage/vercel_kv.js +142 -0
  27. package/dist/stores/doc/in_memory.cjs +13 -0
  28. package/dist/stores/doc/in_memory.d.ts +6 -1
  29. package/dist/stores/doc/in_memory.js +13 -0
  30. package/dist/vectorstores/cassandra.cjs +4 -2
  31. package/dist/vectorstores/cassandra.js +4 -2
  32. package/dist/vectorstores/elasticsearch.cjs +3 -1
  33. package/dist/vectorstores/elasticsearch.js +3 -1
  34. package/dist/vectorstores/neo4j_vector.cjs +578 -0
  35. package/dist/vectorstores/neo4j_vector.d.ts +61 -0
  36. package/dist/vectorstores/neo4j_vector.js +548 -0
  37. package/llms/yandex.cjs +1 -0
  38. package/llms/yandex.d.ts +1 -0
  39. package/llms/yandex.js +1 -0
  40. package/package.json +38 -1
  41. package/storage/encoder_backed.cjs +1 -0
  42. package/storage/encoder_backed.d.ts +1 -0
  43. package/storage/encoder_backed.js +1 -0
  44. package/storage/vercel_kv.cjs +1 -0
  45. package/storage/vercel_kv.d.ts +1 -0
  46. package/storage/vercel_kv.js +1 -0
  47. package/vectorstores/neo4j_vector.cjs +1 -0
  48. package/vectorstores/neo4j_vector.d.ts +1 -0
  49. package/vectorstores/neo4j_vector.js +1 -0
@@ -170,7 +170,8 @@ class CassandraStore extends base_js_1.VectorStore {
170
170
  : ""}
171
171
  vector VECTOR<FLOAT, ${this.dimensions}>
172
172
  );`);
173
- await this.client.execute(`CREATE CUSTOM INDEX IF NOT EXISTS ann_index
173
+ await this.client
174
+ .execute(`CREATE CUSTOM INDEX IF NOT EXISTS idx_vector_${this.table}
174
175
  ON ${this.keyspace}.${this.table}(vector) USING 'StorageAttachedIndex';`);
175
176
  this.isInitialized = true;
176
177
  }
@@ -188,7 +189,8 @@ class CassandraStore extends base_js_1.VectorStore {
188
189
  const document = documents[index];
189
190
  const metadataColNames = Object.keys(document.metadata);
190
191
  const metadataVals = Object.values(document.metadata);
191
- const query = `INSERT INTO ${this.keyspace}.${this.table} (vector, text${metadataColNames.length > 0 ? ", " + metadataColNames.join(", ") : ""}) VALUES ([${vector}], '${document.pageContent}'${metadataVals.length > 0
192
+ const metadataInsert = metadataColNames.length > 0 ? ", " + metadataColNames.join(", ") : "";
193
+ const query = `INSERT INTO ${this.keyspace}.${this.table} (vector, text${metadataInsert}) VALUES ([${vector}], '${document.pageContent}'${metadataVals.length > 0
192
194
  ? ", " +
193
195
  metadataVals
194
196
  .map((val) => (typeof val === "number" ? val : `'${val}'`))
@@ -167,7 +167,8 @@ export class CassandraStore extends VectorStore {
167
167
  : ""}
168
168
  vector VECTOR<FLOAT, ${this.dimensions}>
169
169
  );`);
170
- await this.client.execute(`CREATE CUSTOM INDEX IF NOT EXISTS ann_index
170
+ await this.client
171
+ .execute(`CREATE CUSTOM INDEX IF NOT EXISTS idx_vector_${this.table}
171
172
  ON ${this.keyspace}.${this.table}(vector) USING 'StorageAttachedIndex';`);
172
173
  this.isInitialized = true;
173
174
  }
@@ -185,7 +186,8 @@ export class CassandraStore extends VectorStore {
185
186
  const document = documents[index];
186
187
  const metadataColNames = Object.keys(document.metadata);
187
188
  const metadataVals = Object.values(document.metadata);
188
- const query = `INSERT INTO ${this.keyspace}.${this.table} (vector, text${metadataColNames.length > 0 ? ", " + metadataColNames.join(", ") : ""}) VALUES ([${vector}], '${document.pageContent}'${metadataVals.length > 0
189
+ const metadataInsert = metadataColNames.length > 0 ? ", " + metadataColNames.join(", ") : "";
190
+ const query = `INSERT INTO ${this.keyspace}.${this.table} (vector, text${metadataInsert}) VALUES ([${vector}], '${document.pageContent}'${metadataVals.length > 0
189
191
  ? ", " +
190
192
  metadataVals
191
193
  .map((val) => (typeof val === "number" ? val : `'${val}'`))
@@ -86,7 +86,9 @@ class ElasticVectorSearch extends base_js_1.VectorStore {
86
86
  this.m = args.vectorSearchOptions?.m ?? 16;
87
87
  this.efConstruction = args.vectorSearchOptions?.efConstruction ?? 100;
88
88
  this.candidates = args.vectorSearchOptions?.candidates ?? 200;
89
- this.client = args.client;
89
+ this.client = args.client.child({
90
+ headers: { "user-agent": "langchain-js-vs/0.0.1" },
91
+ });
90
92
  this.indexName = args.indexName ?? "documents";
91
93
  }
92
94
  /**
@@ -60,7 +60,9 @@ export class ElasticVectorSearch extends VectorStore {
60
60
  this.m = args.vectorSearchOptions?.m ?? 16;
61
61
  this.efConstruction = args.vectorSearchOptions?.efConstruction ?? 100;
62
62
  this.candidates = args.vectorSearchOptions?.candidates ?? 200;
63
- this.client = args.client;
63
+ this.client = args.client.child({
64
+ headers: { "user-agent": "langchain-js-vs/0.0.1" },
65
+ });
64
66
  this.indexName = args.indexName ?? "documents";
65
67
  }
66
68
  /**
@@ -0,0 +1,578 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Neo4jVectorStore = void 0;
30
+ const neo4j_driver_1 = __importDefault(require("neo4j-driver"));
31
+ const uuid = __importStar(require("uuid"));
32
+ const document_js_1 = require("../document.cjs");
33
+ const base_js_1 = require("./base.cjs");
34
+ const DEFAULT_SEARCH_TYPE = "vector";
35
+ const DEFAULT_DISTANCE_STRATEGY = "cosine";
36
+ class Neo4jVectorStore extends base_js_1.VectorStore {
37
+ _vectorstoreType() {
38
+ return "neo4jvector";
39
+ }
40
+ constructor(embeddings, config) {
41
+ super(embeddings, config);
42
+ Object.defineProperty(this, "driver", {
43
+ enumerable: true,
44
+ configurable: true,
45
+ writable: true,
46
+ value: void 0
47
+ });
48
+ Object.defineProperty(this, "database", {
49
+ enumerable: true,
50
+ configurable: true,
51
+ writable: true,
52
+ value: void 0
53
+ });
54
+ Object.defineProperty(this, "preDeleteCollection", {
55
+ enumerable: true,
56
+ configurable: true,
57
+ writable: true,
58
+ value: void 0
59
+ });
60
+ Object.defineProperty(this, "nodeLabel", {
61
+ enumerable: true,
62
+ configurable: true,
63
+ writable: true,
64
+ value: void 0
65
+ });
66
+ Object.defineProperty(this, "embeddingNodeProperty", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: void 0
71
+ });
72
+ Object.defineProperty(this, "embeddingDimension", {
73
+ enumerable: true,
74
+ configurable: true,
75
+ writable: true,
76
+ value: void 0
77
+ });
78
+ Object.defineProperty(this, "textNodeProperty", {
79
+ enumerable: true,
80
+ configurable: true,
81
+ writable: true,
82
+ value: void 0
83
+ });
84
+ Object.defineProperty(this, "keywordIndexName", {
85
+ enumerable: true,
86
+ configurable: true,
87
+ writable: true,
88
+ value: void 0
89
+ });
90
+ Object.defineProperty(this, "indexName", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ Object.defineProperty(this, "retrievalQuery", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: void 0
101
+ });
102
+ Object.defineProperty(this, "searchType", {
103
+ enumerable: true,
104
+ configurable: true,
105
+ writable: true,
106
+ value: void 0
107
+ });
108
+ Object.defineProperty(this, "distanceStrategy", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: DEFAULT_DISTANCE_STRATEGY
113
+ });
114
+ }
115
+ static async initialize(embeddings, config) {
116
+ const store = new Neo4jVectorStore(embeddings, config);
117
+ await store._initializeDriver(config);
118
+ await store._verifyConnectivity();
119
+ const { preDeleteCollection = false, nodeLabel = "Chunk", textNodeProperty = "text", embeddingNodeProperty = "embedding", keywordIndexName = "keyword", indexName = "vector", retrievalQuery = "", searchType = DEFAULT_SEARCH_TYPE, } = config;
120
+ store.embeddingDimension = (await embeddings.embedQuery("foo")).length;
121
+ store.preDeleteCollection = preDeleteCollection;
122
+ store.nodeLabel = nodeLabel;
123
+ store.textNodeProperty = textNodeProperty;
124
+ store.embeddingNodeProperty = embeddingNodeProperty;
125
+ store.keywordIndexName = keywordIndexName;
126
+ store.indexName = indexName;
127
+ store.retrievalQuery = retrievalQuery;
128
+ store.searchType = searchType;
129
+ if (store.preDeleteCollection) {
130
+ await store._dropIndex();
131
+ }
132
+ return store;
133
+ }
134
+ async _initializeDriver({ url, username, password, database = "neo4j", }) {
135
+ try {
136
+ this.driver = neo4j_driver_1.default.driver(url, neo4j_driver_1.default.auth.basic(username, password));
137
+ this.database = database;
138
+ }
139
+ catch (error) {
140
+ throw new Error("Could not create a Neo4j driver instance. Please check the connection details.");
141
+ }
142
+ }
143
+ async _verifyConnectivity() {
144
+ await this.driver.verifyAuthentication();
145
+ }
146
+ async close() {
147
+ await this.driver.close();
148
+ }
149
+ async _dropIndex() {
150
+ try {
151
+ await this.query(`
152
+ MATCH (n:\`${this.nodeLabel}\`)
153
+ CALL {
154
+ WITH n
155
+ DETACH DELETE n
156
+ }
157
+ IN TRANSACTIONS OF 10000 ROWS;
158
+ `);
159
+ await this.query(`DROP INDEX ${this.indexName}`);
160
+ }
161
+ catch (error) {
162
+ console.error("An error occurred while dropping the index:", error);
163
+ }
164
+ }
165
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
166
+ async query(query, params = {}) {
167
+ const session = this.driver.session({ database: this.database });
168
+ const result = await session.run(query, params);
169
+ return toObjects(result.records);
170
+ }
171
+ static async fromTexts(texts,
172
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
+ metadatas, embeddings, config) {
174
+ const docs = [];
175
+ for (let i = 0; i < texts.length; i += 1) {
176
+ const metadata = Array.isArray(metadatas) ? metadatas[i] : metadatas;
177
+ const newDoc = new document_js_1.Document({
178
+ pageContent: texts[i],
179
+ metadata,
180
+ });
181
+ docs.push(newDoc);
182
+ }
183
+ return Neo4jVectorStore.fromDocuments(docs, embeddings, config);
184
+ }
185
+ static async fromDocuments(docs, embeddings, config) {
186
+ const { searchType = DEFAULT_SEARCH_TYPE, createIdIndex = true, textNodeProperties = [], } = config;
187
+ const store = await this.initialize(embeddings, config);
188
+ const embeddingDimension = await store.retrieveExistingIndex();
189
+ if (!embeddingDimension) {
190
+ await store.createNewIndex();
191
+ }
192
+ else if (store.embeddingDimension !== embeddingDimension) {
193
+ throw new Error(`Index with name "${store.indexName}" already exists. The provided embedding function and vector index dimensions do not match.
194
+ Embedding function dimension: ${store.embeddingDimension}
195
+ Vector index dimension: ${embeddingDimension}`);
196
+ }
197
+ if (searchType === "hybrid") {
198
+ const ftsNodeLabel = await store.retrieveExistingFtsIndex();
199
+ if (!ftsNodeLabel) {
200
+ await store.createNewKeywordIndex(textNodeProperties);
201
+ }
202
+ else {
203
+ if (ftsNodeLabel !== store.nodeLabel) {
204
+ throw Error("Vector and keyword index don't index the same node label");
205
+ }
206
+ }
207
+ }
208
+ if (createIdIndex) {
209
+ await store.query(`CREATE CONSTRAINT IF NOT EXISTS FOR (n:${store.nodeLabel}) REQUIRE n.id IS UNIQUE;`);
210
+ }
211
+ await store.addDocuments(docs);
212
+ return store;
213
+ }
214
+ static async fromExistingIndex(embeddings, config) {
215
+ const { searchType = DEFAULT_SEARCH_TYPE, keywordIndexName = "keyword" } = config;
216
+ if (searchType === "hybrid" && !keywordIndexName) {
217
+ throw Error("keyword_index name has to be specified when using hybrid search option");
218
+ }
219
+ const store = await this.initialize(embeddings, config);
220
+ const embeddingDimension = await store.retrieveExistingIndex();
221
+ if (!embeddingDimension) {
222
+ throw Error("The specified vector index name does not exist. Make sure to check if you spelled it correctly");
223
+ }
224
+ if (store.embeddingDimension !== embeddingDimension) {
225
+ throw new Error(`The provided embedding function and vector index dimensions do not match.
226
+ Embedding function dimension: ${store.embeddingDimension}
227
+ Vector index dimension: ${embeddingDimension}`);
228
+ }
229
+ if (searchType === "hybrid") {
230
+ const ftsNodeLabel = await store.retrieveExistingFtsIndex();
231
+ if (!ftsNodeLabel) {
232
+ throw Error("The specified keyword index name does not exist. Make sure to check if you spelled it correctly");
233
+ }
234
+ else {
235
+ if (ftsNodeLabel !== store.nodeLabel) {
236
+ throw Error("Vector and keyword index don't index the same node label");
237
+ }
238
+ }
239
+ }
240
+ return store;
241
+ }
242
+ static async fromExistingGraph(embeddings, config) {
243
+ const { textNodeProperties = [], embeddingNodeProperty, searchType = DEFAULT_SEARCH_TYPE, retrievalQuery = "", nodeLabel, } = config;
244
+ let _retrievalQuery = retrievalQuery;
245
+ if (textNodeProperties.length === 0) {
246
+ throw Error("Parameter `text_node_properties` must not be an empty array");
247
+ }
248
+ if (!retrievalQuery) {
249
+ _retrievalQuery = `
250
+ RETURN reduce(str='', k IN ${JSON.stringify(textNodeProperties)} |
251
+ str + '\\n' + k + ': ' + coalesce(node[k], '')) AS text,
252
+ node {.*, \`${embeddingNodeProperty}\`: Null, id: Null, ${textNodeProperties
253
+ .map((prop) => `\`${prop}\`: Null`)
254
+ .join(", ")} } AS metadata, score
255
+ `;
256
+ }
257
+ const store = await this.initialize(embeddings, {
258
+ ...config,
259
+ retrievalQuery: _retrievalQuery,
260
+ });
261
+ const embeddingDimension = await store.retrieveExistingIndex();
262
+ if (!embeddingDimension) {
263
+ await store.createNewIndex();
264
+ }
265
+ else if (store.embeddingDimension !== embeddingDimension) {
266
+ throw new Error(`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}`);
267
+ }
268
+ if (searchType === "hybrid") {
269
+ const ftsNodeLabel = await store.retrieveExistingFtsIndex(textNodeProperties);
270
+ if (!ftsNodeLabel) {
271
+ await store.createNewKeywordIndex(textNodeProperties);
272
+ }
273
+ else {
274
+ if (ftsNodeLabel !== store.nodeLabel) {
275
+ throw Error("Vector and keyword index don't index the same node label");
276
+ }
277
+ }
278
+ }
279
+ // eslint-disable-next-line no-constant-condition
280
+ while (true) {
281
+ const fetchQuery = `
282
+ MATCH (n:\`${nodeLabel}\`)
283
+ WHERE n.${embeddingNodeProperty} IS null
284
+ AND any(k in $props WHERE n[k] IS NOT null)
285
+ RETURN elementId(n) AS id, reduce(str='', k IN $props |
286
+ str + '\\n' + k + ':' + coalesce(n[k], '')) AS text
287
+ LIMIT 1000
288
+ `;
289
+ const data = await store.query(fetchQuery, { props: textNodeProperties });
290
+ if (!data) {
291
+ continue;
292
+ }
293
+ const textEmbeddings = await embeddings.embedDocuments(data.map((el) => el.text));
294
+ const params = {
295
+ data: data.map((el, index) => ({
296
+ id: el.id,
297
+ embedding: textEmbeddings[index],
298
+ })),
299
+ };
300
+ await store.query(`
301
+ UNWIND $data AS row
302
+ MATCH (n:\`${nodeLabel}\`)
303
+ WHERE elementId(n) = row.id
304
+ CALL db.create.setVectorProperty(n, '${embeddingNodeProperty}', row.embedding)
305
+ YIELD node RETURN count(*)
306
+ `, params);
307
+ if (data.length < 1000) {
308
+ break;
309
+ }
310
+ }
311
+ return store;
312
+ }
313
+ async createNewIndex() {
314
+ const indexQuery = `
315
+ CALL db.index.vector.createNodeIndex(
316
+ $index_name,
317
+ $node_label,
318
+ $embedding_node_property,
319
+ toInteger($embedding_dimension),
320
+ $similarity_metric
321
+ )
322
+ `;
323
+ const parameters = {
324
+ index_name: this.indexName,
325
+ node_label: this.nodeLabel,
326
+ embedding_node_property: this.embeddingNodeProperty,
327
+ embedding_dimension: this.embeddingDimension,
328
+ similarity_metric: this.distanceStrategy,
329
+ };
330
+ await this.query(indexQuery, parameters);
331
+ }
332
+ async retrieveExistingIndex() {
333
+ let indexInformation = await this.query(`
334
+ SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options
335
+ WHERE type = 'VECTOR' AND (name = $index_name
336
+ OR (labelsOrTypes[0] = $node_label AND
337
+ properties[0] = $embedding_node_property))
338
+ RETURN name, labelsOrTypes, properties, options
339
+ `, {
340
+ index_name: this.indexName,
341
+ node_label: this.nodeLabel,
342
+ embedding_node_property: this.embeddingNodeProperty,
343
+ });
344
+ if (indexInformation) {
345
+ indexInformation = this.sortByIndexName(indexInformation, this.indexName);
346
+ try {
347
+ const [index] = indexInformation;
348
+ const [labelOrType] = index.labelsOrTypes;
349
+ const [property] = index.properties;
350
+ this.indexName = index.name;
351
+ this.nodeLabel = labelOrType;
352
+ this.embeddingNodeProperty = property;
353
+ const embeddingDimension = index.options.indexConfig["vector.dimensions"];
354
+ return Number(embeddingDimension);
355
+ }
356
+ catch (error) {
357
+ return null;
358
+ }
359
+ }
360
+ return null;
361
+ }
362
+ async retrieveExistingFtsIndex(textNodeProperties = []) {
363
+ const indexInformation = await this.query(`
364
+ SHOW INDEXES YIELD name, type, labelsOrTypes, properties, options
365
+ WHERE type = 'FULLTEXT' AND (name = $keyword_index_name
366
+ OR (labelsOrTypes = [$node_label] AND
367
+ properties = $text_node_property))
368
+ RETURN name, labelsOrTypes, properties, options
369
+ `, {
370
+ keyword_index_name: this.keywordIndexName,
371
+ node_label: this.nodeLabel,
372
+ text_node_property: textNodeProperties.length > 0
373
+ ? textNodeProperties
374
+ : [this.textNodeProperty],
375
+ });
376
+ if (indexInformation) {
377
+ // Sort the index information by index name
378
+ const sortedIndexInformation = this.sortByIndexName(indexInformation, this.indexName);
379
+ try {
380
+ const [index] = sortedIndexInformation;
381
+ const [labelOrType] = index.labelsOrTypes;
382
+ const [property] = index.properties;
383
+ this.keywordIndexName = index.name;
384
+ this.textNodeProperty = property;
385
+ this.nodeLabel = labelOrType;
386
+ return labelOrType;
387
+ }
388
+ catch (error) {
389
+ return null;
390
+ }
391
+ }
392
+ return null;
393
+ }
394
+ async createNewKeywordIndex(textNodeProperties = []) {
395
+ const nodeProps = textNodeProperties.length > 0
396
+ ? textNodeProperties
397
+ : [this.textNodeProperty];
398
+ // Construct the Cypher query to create a new full text index
399
+ const ftsIndexQuery = `
400
+ CREATE FULLTEXT INDEX ${this.keywordIndexName}
401
+ FOR (n:\`${this.nodeLabel}\`) ON EACH
402
+ [${nodeProps.map((prop) => `n.\`${prop}\``).join(", ")}]
403
+ `;
404
+ await this.query(ftsIndexQuery);
405
+ }
406
+ sortByIndexName(
407
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
408
+ values, indexName
409
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
410
+ ) {
411
+ return values.sort((a, b) => (a.index_name === indexName ? -1 : 0) -
412
+ (b.index_name === indexName ? -1 : 0));
413
+ }
414
+ async addVectors(vectors, documents,
415
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
416
+ metadatas, ids) {
417
+ let _ids = ids;
418
+ let _metadatas = metadatas;
419
+ if (!_ids) {
420
+ _ids = documents.map(() => uuid.v1());
421
+ }
422
+ if (!metadatas) {
423
+ _metadatas = documents.map(() => ({}));
424
+ }
425
+ const importQuery = `
426
+ UNWIND $data AS row
427
+ CALL {
428
+ WITH row
429
+ MERGE (c:\`${this.nodeLabel}\` {id: row.id})
430
+ WITH c, row
431
+ CALL db.create.setVectorProperty(c, '${this.embeddingNodeProperty}', row.embedding)
432
+ YIELD node
433
+ SET c.\`${this.textNodeProperty}\` = row.text
434
+ SET c += row.metadata
435
+ } IN TRANSACTIONS OF 1000 ROWS
436
+ `;
437
+ const parameters = {
438
+ data: documents.map(({ pageContent, metadata }, index) => ({
439
+ text: pageContent,
440
+ metadata: _metadatas ? _metadatas[index] : metadata,
441
+ embedding: vectors[index],
442
+ id: _ids ? _ids[index] : null,
443
+ })),
444
+ };
445
+ await this.query(importQuery, parameters);
446
+ return _ids;
447
+ }
448
+ async addDocuments(documents) {
449
+ const texts = documents.map(({ pageContent }) => pageContent);
450
+ return this.addVectors(await this.embeddings.embedDocuments(texts), documents);
451
+ }
452
+ async similaritySearch(query, k = 4) {
453
+ const embedding = await this.embeddings.embedQuery(query);
454
+ const results = await this.similaritySearchVectorWithScore(embedding, k, query);
455
+ return results.map((result) => result[0]);
456
+ }
457
+ async similaritySearchVectorWithScore(vector, k, query) {
458
+ const defaultRetrieval = `
459
+ RETURN node.${this.textNodeProperty} AS text, score,
460
+ node {.*, ${this.textNodeProperty}: Null,
461
+ ${this.embeddingNodeProperty}: Null, id: Null } AS metadata
462
+ `;
463
+ const retrievalQuery = this.retrievalQuery
464
+ ? this.retrievalQuery
465
+ : defaultRetrieval;
466
+ const readQuery = `${getSearchIndexQuery(this.searchType)} ${retrievalQuery}`;
467
+ const parameters = {
468
+ index: this.indexName,
469
+ k: Number(k),
470
+ embedding: vector,
471
+ keyword_index: this.keywordIndexName,
472
+ query,
473
+ };
474
+ const results = await this.query(readQuery, parameters);
475
+ if (results) {
476
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
477
+ const docs = results.map((result) => [
478
+ new document_js_1.Document({
479
+ pageContent: result.text,
480
+ metadata: Object.fromEntries(Object.entries(result.metadata).filter(([_, v]) => v !== null)),
481
+ }),
482
+ result.score,
483
+ ]);
484
+ return docs;
485
+ }
486
+ return [];
487
+ }
488
+ }
489
+ exports.Neo4jVectorStore = Neo4jVectorStore;
490
+ function toObjects(records) {
491
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
492
+ const recordValues = records.map((record) => {
493
+ const rObj = record.toObject();
494
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
495
+ const out = {};
496
+ Object.keys(rObj).forEach((key) => {
497
+ out[key] = itemIntToString(rObj[key]);
498
+ });
499
+ return out;
500
+ });
501
+ return recordValues;
502
+ }
503
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
504
+ function itemIntToString(item) {
505
+ if (neo4j_driver_1.default.isInt(item))
506
+ return item.toString();
507
+ if (Array.isArray(item))
508
+ return item.map((ii) => itemIntToString(ii));
509
+ if (["number", "string", "boolean"].indexOf(typeof item) !== -1)
510
+ return item;
511
+ if (item === null)
512
+ return item;
513
+ if (typeof item === "object")
514
+ return objIntToString(item);
515
+ }
516
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
517
+ function objIntToString(obj) {
518
+ const entry = extractFromNeoObjects(obj);
519
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
520
+ let newObj = null;
521
+ if (Array.isArray(entry)) {
522
+ newObj = entry.map((item) => itemIntToString(item));
523
+ }
524
+ else if (entry !== null && typeof entry === "object") {
525
+ newObj = {};
526
+ Object.keys(entry).forEach((key) => {
527
+ newObj[key] = itemIntToString(entry[key]);
528
+ });
529
+ }
530
+ return newObj;
531
+ }
532
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
533
+ function extractFromNeoObjects(obj) {
534
+ if (
535
+ // eslint-disable-next-line
536
+ obj instanceof neo4j_driver_1.default.types.Node ||
537
+ // eslint-disable-next-line
538
+ obj instanceof neo4j_driver_1.default.types.Relationship) {
539
+ return obj.properties;
540
+ // eslint-disable-next-line
541
+ }
542
+ else if (obj instanceof neo4j_driver_1.default.types.Path) {
543
+ // eslint-disable-next-line
544
+ return [].concat.apply([], extractPathForRows(obj));
545
+ }
546
+ return obj;
547
+ }
548
+ function extractPathForRows(path) {
549
+ let { segments } = path;
550
+ // Zero length path. No relationship, end === start
551
+ if (!Array.isArray(path.segments) || path.segments.length < 1) {
552
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
553
+ segments = [{ ...path, end: null }];
554
+ }
555
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
556
+ return segments.map((segment) => [
557
+ objIntToString(segment.start),
558
+ objIntToString(segment.relationship),
559
+ objIntToString(segment.end),
560
+ ].filter((part) => part !== null));
561
+ }
562
+ function getSearchIndexQuery(searchType) {
563
+ const typeToQueryMap = {
564
+ vector: "CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score",
565
+ hybrid: `
566
+ CALL {
567
+ CALL db.index.vector.queryNodes($index, $k, $embedding) YIELD node, score
568
+ RETURN node, score UNION
569
+ CALL db.index.fulltext.queryNodes($keyword_index, $query, {limit: $k}) YIELD node, score
570
+ WITH collect({node: node, score: score}) AS nodes, max(score) AS max
571
+ UNWIND nodes AS n
572
+ RETURN n.node AS node, (n.score / max) AS score
573
+ }
574
+ WITH node, max(score) AS score ORDER BY score DESC LIMIT toInteger($k)
575
+ `,
576
+ };
577
+ return typeToQueryMap[searchType];
578
+ }