mongoose populate generic array of ObjectId without ref - express

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

Related

TypeError: doc.data is not a function

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.

How to add array of objects to mongoDB model

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.

Database error handling with Sequelize when column doesn't exist?

I'm trying to handle different errors that might show up when inserting into a MYSQL database.
Using Sequelize with express.
My foo.js model file looks like this:
module.exports = (sequelize, type) => {
return sequelize.define('event', {
id: {
type: type.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: type.STRING,
}
},{
freezeTableName: true,
rejectOnEmpty: true,
})
}
and my route file (or whatever you wanna call it), looks like this.
const Sequelize = require('sequelize')
const fooModel = require('../../models/Foo')
const router = require('express').Router();
const auth = require('../auth');
const bodyParser = require('body-parser');
const sequelize = new Sequelize('username', 'password', 'db', {
host: 'localhost',
dialect: 'mysql'
})
const Foo = fooModel(sequelize, Sequelize);
router.use(bodyParser.json({limit: '100mb'}));
router.use(bodyParser.urlencoded({ extended: true, limit: '100mb', parameterLimit: 1000000 }));
sequelize.sync({force: true})
.then(() => {
console.log('Worked');
});
router.post('/', (req,res,next) => {
if(Object.keys(req.body).length > 0){
return Foo.create({
Name: req.body.Name
}).then((result) => {
if(result){
return res.status(200).json(result);
}else{
return res.status(400).json({'error': 'Could not create record.'});
}
}).catch(Sequelize.DatabaseError, function(err){
return res.status(400).json(err);
}).catch(function(err){
res.send(err);
})
}else{
return res.status(400).json({'error': 'error'});
}
});
module.exports = router;
Whenever I try to post to the route with something like:
{
"name": "test",
"foo": "bar"
}
Sequelize accepts the body and puts "test" in the ”name” column, and ignores the "foo" column, because the "foo" column does not exist. Meaning, all I get back once it's posted is:
{"id": "123",
createdAt: 2020-01-23 13:337:00
updatedAt: 2020-01-23 13:337:00
}
And not an error as I expect.
What Im trying to do, is catch that error (that I today ain't recieving) whenever I try to post to a column that doens't exist, basically replicate a normal MYSQL error behaviour.
Could someone point me in the right direction on what I'm missing?
In my experience, it would be better to avoid this particular problem by validating the fields on the client side.
But, you can trap such a condition in js. You'll not get a DB exception because Sequelize isn't sending your unrecognized attributes to the database.
if (!Foo.attributes.hasOwnProperty('foo')) {
// some error handing here, for invalid field.
}
You could write a utility function to iterate through the attributes of req.body and send an appropriate error to the response.
FWIW, you'll find that Name is also invalid, because your model specifies(lower case) name
hth

How to create types.array in MST from external api - always returning proxy not object

I'm fetching data from external API, and want to store it in MST store as array. But the result is always proxy, not the object I wanted.
This is result from API:
(4) [Object, Object, Object, Object]
0:Object
id:1
name: "Foobar"
created_at: "2019-04-27 09:09:29"
updated_at:null
deleted_at:null
__proto__:Object
.........
This is my store:
const TypesModel = types.model({
name: types.maybe(types.string),
created_at: types.maybe(types.string)
});
export const TransactionTypeStore = types
.model("TransactionTypeStore", {
transaction_types: types.optional(types.array(TypesModel), [])
})
.actions(self => ({
getTypes: flow(function*(token) {
try {
const res = yield typesApi
.headers({ Authorization: `Bearer ${token}` })
.get()
.json();
console.log("result", res);
self.transaction_types = res;
// res.map(data => {
// self.transaction_types.push(data);
// });
} catch (err) {
console.log(err);
}
})
}));
And this is console.log of my MST store:
transaction_types:Proxy
[[Handler]]:Object
[[Target]]:Array(4)
0:ObjectNode
1:ObjectNode
2:ObjectNode
3:ObjectNode
$treenode:ObjectNode
length:4
toJSON:function toJSON()
Symbol(mobx administration):ObservableArrayAdministration
__proto__:Array(0)
[[IsRevoked]]:false
.........
Does anyone know how to deal with this kind of problem?
its similar to TypeScript, only you are using Mobx .MODEL
So lets say we want to create a ToDo list, which array that has id: number, name: string, isDone: boolean.
You first Define this Interface using Mobx .model, like this:
const singleToDoItem = types.model({
id: types.number,
name: types.string,
isDone: types.boolean
})
we then create an Actual Store, with ARRAY as a type (you can also use .optional) AND then put the singleToDoItem inside the types.array(singleToDoItem), so it looks like this:
const myStore = types.model({
list: types.array(singleToDoItem)
})
FINAL CODE will look like this:
const singleToDoItem = types.model({
id: types.number,
name: types.string,
isDone: types.boolean })
const myStore = types.model({
toDoList: types.array(singleToDoItem) })
I made a video on how to do this on my YouTube channel.
How does your res object look like, it should be an array of { name, created_at } objects - nothing more nothing less, is it? Also transaction_types will never be a mere array - types.array is a complex MST type, it has some array methods, but it's not an array. It's an observable array and you should treat it accordingly.
Also check this video tutorial by Michel Weststrate himself: Use observable objects, arrays, and maps to store state in MobX to get a better grip on the concept (create an account, it's free).

How to push items into an array in mongoose

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.