UNPKG

mongodb-rag-core

Version:

Common elements used by MongoDB Chatbot Framework components.

203 lines (202 loc) 8.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fuzzyMatchArrays = exports.fuzzyMatchAggregation = exports.fuzzyMatchExecutionResults = exports.fuzzyMatchSyntheticKey = exports.ERRORS = void 0; const bson_1 = require("bson"); const assert_1 = require("assert"); exports.ERRORS = { EXPECTED_ARRAY: "Expected value must be an array", SINGLE_ITEM_ARRAY: "Expected value and output value must both be a single document arrays", }; exports.fuzzyMatchSyntheticKey = "__output__"; function fuzzyMatchExecutionResults({ mongoDbOutput, expected, orderMatters = false, isAggregation = false, allowedNumberDifference = 0.01, }) { const outputEjson = bson_1.EJSON.serialize(mongoDbOutput, { relaxed: true }); const expectedEjson = bson_1.EJSON.serialize(expected, { relaxed: true }); const outputIsArray = Array.isArray(outputEjson); const expectedIsArray = Array.isArray(expectedEjson); (0, assert_1.strict)(expectedIsArray, exports.ERRORS.EXPECTED_ARRAY); if (isAggregation) { return fuzzyMatchAggregation(expectedEjson, outputEjson, orderMatters, allowedNumberDifference); } if (outputIsArray && expectedIsArray) { return fuzzyMatchArrays(expectedEjson, outputEjson, orderMatters, allowedNumberDifference); } return null; } exports.fuzzyMatchExecutionResults = fuzzyMatchExecutionResults; function fuzzyMatchAggregation(truthArray, testInput, nestedArrayOrderMatters, allowedNumberDifference = 0.01) { const testIsArray = Array.isArray(testInput); let testArray; if (testIsArray) { // If testInput is an array of arrays, keep it as is to avoid flattening testArray = testInput; } else if (typeof testInput === "object") { testArray = [testInput]; } else { testArray = [ { [exports.fuzzyMatchSyntheticKey]: testInput, }, ]; } // Since order doesn't matter, we need to find a matching object for each object in truthArray // Create a copy of testArray to keep track of unmatched objects const testArrayCopy = [...testArray]; for (const truthObj of truthArray) { // Try to find a matching object in testArrayCopy const matchIndex = testArrayCopy.findIndex((testObj) => fuzzyMatchObjectsIgnoreKeys(truthObj, testObj, nestedArrayOrderMatters, allowedNumberDifference)); if (matchIndex === -1) { // No matching object found return false; } else { // Remove the matched object to prevent duplicate matching testArrayCopy.splice(matchIndex, 1); } } // All objects matched return true; } exports.fuzzyMatchAggregation = fuzzyMatchAggregation; /** Performs a fuzzy match between two arrays of objects. @param truthArray - The array of objects to match against @param testArray - The array of objects to test */ function fuzzyMatchArrays(truthArray, testArray, orderMatters, allowedNumberDifference) { // If array lengths are different, they can't match if (truthArray.length !== testArray.length) return false; // If order matters, compare each object in order if (orderMatters) { for (let i = 0; i < truthArray.length; i++) { if (!fuzzyMatchObjects(truthArray[i], testArray[i], orderMatters, allowedNumberDifference)) { return false; } } return true; } // Create a copy of array2 to keep track of matched objects const testArrayCopy = [...testArray]; // Iterate through each object in array1 for (const truthObj of truthArray) { // Find the index of a matching object in testArrayCopy const matchIndex = testArrayCopy.findIndex((testObj) => fuzzyMatchObjects(truthObj, testObj, orderMatters, allowedNumberDifference)); if (matchIndex === -1) { // No matching object found return false; } else { // Remove the matched object to prevent duplicate matching testArrayCopy.splice(matchIndex, 1); } } // All objects matched return true; } exports.fuzzyMatchArrays = fuzzyMatchArrays; /** Fuzzy comparison function for two objects. Allow small differences in numeric values. Only validates keys present in the truth object. */ function fuzzyMatchObjects(truthObject, testObject, nestedArrayOrderMatters, allowedNumberDifference = 0.01) { const truthKeys = Object.keys(truthObject); for (const key of truthKeys) { if (!Object.prototype.hasOwnProperty.call(testObject, key)) { return false; } const truthVal = truthObject[key]; const testVal = testObject[key]; // Fuzzy conditions: // - If values are numbers, allow a small difference if (typeof truthVal === "number" && typeof testVal === "number") { const difference = Math.abs(truthVal - testVal); const allowedDifference = allowedNumberDifference; if (difference > allowedDifference) return false; } // If values are arrays, recursively call fuzzyMatchArrays else if (Array.isArray(truthVal) && Array.isArray(testVal)) { if (!fuzzyMatchArrays(truthVal, testVal, nestedArrayOrderMatters)) return false; } // Handle null values explicitly to allow for // typeof object test below, b/c null is typeof object in JS :( else if (truthVal === null) { if (testVal !== null) return false; } // Nested objects else if (typeof truthVal === "object" && typeof testVal === "object") { if (!fuzzyMatchObjects(truthVal, testVal, nestedArrayOrderMatters)) return false; } else { // For other types, use strict equality if (truthVal !== testVal) return false; } } return true; } /** Compares two objects by their values, ignoring keys. */ function fuzzyMatchObjectsIgnoreKeys(truthObject, testObject, nestedArrayOrderMatters, allowedNumberDifference = 0.01) { const truthValues = Object.values(truthObject); const testValues = Object.values(testObject); // If more truth values than test values, the objects can't match if (truthValues.length > testValues.length) return false; // Compare values regardless of order const testValuesCopy = [...testValues]; for (const truthValue of truthValues) { const matchIndex = testValuesCopy.findIndex((testValue) => fuzzyMatchValues(truthValue, testValue, nestedArrayOrderMatters, allowedNumberDifference)); if (matchIndex === -1) { // No matching value found return false; } else { // Remove the matched value to prevent duplicate matching testValuesCopy.splice(matchIndex, 1); } } // All values matched return true; } /** Fuzzy comparison function for values. */ function fuzzyMatchValues(truthVal, testVal, nestedArrayOrderMatters, allowedNumberDifference = 0.01) { // Fuzzy conditions: // - If values are numbers, allow a small difference if (typeof truthVal === "number" && typeof testVal === "number") { const difference = Math.abs(truthVal - testVal); if (difference > allowedNumberDifference) return false; } // If values are arrays, recursively call fuzzyMatchArrays else if (Array.isArray(truthVal) && Array.isArray(testVal)) { if (fuzzyMatchArrays(truthVal, testVal, nestedArrayOrderMatters, allowedNumberDifference) === false) return false; } // Handle null values explicitly else if (truthVal === null) { if (testVal !== null) return false; } // Nested objects else if (typeof truthVal === "object" && typeof testVal === "object") { if (fuzzyMatchObjects(truthVal, testVal, nestedArrayOrderMatters, allowedNumberDifference) === false) return false; } else { // For other types, use strict equality if (truthVal !== testVal) return false; } return true; } //# sourceMappingURL=fuzzyMatchExecutionResults.js.map