猫鼬模式:使数组中的对象具有文档的唯一字段

Mongoose Schema: making an object within an array have a unique field for the document

不是 试图制作一个独特的数组元素,而是确保如果我有一个 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"
}

但是在本文档中插入另一个具有相同 codepair 应该是不行的,无论 site.

{
    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)
    }

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

由于我为 POST 路线使用了 async 函数,我进行了一些搜索,发现 forEachasync 函数。 我改用旧式 for (let i = 0; 循环,现在可以正常工作了!

这是我的代码。

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

  2. 如果 pairs 包含具有请求代码的对象,return 一条错误消息。

  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)
    }
})