Mongoose: Update by pushing an object in an array - arrays

This is the model I am working with:
name: {
type: String
},
payment: {
id: {
type: String,
required: true
},
cards: [
{
id: {
type: String
},
is_default: {
type: Boolean,
"default": false
}
}
]
}
I want to add a card to the cards array, for example:
card =
id: "some_token"
is_default: true
I am using the update method to push the card to the array, but it won't add the card to the document. Instead, it creates a new document with only those fields:
{
id: "some_token",
is_default: true,
_id: someId
}
Any idea how I can update the actual document I am targeting instead of creating a new document?
Here's my code (using CoffeeScript):
update_where =
payment:
id: "some_id"
update_push =
$push:
'payment.cards':
id: card_token
is_default: false
Customer.update update_where, update_push, {upsert: true}, (err, results) ->
# Do something with the results

Oh… I just noticed my mistake. The problem was in the where statement.
I was doing:
payment:
id: "some_id"
But the right thing to write is the following:
'payment.id': 'some_id'
And it now works!

Related

Why is my mongoose populate query throwing "Cannot populate path because it is not in your schema" error?

I'm building a form management program but right now I'm just trying to build a queue system to handle all the forms when they're assigned to someone.
when I call this first function, it should populate the elements of the activeWork array by pulling from each collection that the entries reference, there are several collections that could be referenced in active work, so I'm trying to use the collection type field to determine what collection to pull from, I don't know if I formatted any of this correctly because its my first time building any of this.
import statesPersons from "./statesPersons.schema.js";
export async function getStatesPersonsActiveWorkByProfileId(req, res){
try{
const { profileId } = req.params
const data = await statesPersons.find({profileId})
.populate('statesPersons.activeWork.referenceId')
return res.send({
message: "success",
data: data,
status: 200 })
}catch(e) {
console.error(e.message)
return res.send({
message: "couldn't fetch active work",
data: null,
status: 500 })
}
}
Here is the schema for statesPersons, the collection where active work is stored.
import mongoose, {model, Schema} from "mongoose";
const activeWorkSchema = new Schema({
active: Boolean,
collectionType: {
type: String,
enum: ['messages'],
},
referenceId: {
type: Schema.Types.ObjectId,
refPath: "statesPersons.activeWork.collectionType"
},
sentBy: {
type: Schema.Types.String,
ref: "statesPerson",
},
sentTo: {
type: Schema.Types.String,
ref: "statesPerson",
},
timeRecived: Date,
dueDate: Date,
subject: String,
viewed: Boolean,
content: {},
})
const statesPersonsSchema = new Schema({
profileId:{
type: String,
required: true,
unique: true
},
department: {
type: String,
required: true,
index: true,
},
firstName: String,
lastName: String,
location: String,
org: String,
title: String,
jobDescription: String,
email: {
type: String,
lowercase: true,
},
phoneNumber: String,
activeWork: [activeWorkSchema],
emailList: [String],
jobAssignments: [String],
affiantInfo: {
affiantInfoTitle: String,
affiantInfoExperience: String,
},
assessments: [
{
assessdBy: {
type: Schema.Types.ObjectId,
ref: "statesPerson",
},
dueDate: Date,
questions: {},
},
],
});
export default mongoose.model("statesPersons", statesPersonsSchema);
When I make a query, I get:
Cannot populate path statesPersons.activeWork.referenceId because it is not in your schema. Set the strictPopulate option to false to override.
I don't know if I formatted my populate correctly or if the problem is in my schema,

How do I accept an array of strings using Apollo Server and GQL?

I currently have a typeDefs file that has a custom Books type. This is how it looks at the moment:
type: Books {
bookId: String
authors: [String]
description: String
title: String
}
I am using MongoDB in order to store my data. My model looks like this:
const bookSchema = new Schema({
authors: [
{
type: String,
},
],
description: {
type: String,
required: true,
},
// saved book id from GoogleBooks
bookId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
}
});
And my resolvers look like this:
saveBook: async (parent, args, context) => {
if (context.user) {
const book = await Book.create({ ...args })
await User.findByIdAndUpdate(
{ _id: context.user._id },
{ $addToSet: { savedBooks: { bookId: args.bookId } } },
{ new: true }
);
return book;
}
throw new AuthenticationError('You need to be logged in!');
},
When I use graphql playground and send data in the query variable I am getting an error that String cannot represent a non string value: [\"james\", \"jameson\"]", when I send
{
"input": {
"bookId": "1",
"authors": ["james", "jameson"],
"description": "thdfkdaslkfdklsaf",
"title": "fdjsalkfj;a",
}
}
I know that it is because I am using an array of strings and entering an array of strings to gql will result in this error. I thought that if I put brackets around the String in my typeDefs it would work just find. I can't seem to find a way to send an array of strings to gql. I looked through the documentation and can't find a way to complete this..
Make a typedef out of author and then give the author variable within books the type "Author".
I think you also have to define the array in the bookschema if I'm not incorrect.
And don't forget to make sure the model naming in your database has to be the same as in your code.
Like this:
type: Author {
name: String
}
type: Books {
bookId: String
authors: [Author]
description: String
title: String
}
const bookSchema = new Schema({
authors: [
{
type: Author,
},
],
description: {
type: String,
required: true,
},
// saved book id from GoogleBooks
bookId: {
type: String,
required: true,
},
title: {
type: String,
required: true,
}
});
Hope this works :)

Save current User into field within array in Mongoose

Here is a relevant part of my Schema, where I'll make reservations to a "space":
var spaceSchema = new mongoose.Schema({
spaceName: String,
scheduledDates: [{
scheduledDates: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
});
Author should be the current user that's logged in. Here is my route to update those fields:
router.put('/:space_id/schedule', function(req, res) {
Space.findByIdAndUpdate(req.params.space_id, {
'$push': { 'scheduledDates': req.body.space, 'author': req.user._id }
}, { "new": true, "upsert": true }, function(err, space) {
if (err) {
console.log(err);
} else {
console.log(req.body.space);
}
});
});
I can't access "author" correctly, because it's inside the array. What can I do to update this array, adding a new date and user to make the reservation?
Thank you
UPDATE
I tried to use "_id" instead of "id" in my property but got the same result. It seems like it's ignoring the "author" field, and only saving "scheduledDates"
So the schema was like this:
scheduledDates: [{
scheduledDates: String,
author: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
And then in my route, I changed what I was 'pushing':
'$push': { 'scheduledDates': req.body.space, 'author._id': req.user._id }
UPDATED 2
Changed the way I was getting the object to push:
'$push': {
'scheduledDates': {
'scheduledDates': req.body.space,
'author': { _id: req.user._id, username: req.user.username }
}
}
Now I'm getting the following error:
message: 'Cast to string failed for value "{ scheduledDates: \'04/11/2017\' }" at path "scheduledDates"',
name: 'CastError',
stringValue: '"{ scheduledDates: \'04/11/2017\' }"',
kind: 'string',
value: [Object],
path: 'scheduledDates',
reason: undefined } } }

MongoDB/Mongoose update array within array with $addToSet

Good day all,
i'm trying to save image galleries where each image can have up to 5 tags associated with them. So in the Document the Gallery field is an array that has multiple values of which the Tag attribute which itself is an array of Tags.
I'm having issue saving an image where the user adds 2 or more tags for that picture. below this is what i have in my mongoose user schema
gallery : [{
origin : {type: String, trim: true, default: null},
uploadOn: {type: Date, Default: null},
title: { type: String, trim: true, default: null},
caption: { type: String, trim: true, default: null },
tag: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Imagetag', unique: true, sparse: true }],
imageLink: { type: String, trim: true }
}]
and this is my update script
var query = {username:'testUser'};
var options = { runValidators: true };
var toUpdate = {
$addToSet : {
gallery : {
origin : req.body.orgin,
uploadOn: new Date(),
title: req.body.title,
caption: req.body.caption,
tag: '59a5fea0382f1305841f0d86' // working
$addToSet: { tag: { $each: ['59a5fea0382f1305841f0d86', '59a5fea0382f1305841f0d88'] } } , // not working
$push: { tag: { $each: ['59a5fea0382f1305841f0d86', '59a5fea0382f1305841f0d88'] } } , // not working
imageLink: req.body.path
}
}
};
// proceed to update
var user = await User.findOneAndUpdate(query, toUpdate, options);
i have listed the different combination that i have tried without success. i do not want to have to make multiple calls to update the tags one by one for that same image.

MongoDB $addToSet replace

I have a document like the following one:
{
_id: "00112233",
array: [
{
_id: "potatoes",
amount: 5
},
{
_id: "carrots",
amount: 6
}
]
}
and I need to update or push the documents so that if there is one in the array, it gets replaced, if not, it gets pushed.
If i try to do an update like this:
db.collection.update(
{ _id: "00112233" },
{
"$push": { "array": { "$each": [
{
_id: "potatoes",
amount: 6
},
{
_id: "apples",
amount: 2
}
]}}
}
)
In the database I will find two "potatoes" field. But if I try to replace the "push" with a "addToSet" I won't have the "potatoes" field updated.
Is there any way I can do this in only one query?
Unfortunately, you can't achieve the result that you are expecting in single update operation.
The addToSet doesn't work for this scenario because the array has embedded documents. addToSet checks for both the attribute names and values whether it matches. If match is found, it does nothing. If the match is not found, it adds the data to the array attribute.
In the above case for "potatoes", the amount value of potatoes doesn't match. So it inserts a new element into array. So the result has two potatoes with 2 different values.
{
_id: "potatoes",
amount: 6
}
You may need to perform two update operations.
1) Update the value of an array field if it exists using $set
db.collection.update(
{ _id: "00112233", "array._id": "potatoes"},
{
"$set": { "array.$":
{
_id: "potatoes",
amount: 7
}
},
}
);
2) Add the new element to an array field if it doesn't exists using $addToSet
db.collection.update(
{ _id: "00112233"},
{
"$addToSet": { "array":
{
_id: "apples",
amount: 7
}
},
}
);
you need to use $set instead of $push/$addToSet.
https://docs.mongodb.com/manual/reference/operator/update/set/
Had a similar issue and tried notion quest's approach and it did work.My schema was as follows
const MatchInstanceSchema = new Schema({
_id: false,
pseudoKey: { type: String, required: true, unique: true },
service: { type: String, required: true },
region: { type: String, required: true },
team1: {
name: { type: String, required: true },
price: { type: String, required: true }
},
team2: {
name: { type: String, required: true },
price: { type: String, required: true }
},
drawPrice: { type: String, required: true },
url: { type: String, required: true }
})
const MatchSchema = new Schema({
pseudoKey: { type: String, required: true, unique: true },
sport: { type: String, required: true },
league: { type: String, required: true },
date: { type: Date, required: true },
team1: { type: String, required: true },
team2: { type: String, required: true },
matchInstances: [MatchInstanceSchema]
})
I wanted to update the following matchInstances subdocument if there exists a match whose pseudoKey was same as the top level(parent document) pseudoKey without
duplicating the value and create a new entry in the matchInstances subdocument if it did not exist.
{
"_id" : ObjectId("5b47d47e7fd01d29178aa3a5"),
"pseudoKey" : "newcastle-tottenham",
"sport" : "Soccer",
"league" : "Premier League",
"date" : ISODate("2018-08-11T11:30:00.000Z"),
"team1" : "Newcastle",
"team2" : "Tottenham",
"matchInstances" : [
{
"team1" : {
"name" : "Newcastle",
"price" : "74.6"
},
"team2" : {
"name" : "Tottenham",
"price" : "2.06"
},
"pseudoKey" : "newcastle-tottenham",
"service" : "Betin",
"region" : "Kenya",
"drawPrice" : "3.45",
"url" : "https://web.betin.co.ke/Sport/SubEventOdds.aspx?SubEventID=16701401"
}
]
This was my solution
//If it already exists we just want to update with the new values without
//duplicating
Match.findOneAndUpdate(
{ pseudoKey: pseudoKey, "matchInstances.service": service },
{ $set: { "matchInstances.$": matchInstances[0] } }
//If it does not exist we want to add it as a new entry into the matchInstances
//subdocument
Match.findOneAndUpdate(
{ pseudoKey: pseudoKey },
{ $addToSet: { matchInstances: matchInstances } }
)

Resources