MongoDB/Mongoose update array within array with $addToSet - arrays

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.

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,

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

Mongoose: Update by pushing an object in an array

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!

MEAN Angular up-voting function throwing forbidden message

What im trying to do is create an app where users can post items and others can vote on the items similar to Reddit etc.
Problem i have is that when one user votes on an item posted by another user they get a forbidden message.
How i do i make it so that one user can modify the votes count of another users item.
This is the model for the Item
var ItemSchema = new Schema({
title: {
type: String,
default: 'NA',
required: 'Please fill Item title',
trim: true
},
link: {
type: String,
required: 'Please fill Item URL',
trim: true
},
details: {
type: String,
default: 'NA',
required: 'Please fill Item Details',
trim: true
},
votes: {
type: Number,
default: '0'
},
voters: [{
type: Schema.ObjectId,
ref: 'User'
}],
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
So one user should be able to increment the upvotes for another users Item.
Routes
// Items collection routes
app.route('/items').all(itemsPolicy.isAllowed)
.get(items.list)
.post(items.create);
app.route('/items/itemCount').all()
.get(items.countitems);
app.route('/items/itemCountToday').all()
.get(items.countitemsToday);
// Single item routes
app.route('/items/:itemId').all(itemsPolicy.isAllowed)
.get(items.read)
.put(items.update)
.delete(items.delete);
Upvote function
$scope.upVoteHome = function(item) {
item.votes++;
item.$update(function() {
//$location.path('items/' + item._id);
}, function(errorResponse) {
// rollback votes on fail also
$scope.error = errorResponse.data.message;
});
};
Button
<button ng-show="authentication.user" ng-click="upVoteHome(item)" type="button"
class="btn btn-danger vote-up-button"><i class="glyphicon glyphicon-arrow-up"></i> Hot</button>
add a 'put' permission in the routing policy
{
roles: ['user'],
allows: [{
resources: '/deals',
permissions: ['get', 'post', 'put']
}, {
resources: '/deals/:dealId',
permissions: ['get', 'put']
}]
}

Mongo Giving 'duplicate key error' on non-unique fields

I am getting a MongoDB error when trying to insert a subdocument. The subdocs already have unique _ids, but an error is being thrown for a different, non-unique field that I don't want unique.
The error in Angular is: "Assets.serial already exist". How can I make this field contain duplicate values, and what is causing the model to assume it should be unique?
Here is my Mongoose model:
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var AssetUrlSchema = new Schema({
name: {
type: String,
unique: false,
default: '',
trim: true
},
url: {
type: String,
unique: false,
default: 'http://placehold.it/75x75',
trim: true
},
}),
AssetSchema = new Schema({
serial: {
type: Number,
unique: false
},
urls: {
type: [AssetUrlSchema],
unique: false,
default: [
{ name: '', url: 'http://placehold.it/75x75' },
{ name: '', url: 'http://placehold.it/75x75' }
]
}
}),
/**
* Item Schema
*/
ItemSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please enter name',
trim: true
},
assets: {
type: [AssetSchema],
default: [],
unique: false
},
property: {
type: Schema.ObjectId,
zd: 'Please select a property',
ref: 'Property'
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Item', ItemSchema);
And here is my 'save' method:
function(){
var i = 0, assets = [];
for (;i < 24;i++) {
assets.push({
serial: 1000+i,
urls: {
name: 'Asset Name ' + i,
url: 'http://placehold.it/75x75?'
}
});
}
item = new Items ({
name: 'FPO',
property: newPropId,
assets: assets
});
return item.$save(
function(response){ return response; },
function(errorResponse) {
$scope.error = errorResponse.data.message;
}
);
}
The first time I insert a document, it works fine. Any subsequent time, it fails with a 400 because the assets.serial field is not unique. However, I am specifically marking that field as unique:false.
The error in the mode console is:
{ [MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index: mean-dev.items.$assets.serial_1 dup key: { : 1000 }]
name: 'MongoError',
code: 11000,
err: 'insertDocument :: caused by :: 11000 E11000 duplicate key error index: mean-dev.items.$assets.serial_1 dup key: { : 1000 }' }
POST /api/items 400 14.347 ms - 41
Mongoose doesn't remove existing indexes so you'll need to explicitly drop the index to get rid of it. In the shell:
> db.items.dropIndex('assets.serial_1')
This will happen if you initially define that field unique: true but then later remove that from the schema definition or change it to unique: false.
If you're using MongoAtlas, you can go to the collection -> click 'indexes' -> on the index you want to delete, click 'drop index'
If you are in a dev/prototype mode, simply deleting the actual collection (after changing the unique:true to false for instance), will reset everything and mongoose will allow your duplicates.

Resources