【问题标题】:Mongoose Schema: making an object within an array have a unique field for the documentMongoose Schema:使数组中的对象具有文档的唯一字段
【发布时间】:2020-03-23 12:27:16
【问题描述】:

我不是试图创建一个唯一的数组元素,而是确保如果我有一个 pairs 数组(它们是对象),code 的值都不同。

这是我的架构定义:

const redirectSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true,
    },
    name: {
        type: String,
        required: true,
        unique: true
    },
    default_link: {
        type: String,
        required: true,
    },
    pairs: [
        {
            code: { // I want this field specifically to be unique within the document only
                type: String,
                required: true
            },
            site: {
                type: String,
                required: true
            }

        }
    ],
    date: {
        type: Date,
        required: true,
        default: Date.now
    }
})

例如,像这样插入pair 就可以了。

{
    code: "US",
    site: "google.com"
}

但是不管site如何,在此文档中插入另一个具有相同code的pair是不行的。

{
    code: "US", // Should not work, since the code "US" already exists within the array of pairs
    site: "amazon.com"
}

我正在尝试使用Redirect.findOne() 和Redirect.updateOne() 执行此操作,但是我遇到了编译器错误,或者只有在已经存在重复项后才会执行检查。

这是我目前的路线(允许重复 codes):

// Add pair to given redirect
router.post('/:name/insert_pair', verifyToken, async (request, response) => {
    const token = request.header("auth-token")
    const usernameFromToken = jwt.decode(token).username

    const { code, site } = request.body

    // Remember: you may edit only your own content
    try {
        const edited = await Redirect.findOneAndUpdate(
            {
                username: usernameFromToken,
                name: request.params.name
            },
            {
                $push: {
                    pairs: { code, site }
                }
            }
        )

        const specific = await Redirect.findOne(
            {
                username: usernameFromToken,
                name: request.params.name
            })

        response.send(specific)
    } catch (error) {
        response.status(400).json(error)
    }

【问题讨论】:

    标签: mongodb express mongoose mongodb-query mongoose-schema


    【解决方案1】:

    我的代码中的问题在于我使用 forEach 查找遍历文档数组中的 pair 对象。

    由于我在POST 路由中使用了async 函数,因此我进行了一些搜索,发现forEach 在async 函数中不能很好地发挥作用。 我切换了使用 oldschool for (let i = 0; 循环,现在可以正常工作了!

    这是我的代码。

    1. 获取文档并检查pairs数组

    2. 如果pairs 包含具有请求代码的对象,则返回错误消息。

    3. 如果没有,请将pair 对象添加到文档中。

    // Add pair to given redirect
    router.post('/:name/insert_pair', verifyToken, async (request, response) => {
        const token = request.header("auth-token")
        const usernameFromToken = jwt.decode(token).username
    
        const { code, site } = request.body
    
        // Remember: you may edit only your own content
        try {
            // Get the object as plain JSON and check if it contains the code
            const check = await Redirect.findOne(
                {
                    username: usernameFromToken,
                    name: request.params.name
                }
            ).lean()
    
            // SOLUTION: DON'T USE FOREACH WITHIN ASYNC!
            for (let i = 0; i < check.pairs.length; i++) {
                if (check.pairs[i].code === code) {
                    return response.status(400).json({
                        message: `${request.params.name} already contains an entry for ${code}`
                    })
                }
            }
    
            // TODO BUG: ONLY WORKS AFTER 1 DUPLICATE HAS ALREADY BEEN MADE.
            // await check.pairs.forEach((pair) => {
            //     if (pair.code === code) {
            //         return response.status(400).json({
            //             message: `${request.params.name} already contains an entry for ${code}`
            //         })
            //     }
            // })
    
            // Make the edit
            await Redirect.findOneAndUpdate(
                {
                    username: usernameFromToken,
                    name: request.params.name
                },
                {
                    $push: {
                        pairs: { code, site }
                    }
                }
            )
    
            // Get it to display it on screen
            const display = await Redirect.findOne(
                {
                    username: usernameFromToken,
                    name: request.params.name
                })
    
            response.json(display)
        } catch (error) {
            response.status(400).json(error)
        }
    })
    

    【讨论】:

      猜你喜欢
      • 2012-12-26
      • 2012-08-27
      • 2016-09-26
      • 1970-01-01
      • 2016-12-09
      • 2013-04-16
      • 1970-01-01
      • 2019-07-19
      相关资源
      最近更新 更多