I have a problem where I am creating a user and want to save some items associated to this user at the same time.
I have managed to get it where I can create an item and reference the user_id associated to that item but I cant work out how to push all the items the user has into the User schema.
I have tried looping through req.body.taken and adding to user schema but I just get null returned.
router.post('/data', async (req, res) => {
var user = new User({
name: req.body.name,
website: req.body.website,
})
user.save(function (err) {
if (err) {
return next(err);
}
})
req.body.taken.forEach((item) => {
var item = new Item({
x: item.x,
y: item.y,
xSize: item.xSize,
ySize: item.xSize,
user: user,
imageSource: item.imageSource,
user: user
}
)
item.save(function (err) {
if (err) {
return next(err);
}
})
})
const UserSchema = new Schema(
{
id: Number,
name: String,
website: String,
items: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Item'
}]
},
{ timestamps: true }
);
const ItemSchema = new Schema(
{
id: Number,
x: Number,
y: Number,
xSize: String,
ySize: String,
imageSource: String,
user: { type: mongoose.Schema.ObjectId, ref: 'User' }
},
{ timestamps: true }
);
User.find({})
.populate('items')
.exec(function(error, items) {
console.log(items)
})
When I call User find I want to get all items associated to that user (which will be an array as req.body.taken is an array of items.
There's a few issues here.
you are not waiting for user.save, theres a good chance all your items are being saved before the user doc is even returned. you should either await for promise on user.save or put the items.forEach loop in the callback. also you are saving the entire objet and not the user._id returned which mongoose wont allow as it does not match the scheme. making the creation fail.
You are not saving the items array in your user, how can you expect to populate that field if its empty. the flow of your code needs to change a little in order to allow you to update this field.
Related
Error message
TypeError: doc.data is not a function
Source code
state() {
myWallet: ''
},
getters: {
getMyWallet: state => state.myWallet.wallet,
},
mutations: {
getMyWallet(state, doc) {
state.myWallet = doc.data();
}
},
actions: {
async getMyWallet({ commit }) {
firebase.auth().onAuthStateChanged(user => {
const uid = user.email;
const db = firebase.firestore();
const doc = db
.collection('myData')
.where('uid', '==', uid)
.get()
commit('getMyWallet', doc);
});
},
}
For the uid of .where ('uid','==', uid), the email address at the time of new registration is stored in the firestore as a value.
.collection('myData')
.doc('Specific document ID')
.get()
commit('getMyWallet', doc);
If you specify a specific document ID without using where, you can get the corresponding document, but if you use the where clause, you will get an error message.
The cause is unknown.
Postscript
↓SignUp.vue
methods: {
async signUp() {
await this.$store.dispatch('signUp', { username:this.username, email:this.email, password:this.password });
const db = firebase.firestore();
const user = firebase.auth().currentUser;
db.collection('myData').doc(user.uid).set({
uid: user.uid,
userName: user.displayName,
email: user.email,
myWallet: 300
});
this.$store.dispatch('getMyWallet', user.uid);
this.$router.push('/home');
}
}
↓store.js
state() {
myWallet: '',
},
getters: {
getMyWallet: state => state.myWallet.myWallet,
},
mutations: {
getMyWallet(state, doc) {
state.myWallet = doc.data();
console.log(doc.data())
}
},
actions: {
async getMyWallet({ commit }, uid) {
const db = firebase.firestore();
const doc = await db
.collection('myData')
.doc(uid)
.get();
commit('getMyWallet', doc);
}
}
When I changed the code as described above, my balance was displayed when I newly registered and moved to the home screen, but there was only one problem.
For example, if you log out once and set'myWallet: 300'to 1000 yen and register again, the previous 300 yen will be displayed.
And when I checked with the firestore and the console, both were registered for 300 yen.
I don't know why it behaves like this.
The error message TypeError: doc.data is not a function means that your object 'doc' is not of that type you think it should be. In the case when you use firebase it is just another object ... maybe a wrapper or can it be that 'where' returns a list of objects? Take a look at the documentaion of 'where' and what type of object it will return.
Nevertheless ...
.collection('myData')
.where('uid', '==', uid)
.get()
commit('getMyWallet', doc);
//-- This code returns another object than 'doc' where the 'data' function does not exist.
.collection('myData')
.doc('Specific document ID')
.get()
commit('getMyWallet', doc);
//-- This code returns the intended object where the 'data' function exists.
I have a model which represents a menu for a restaurant, within that model I have an array for the items in the menu. I am not sure how to add menu items to my menu model using express, I bolded the line where I am not sure what to write.
This is the menu model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const FoodItemSchema = new Schema({
name: { type: String, required: true },
price: { type: Number, required: true },
Category: { type: String, required: false },
Quantity: { type: String, required: false },
});
const MenuSchema = new Schema({
restaurant_id: { type: mongoose.Schema.Types.ObjectId, ref: "restaurant" },
items: [FoodItemSchema],
dateCreated: { type: String, required: false },
});
module.exports = mongoose.model("menu", MenuSchema);
And this is my express function which I am using to add menus to my database, I am unsure what to add for the array of menu items within the function.
exports.sendMenuData = (req, res) => {
const menu = new Menu({
restaurant_id: req.body.restaurant_id,
**items: [FoodItemSchema]**, //Not sure what to write here in terms of req
dateCreated:req.dateCreated,
});
menu
.save()
.then((data) => {
console.log(data);
res.send(data);
})
.catch((err) => {
console.log(err);
});
};
You need to pass through postman or whatever app you are using to acces your db the array of food items.
Example:
items: [req.body.theNameYouWant],
In postman:
theNameYouWant: {name:"Hot dog",price: 3,...(You must place here all the atributes of FoodItemSchema)}
Otherwise, you can also use operators such $addToSet $push which will allow you to introduce FoodItemSchema in the array.
I have a question for validating a PUT request. The body of the request is an array of objects. I want the request to succeed if the body contains an array of at least length one, but I also need to do a separate validation on each object in the array and pass that back in the response. So my put body would be:
[1, 2, {id: "thirdObject"}]
The response should be 200 even though the first two items are not even objects. The request just needs to succeed if an array of length 1 is passed in the body. The response needs to be something like:
[{id: firstObject, status: 400, error: should be object}, {id: secondObject, status: 400, error: should be object}, { id: thirdObject, status: 204 }]
Currently I am validating the body as such with fluent schema:
body: S.array().items(myObjectSchema)
.minItems(1)
Which will result in a 400 if any of the items in the body don’t match the myObjectSchema. Was wondering if you have any idea how to achieve this?
The validation doesn't tell you if a schema is successful (eg { id: thirdObject, status: 204 }), so you need to manage it by yourself.
To do that, you need to create an error handler to read the validation error and merge with the request body:
const fastify = require('fastify')()
const S = require('fluent-schema')
fastify.put('/', {
handler: () => { /** this will never executed if the schema validation fail */ },
schema: {
body: S.array().items(S.object()).minItems(1)
}
})
const errorHandler = (error, request, reply) => {
const { validation, validationContext } = error
// check if we have a validation error
if (validation) {
// here the validation error
console.log(validation)
// here the body
console.log(request.body)
reply.send(validation)
} else {
reply.send(error)
}
}
fastify.setErrorHandler(errorHandler)
fastify.inject({
method: 'PUT',
url: '/',
payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
console.log(res.json())
})
This will log:
[
{
keyword: 'type',
dataPath: '[0]',
schemaPath: '#/items/type',
params: { type: 'object' },
message: 'should be object'
},
{
keyword: 'type',
dataPath: '[1]',
schemaPath: '#/items/type',
params: { type: 'object' },
message: 'should be object'
}
]
[ 1, 2, { id: 'thirdObject' } ]
As you can see, thanks to validation[].dataPath you are able to understand which elements of the body array is not valid and merge the data to return your info.
Consider that the handler will be not executed in this scenario. If you need to execute it regardless the validation, you should do the validation job in a preHandler hook and avoid the default schema validation checks (since it is blocking)
edit
const fastify = require('fastify')()
const S = require('fluent-schema')
let bodyValidator
fastify.decorateRequest('hasError', function () {
if (!bodyValidator) {
bodyValidator = fastify.schemaCompiler(S.array().items(S.object()).minItems(1).valueOf())
}
const valid = bodyValidator(this.body)
if (!valid) {
return bodyValidator.errors
}
return true
})
fastify.addHook('preHandler', (request, reply, done) => {
const errors = request.hasError()
if (errors) {
console.log(errors)
// show the same errors as before
// you can merge here or set request.errors = errors to let the handler read them
reply.send('here merge errors and request.body')
return
}
done() // needed to continue if you don't reply.send
})
fastify.put('/', { schema: { body: S.array() } }, (req, reply) => {
console.log('handler')
reply.send('handler')
})
fastify.inject({
method: 'PUT',
url: '/',
payload: [1, 2, { id: 'thirdObject' }]
}, (_, res) => {
console.log(res.json())
})
I don't know the schema syntax you are using, but using draft 7 of the JSON Schema (https://json-schema.org/specification-links.html, and see also https://json-schema.org/understanding-json-schema for some reference material), you can do:
{
"type": "array",
"minItems": 1
}
If you want to ensure that at least one, but not necessarily all items match your object type, then add the "contains" keyword:
{
...,
"contains": ... reference to your object schema here
}
I want to use the $push method to push an object into a nested array. But i cant get it to work that you can dynamically get the right object inside of the array. Let me explain better by showing the code.
This is my Schema:
var StartedRaceSchema = new mongoose.Schema({
waypoints: {
type: Object,
name: String,
check_ins: {
type: Object,
user: {
type: Object,
ref: 'User'
}
}
}
});
When you check in on a waypoint, it has to be pushed in the correct waypoints nested Check_ins
This is the code for the update:
StartedRace.findByIdAndUpdate(req.params.id,
{ $push: { 'waypoints.1.check_ins': req.body.user } },
function (error) {
if (error) {
console.log(error)
res.send({
success: false,
error: error
})
} else {
res.send({
success: true
})
}
}
)
As you can see i can only get it to work with fields like:
'waypoints.1.check_ins'
That 1 needs to be dynamically because it gets send within the parameters.
But i can not get it to work dynamically, only hard coded.
Does anyone know how to do this?
Populate the collection with a list of check_ins enumerated by their ids.
waypoints.check_ins = {
...waypoints.check_ins,
[response.id]: response
}
You'd then have a list of check_ins that can referenced by their ids.
You could try this syntax instead of the dot notation:
let id = req.params.id;
StartedRace.findByIdAndUpdate(req.params.id,
{ $push: { waypoints: { id: { check_ins: req.body.user } } } }, { new : true } )
.exec()
.then(race => console.log(race))
.catch(err => err);
I used a Promise, but it's the same with a callback.
I'm using mongoose 4.6.6, express 4.13, passport 0.3.
I have the next mongoose Schema
var userSchema = new Schema({
nombre: String,
apellidos: String,
email: String,
pass: String,
fecha_registro : { type: Date, default: Date.now },
rol_list: [Schema.Types.ObjectId], // generic array of objectId
deleted: {type: Boolean, default: false}
});
module.exports = mongoose.model('User', userSchema);
When I search a user and try to populate the "rol_list" array, is always empty.
I have looked in mongo the users are well filled, but mongoose return it empty.
passport.deserializeUser(function(id, done) {
User.findById(id)
.populate('rol_list')
.exec(function(err, user) {
console.log(user);
done(err, user);
});
});
The console.log(user) show always the array rol_list empty.
If I assign a reference to the ObjectId like:
rol_list: [{ type: Schema.Types.ObjectId, ref: 'Rol1' }]
than is correct filled, logically only with the element "Rol1".
Any idea?
There is an option in .populate(...) mongoose function that allow you to specify the model that's behind the ObjectId.
#example
Conversation.find().populate('creator', null, 'User2').exec(callback);
Stack overflow post: mongoose-populate-field-without-ref-option
If you want array of only object ids then don't use populate with it.
like:
passport.deserializeUser(function(id, done) {
User.findById(id)
.exec(function(err, user) {
console.log(user);
done(err, user);
});
});