Seeding a MongoDB Collection Idempotently
MongoDB by default does not have a way to deduplicate entries. Two entries with identical fields can be created and MongoDB will create a unique ObjectId for them
To solve this, you can use MongoDB’s concept of upsert
– essentially “update it, or create it if it doesn’t exist”
Here’s an example using mongoose. I’ve defined a schema for a collection called rbacGroups
that looks like this:
1const rbacGroupsSchema = new Schema({
2 rbacGroupName: {
3 type: String,
4 required: true,
5 },
6 members: {
7 type: Schema.Types.ObjectId,
8 ref: 'users'
9 },
10 createdAt: {
11 type: Date,
12 required: true,
13 immutable: true,
14 default: () => Date.now()
15 },
16}, { strict: "throw" })
Then, in a script for seeding the database with example/test data, use the following:
1const seedRbacGroups = () => {
2 for (i = 0; i < groupNames.length; i++){
3 let rbacGroupName = groupNames[i]
4 rbacGroupsModel.updateOne(
5 { rbacGroupName: rbacGroupName }, // Query to search by
6 {
7 $set: {
8 rbacGroupName: rbacGroupName
9 }
10 }, // Updates to make to the document
11 { upsert: true} // Tell MongoDB to create the document if it doesn't exist
12 )
13 .then(msg => msg.upsertedId ? console.log(`New RBAC Group with name ${rbacGroupName} created. ${msg.upsertedId}`) : console.log(`Group with name ${rbacGroupName} already exists. Skipping...`))
14 .catch(err => console.error(err.message))
15 }
16}
I’m using the updateOne
method from Mongoose, which is taking three parameters:
- The query to search by. In this case, we’re searching by the rbacGroupName
- The updates to make. This will either update the document matching the query, or create a document with these properties if nothing matches the query
- Options. In this case, we’re passing
{ upsert: true }