rebase continue

This commit is contained in:
Pavel Duchovny 2025-01-29 16:19:52 +02:00
parent 323e87825e
commit 3028a51dc7
3 changed files with 255 additions and 0 deletions

View file

@ -0,0 +1,181 @@
import { MongoDBAtlasVectorSearch } from '@langchain/mongodb';
import { MongoClient } from 'mongodb';
import { NodeOperationError, type INodeProperties } from 'n8n-workflow';
import { metadataFilterField } from '@utils/sharedFields';
import {
mongoCollectionRLC,
embeddingField,
metadataField,
vectorIndexName,
} from '../shared/descriptions';
import { mongoCollectionSearch } from '../shared/methods/listSearch';
import { createVectorStoreNode } from '../shared/createVectorStoreNode';
const sharedFields: INodeProperties[] = [
mongoCollectionRLC,
embeddingField,
metadataField,
vectorIndexName,
];
const mongoNamespaceField: INodeProperties = {
displayName: 'Namespace',
name: 'namespace',
type: 'string',
description: 'Logical partition for documents. Uses metadata.namespace field for filtering.',
default: '',
};
const retrieveFields: INodeProperties[] = [
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [mongoNamespaceField, metadataFilterField],
},
];
const insertFields: INodeProperties[] = [
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Option',
default: {},
options: [
{
displayName: 'Clear Namespace',
name: 'clearNamespace',
type: 'boolean',
default: false,
description: 'Whether to clear documents in the namespace before inserting new data',
},
mongoNamespaceField,
],
},
];
export class VectorStoreMongoDBAtlas extends createVectorStoreNode({
meta: {
displayName: 'MongoDB Atlas Vector Store',
name: 'vectorStoreMongoDBAtlas',
description: 'Work with your data in MongoDB Atlas Vector Store',
icon: { light: 'file:mongodb.svg', dark: 'file:mongodb.dark.svg' },
docsUrl:
'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoremongodbatlas/',
credentials: [
{
name: 'mongoDb',
required: true,
},
],
operationModes: ['load', 'insert', 'retrieve', 'update', 'retrieve-as-tool'],
},
methods: { listSearch: { mongoCollectionSearch } },
retrieveFields,
loadFields: retrieveFields,
insertFields,
sharedFields,
async getVectorStoreClient(context, filter, embeddings, itemIndex) {
try {
const collectionName = context.getNodeParameter('mongoCollection', itemIndex, '', {
extractValue: true,
}) as string;
const vectorIndexName = context.getNodeParameter('vectorIndexName', itemIndex, '', {
extractValue: true,
}) as string;
const embeddingField = context.getNodeParameter('embedding', itemIndex, '', {
extractValue: true,
}) as string;
const metadataField = context.getNodeParameter('metadata_field', itemIndex, '', {
extractValue: true,
}) as string;
const credentials = await context.getCredentials('mongoDb');
const client = new MongoClient(credentials.connectionString as string, {
appName: 'devrel.content.n8n_vector_integ', // Added appName
});
await client.connect();
const collection = client.db(credentials.database as string).collection(collectionName);
// test index exists
const indexes = await collection.listSearchIndexes().toArray();
const indexExists = indexes.some((index) => index.name === vectorIndexName);
if (!indexExists) {
throw new NodeOperationError(context.getNode(), `Index ${vectorIndexName} not found`, {
itemIndex,
description: 'Please check that the index exists in your collection',
});
}
return new MongoDBAtlasVectorSearch(embeddings, {
collection,
indexName: vectorIndexName, // Default index name
textKey: metadataField, // Field containing raw text
embeddingKey: embeddingField, // Field containing embeddings
});
} catch (error) {
throw new NodeOperationError(context.getNode(), `Error: ${error.message}`, {
itemIndex,
description: 'Please check your MongoDB Atlas connection details',
});
}
},
async populateVectorStore(context, embeddings, documents, itemIndex) {
try {
const collectionName = context.getNodeParameter('mongoCollection', itemIndex, '', {
extractValue: true,
}) as string;
const embeddingField = context.getNodeParameter('embedding', itemIndex, '', {
extractValue: true,
}) as string;
const metadataField = context.getNodeParameter('metadata_field', itemIndex, '', {
extractValue: true,
}) as string;
const vectorIndexName = context.getNodeParameter('vectorIndexName', itemIndex, '', {
extractValue: true,
}) as string;
const credentials = await context.getCredentials('mongoDb');
const client = new MongoClient(credentials.connectionString as string, {
appName: 'devrel.content.n8n_vector_integ', // Added appName
});
await client.connect();
const db = client.db(credentials.database as string);
const collection = db.collection(collectionName);
// Check if collection exists
const collections = await db.listCollections({ name: collectionName }).toArray();
if (collections.length === 0) {
db.createCollection(collectionName);
}
await MongoDBAtlasVectorSearch.fromDocuments(documents, embeddings, {
collection,
indexName: vectorIndexName, // Default index name
textKey: metadataField, // Field containing raw text
embeddingKey: embeddingField, // Field containing embeddings
});
await client.close();
} catch (error) {
throw new NodeOperationError(context.getNode(), `Error: ${error.message}`, {
itemIndex,
description: 'Please check your MongoDB Atlas connection details',
});
}
},
}) {}

View file

@ -68,3 +68,54 @@ export const qdrantCollectionRLC: INodeProperties = {
},
],
};
export const mongoCollectionRLC: INodeProperties = {
displayName: 'MongoDB Collection',
name: 'mongoCollection',
type: 'resourceLocator',
default: { mode: 'list', value: '' },
required: true,
modes: [
{
displayName: 'From List',
name: 'list',
type: 'list',
typeOptions: {
searchListMethod: 'mongoCollectionSearch', // Method to fetch collections
},
},
{
displayName: 'Name',
name: 'name',
type: 'string',
placeholder: 'e.g. my_collection',
},
],
};
export const vectorIndexName: INodeProperties = {
displayName: 'Vector Index Name',
name: 'vectorIndexName',
type: 'string',
default: 'vector_index',
description: 'The name of the vector index',
required: true,
};
export const embeddingField: INodeProperties = {
displayName: 'Embedding',
name: 'embedding',
type: 'string',
default: 'embedding',
description: 'The field with the embedding array',
required: true,
};
export const metadataField: INodeProperties = {
displayName: 'Metadata Field',
name: 'metadata_field',
type: 'string',
default: 'text',
description: 'The text field of the raw data',
required: true,
};

View file

@ -1,6 +1,7 @@
import { Pinecone } from '@pinecone-database/pinecone';
import { QdrantClient } from '@qdrant/js-client-rest';
import { ApplicationError, type IDataObject, type ILoadOptionsFunctions } from 'n8n-workflow';
import { MongoClient } from 'mongodb';
export async function pineconeIndexSearch(this: ILoadOptionsFunctions) {
const credentials = await this.getCredentials('pineconeApi');
@ -67,3 +68,25 @@ export async function qdrantCollectionsSearch(this: ILoadOptionsFunctions) {
return { results };
}
export async function mongoCollectionSearch(this: ILoadOptionsFunctions) {
const credentials = await this.getCredentials('mongoDb');
const client = new MongoClient(credentials.connectionString as string, {
appName: 'devrel.content.n8n_vector_integ',
});
await client.connect();
try {
const db = client.db(credentials.database as string);
const collections = await db.listCollections().toArray();
const results = collections.map((collection) => ({
name: collection.name,
value: collection.name,
}));
return { results };
} finally {
await client.close();
}
}