MongoDb: What's the best approach to modify a model field from an array of strings to an array of ids that would refer to another model?
Step 1:
- Create a Model for interests,
- specify your desired fields fir interests schema and set properties for particular fields
- specify collection name in options as per your requirement
- create a model and specify your desired name in model
const InterestsSchema = new mongoose.Schema( { name: { type: String } }, { collection: "interests" });const Interests = mongoose.model("Interests", InterestsSchema);
- Instead of removing
interests
field add new fieldinterest
(you can choose desired field), for safe side whenever you feel the current update working properly you can remove it, Update profile schema,
- update
interest
field as per your requirement, now newly added field isinterest
interests: { type: [String] }, interest: [{ type: Schema.Types.ObjectId, ref: "interests" }],
Step 2:
Wherever interests
in the app are used in the backend, use interest
and populate to fill them.
Step 3: (just execute the query)
Make a collection for interests and store all unique interests string from profile collection, so write a aggregation query to select unique string and store in interests collection, you can execute this query in mongo shell or any editor that you are using after specifying your original profile collection name,
$project
to show interests field only because we are going to deconstruct it$unwind
to deconstructinterests
array$group
by interests and select unique field, and trim white space frominterests
string$project
to showname
field and if you want to then add your desired fields- $out will create a new collection
interests
and write allinterests
with newly generated_id
field
db.getCollection('profile').aggregate([ { $project: { interests: 1 } }, { $unwind: "$interests" }, { $group: { _id: { $trim: { input: "$interests" } } } }, { $project: { _id: 0, name: "$_id" } }, { $out: "interests" }])
You have example input:
After executing above query the output/result in interests
/ new collection would be something like:
Step 4: (just execute the query)
Add new field interest with reference _id
s from interests
collection in profile collection, there are sequence to execute queries,
- find profile query and project only required fields
_id
andinterests
wheninterest
(new field) field is not exists and iterate loop usingforEach
- trim
interests
string iterating loop through map - find the
interests
reference_id
by itsname
field from createdinterests
collection - update query for add
interest
field that have_id
s in profile collection
db.getCollection('profile').find( { interest: { $exists: false } }, { _id: 1, interests: 1 }).forEach(function(profileDoc) { // TRIM INTEREST STRING var interests = profileDoc.interests.map(function(i){ return i.trim(); }); // FIND INTERESTS IDs var interest = []; db.getCollection('interests').find( { name: { $in: interests } }, { _id: 1 }).forEach(function(interestDoc){ interest.push(interestDoc._id); }); // UPDATE IDS IN PROFILE DOC db.getCollection('profile').updateOne( { _id: profileDoc._id }, { $set: { interest: interest } } );});
You have example input:
After executing above query the result in your profile collection would be something like:
Step 5:
Now you have completed all the steps and you have newly added interest
fieldand also old field interests
field is still in safe mode, just make sure everything is working properly you can delete old interests
field,
- remove old field
interests
field from all profiles
db.getCollection('profile').updateMany( { interests: { $exists: true } }, { $unset: { "interests": 1 } });
Warning:
- Test this steps in your local/development server before executing in production server.
- Take backup of your database collections before executing queries.
- Field and schema names are predicted you can replace with your original name.