I am not trying to make a unique array element, but rather ensure that if I have an array of pairs
(which are objects) all have a different value for code
.
Here is my schema's definition:
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
}
})
So for example, inserting a pair
like so is OK.
{
code: "US",
site: "google.com"
}
But inserting another pair
with the same code
within this document should NOT be okay, regardless of the site
.
{
code: "US", // Should not work, since the code "US" already exists within the array of pairs
site: "amazon.com"
}
I'm trying to do this using Redirect.findOne()
and Redirect.updateOne()
but I've either been getting compiler errors or the check will only be performed after a duplicate is already present.
Here's my route so far (allows duplicate code
s):
// 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)
}
The issue in my code laid in the fact that I was using a forEach
look to traverse the pair
objects within my document's array.
Since I'm using an async
function for my POST
route, I did some searching and found that forEach
does not play well within an async
function. I switched to using an oldschool for (let i = 0;
loop and it now works fine!
Here's my code.
Get the document and check the pairs
array
If pairs
contains an object that has the requested code, return an error message.
If not, add the pair
object to the document.
// 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)
}
})