In my app, users create word lists. A user can change the order of words in each list, they can also change the order of each list.
When a user creates or edits a list, the word list is updated.
-
If word lists have already been created, the word list will be added to the existing word lists.
-
If a word list has been edited (added words / removed words / word order changed) the existing word list will be updated.
-
If word lists have not been created yet, then a new document should be added to the database.
I found a solution, but it runs 2 findOneAndUpdate
queries if a new word list is added. How can I achieve this with 1 query? Should I simplify my schema or change it so that it uses a nested object instead of an array?
My word Lists schema:
const wordListsSubSchema = new Schema({
title: {
type: String,
},
words: [
{
id: {
type: String,
},
word: {
type: String,
},
},
],
});
const WordListSchema = new Schema({
wordLists: [wordListsSubSchema],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: 'You must supply a user',
},
});
My 1 or 2 query solution:
exports.wordListAddorEdit = async (req, res) => {
const listUpdate = await WordList.findOneAndUpdate(
{
user: req.user.id,
'wordLists.title': req.body.wordList.title,
},
{
$set: { 'wordLists.$.words': req.body.wordList.words },
},
{
new: true,
runValidators: true,
useFindAndModify: false,
}
);
if (listUpdate === null) {
await WordList.findOneAndUpdate(
{
user: req.user.id,
},
{
$addToSet: { wordLists: [req.body.wordList] },
},
{
upsert: true,
new: true,
runValidators: true,
useFindAndModify: false,
}
);
}
res.json('success');
};
My attempted solution:
Does not work. Can’t use $set and $setOnInsert with the same field.
exports.wordListAddorEditNOTWORKING = async (req, res) => {
const list = await WordList.findOneAndUpdate(
{
user: req.user.id,
},
{
$set: {
wordLists: {
$cond: {
if: { $in: [req.body.wordList.title, '$wordLists.title'] },
then: {
$map: {
input: '$wordLists',
in: {
$mergeObjects: [
'$$this',
{
$cond: [
{
$eq: ['$$this.title', req.body.wordList.title],
},
{ words: req.body.wordList.words },
{},
],
},
],
},
},
},
else: {
$concatArrays: [
{ $ifNull: ['$wordLists', []] },
[req.body.wordList],
],
},
},
},
},
$setOnInsert: {
wordLists: [req.body.wordList],
},
},
{
upsert: true,
new: true,
runValidators: true,
useFindAndModify: false,
}
);
res.json('success');
};