n8n-nodes-rckflr-cosine-similarity
Version:
A custom n8n node to calculate cosine similarity between two arrays of vectors.
95 lines • 4.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CosineSimilarity = void 0;
const n8n_workflow_1 = require("n8n-workflow");
function cosineSimilarity(vecA, vecB) {
const dotProduct = vecA.reduce((sum, val, i) => sum + val * vecB[i], 0);
const magnitudeA = Math.sqrt(vecA.reduce((sum, val) => sum + val * val, 0));
const magnitudeB = Math.sqrt(vecB.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (magnitudeA * magnitudeB);
}
class CosineSimilarity {
constructor() {
this.description = {
displayName: 'Cosine Similarity',
name: 'cosineSimilarity',
group: ['transform'],
version: 1,
description: 'Calculates cosine similarity between two arrays of vectors',
defaults: {
name: 'Cosine Similarity',
},
inputs: ['main'],
outputs: ['main'],
properties: [
{
displayName: 'Array of Vectors A',
name: 'vectorsA',
type: 'json',
default: '',
description: 'Enter the first array of vectors (e.g., [[1,2,3],[4,5,6]])',
},
{
displayName: 'Array of Vectors B',
name: 'vectorsB',
type: 'json',
default: '',
description: 'Enter the second array of vectors (e.g., [[7,8,9],[10,11,12]])',
},
{
displayName: 'Similarity Threshold',
name: 'threshold',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 1,
},
default: 0.5,
description: 'Minimum cosine similarity to include in the output (0 to 1)',
},
],
};
}
async execute() {
let vectorsA = this.getNodeParameter('vectorsA', 0);
let vectorsB = this.getNodeParameter('vectorsB', 0);
const threshold = this.getNodeParameter('threshold', 0);
try {
if (typeof vectorsA === 'string')
vectorsA = JSON.parse(vectorsA);
if (typeof vectorsB === 'string')
vectorsB = JSON.parse(vectorsB);
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON format for vector arrays.');
}
if (!Array.isArray(vectorsA) || vectorsA.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Array A must be a valid array of vectors.');
}
if (!Array.isArray(vectorsB) || vectorsB.length === 0) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Array B must be a valid array of vectors.');
}
if (!vectorsA.every(Array.isArray) || !vectorsB.every(Array.isArray)) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Each element of the arrays must be a vector (another array).');
}
const dimensionA = vectorsA[0].length;
const dimensionB = vectorsB[0].length;
if (!vectorsA.every(vec => vec.length === dimensionA) || !vectorsB.every(vec => vec.length === dimensionB)) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'All vectors must have the same dimension.');
}
const result = [];
for (const vecA of vectorsA) {
for (const vecB of vectorsB) {
if (vecA.length !== vecB.length)
continue;
const similarity = cosineSimilarity(vecA, vecB);
if (similarity >= threshold) {
result.push({ vectorA: vecA, vectorB: vecB, similarity });
}
}
}
return [[{ json: { matches: result } }]];
}
}
exports.CosineSimilarity = CosineSimilarity;
//# sourceMappingURL=CosineSimilarity.node.js.map