diff --git a/packages/core/src/Node.ts b/packages/core/src/Node.ts index 386c37b44c6a67f83a11fa0b044d22d2434fa814..2e275805e29917f198f48aa4258ffca3c81284a0 100644 --- a/packages/core/src/Node.ts +++ b/packages/core/src/Node.ts @@ -141,11 +141,13 @@ export abstract class BaseNode<T extends Metadata = Metadata> { } /** - * Used with built in JSON.stringify + * Creates a deep-clone of the node as JSON * @returns */ toJSON(): Record<string, any> { - return { ...this, type: this.getType() }; + const json: Record<string, any> = structuredClone(this); + json.type = this.getType(); + return json; } } diff --git a/packages/core/src/storage/vectorStore/utils.ts b/packages/core/src/storage/vectorStore/utils.ts index f7905fcaee2b8a350e05fc23ff7c0cdebc8d2119..1c69ae57ef5ed4ca334350aa574aff531dce10a3 100644 --- a/packages/core/src/storage/vectorStore/utils.ts +++ b/packages/core/src/storage/vectorStore/utils.ts @@ -16,15 +16,14 @@ export function nodeToMetadata( textField: string = DEFAULT_TEXT_KEY, flatMetadata: boolean = false, ): Metadata { - const nodeObj = node.toJSON(); - const { metadata, embedding, ...rest } = nodeObj; + const { metadata, embedding, ...rest } = node.toJSON(); if (flatMetadata) { - validateIsFlat(node.metadata); + validateIsFlat(metadata); } if (removeText) { - nodeObj[textField] = ""; + rest[textField] = ""; } metadata["_node_content"] = JSON.stringify(rest); diff --git a/packages/core/src/tests/VectorStore.test.ts b/packages/core/src/tests/VectorStore.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..734a6aa56fc26f7be1d11eb8cc865d4b190035f9 --- /dev/null +++ b/packages/core/src/tests/VectorStore.test.ts @@ -0,0 +1,47 @@ +import { Document, MetadataMode } from "../Node"; +import { + metadataDictToNode, + nodeToMetadata, +} from "../storage/vectorStore/utils"; + +describe("Testing VectorStore utils", () => { + let node: Document; + + beforeEach(() => { + node = new Document({ + text: "text", + metadata: { meta1: "Some metadata" }, + }); + }); + + test("nodeToMetadata should not modify a node's metadata", () => { + nodeToMetadata(node, true); + expect(node.metadata).toEqual({ meta1: "Some metadata" }); + }); + test("metadataDictToNode should reconstructs node and remove text (except embedding)", () => { + const metadata = nodeToMetadata(node, true); + const newNode = metadataDictToNode(metadata); + expect(newNode.metadata).toEqual({ meta1: "Some metadata" }); + expect(() => newNode.getEmbedding()).toThrow(); + expect(newNode.getContent(MetadataMode.NONE)).toEqual(""); + }); + test("metadataDictToNode should reconstructs node (except embedding)", () => { + const metadata = nodeToMetadata(node, false); + const newNode = metadataDictToNode(metadata); + expect(newNode.metadata).toEqual({ meta1: "Some metadata" }); + expect(newNode.getContent(MetadataMode.NONE)).toEqual("text"); + expect(() => newNode.getEmbedding()).toThrow(); + }); + test("metadataDictToNode should not allow deep metadata if flatMetadata is true", () => { + node.metadata = { meta: { meta: "meta" } }; + expect(() => nodeToMetadata(node, false, "text", true)).toThrow(); + }); + test("metadataDictToNode should throw an error when node content not found in metadata", () => { + const faultyMetadata = { + _node_type: "IndexNode", + }; + expect(() => { + metadataDictToNode(faultyMetadata); + }).toThrow(); + }); +});