What i feel like I am doing is very basic yet I'm getting an undefined object everytime, I have an application called Example and have defined an ApplicationRoute:
Example.ApplicationRoute = Em.Route.extend({
model: function() {
var user = this.store.find('user',1);
console.log("username: " + user.username);
return { user: user };
}
});
And My Model:
var attr = DS.attr
Example.User = DS.Model.extend({
//logged in state can be loggedIn, loggedOff, pending, failed
loggedInState: attr('string'),
token: attr('string'),
username: attr('string'),
firstName: attr('string'),
lastName: attr('string'),
email: attr('string'),
isLoggedIn: Em.computed.match('loggedInState', /loggedIn/)
});
And lastly a FIXTURE:
Example.User.FIXTURES = [
{
id: 1,
username: 'jxnagl',
loggedInState: 'loggedOn',
firstName: 'Jared',
lastName: 'Nagle',
token: '1234',
email: 'jared.nagle#example.com'
}
]
It is my understanding that this FIXTURE will be present in the store by the time the model hook is called in the Application Route, But when i access my application, I get the following message in my console:
"username: undefined"
Is there something I'm missing? Ember's documentation shows similar examples and I don't see a difference.
"find" returns promise.
var user = this.store.find('user', 1).then(function(user){
console.log('username: ' + user.get('username'));
});
I think your Route should be like
Example.ApplicationRoute = Em.Route.extend({
model: function() {
return this.store.find('user',1);
}
});
I think the problem here was the ember-auth plugin I had a dependency on. It was somehow interferring my application. Removing this plugin solved the problem for me. :)
Related
I'm setting up simple API using Postgresql, Knex.js and Objection.js. I created User model with "location" property. This "location" property is another table. How I have to insert that user to database with defaults 'city' and 'country' in 'location' property?
I already tried to use 'static get jsonSchema' in model itself and 'allowInsert' method in mutation but when I fetching created that user the 'location' still 'null'.
So, let's say we have migration for users_table:
exports.up = knex =>
knex.schema.createTable('users', table => {
table.increments('id').primary();
table
.string('email')
.unique()
.notNullable();
table.string('firstName').notNullable();
table.string('lastName').notNullable();
table.string('password').notNullable();
});
exports.down = knex => knex.schema.dropTable('users');
And we have location_table:
exports.up = knex =>
knex.schema.createTable('locations', table => {
table.increments('id').primary();
table.string('country').defaultTo('USA');
table.string('city').defaultTo('San Francisco');
table
.integer('user_id')
.references('id')
.inTable('users')
.onUpdate('CASCADE')
.onDelete('CASCADE');
});
exports.down = knex => knex.schema.dropTable('locations');
Here User Model with objection.js:
export default class User extends Model {
static get tableName() {
return 'users';
}
// wrong probably
static get jsonSchema() {
return {
type: 'object',
properties: {
location: {
type: 'object',
properties: {
city: {
type: 'string',
default: 'Los Angeles',
},
country: {
type: 'string',
default: 'USA',
},
},
},
},
};
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
static get relationMappings() {
return {
location: {
relation: Model.HasOneRelation,
modelClass: Location,
join: {
from: 'users.id',
to: 'locations.user_id',
},
},
};
}
}
And Location model:
export default class Location extends Model {
static get tableName() {
return 'locations';
}
static get relationMappings() {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: `${__dirname}/User`,
join: {
from: 'locations.user_id',
to: 'users.id',
},
},
};
}
}
My mutation when I creating new User:
// ...
const payload = {
email,
firstName,
lastName,
password: hash,
};
const newUser = await User.query()
.allowInsert('[user, location]')
.insertAndFetch(payload);
// ...
And in the end query:
// ...
User.query()
.eager('location')
.findOne({ email });
// ...
From query of user I expect to see the object with locatoin propety with my defaults. Example:
{
email: 'jacklondon#gmail.com',
firstName: 'Jack',
fullName: 'Jack London',
id: '1',
lastName: 'London',
location: {
city: 'San Francisco',
country: 'USA',
},
userName: 'jacklondon1',
__typename: 'User',
}
So, where I made mistake with such simple operation?
One to One Solution
I think part of the issue is that your allow insert included the user object. You shouldn't include the user in the allow insert because it's implicit since you're on the User model (example). The other issue you had was that you were trying to use insertAndFetch method. insertAndFetch cannot be used when inserting a graph. You need to use the insertGraph method to insert a graph (docs). Since you are using Postgres, you can chain the returning(*) method and it will return the result without additional queries (example). Finally, since you're asking for a one-to-one relation, you have to specify a city and country every time. Objection will not know it needs to create a new row without specifying it (even if you have configured the database to have default values). The way I accomplished this for you was to use default parameters.
const createNewuser = async (email, firstName, lastName, hash, city = 'Los Angeles', country = 'USA') => {
const newUser = await User
.query()
.insertGraph({
email,
firstName,
lastName,
password: hash,
location: {
city: city,
country: country
}
})
.returning('*');
return newUser;
}
Additional Thought to Ponder
I'm not sure why you have a one-to-one relationship between user and location. Why not just make city, state, and country part of the user's table since it's already one to one?
However, what I think you're really going for is a one-to-many relationship between user and location. One location has multiple users. This would put you into 3rd normal form by reducing the amount of duplicate data in your database since you wouldn't duplicate a city/country for each user in an identical location.
If you're just learning objection, I would recommend reading up on graphs in the documentation.
I have a post route that receives data from a PUT request in an express app that aims to update a mongoose document based on submitted form input. The "Base" model is Profile, and I have two discriminator models Helper and Finder that conditionally add fields to the Profile schema (see below for details).
Thus, req.body.profile will contain different fields depending on the discriminator it's associated with, but will always contain the fields (username, email city, accountType) present in the "base" model, Profile.
Before I send my PUT request, an example of a document in Profile looks like this:
{ jobTitle: '',
lastPosition: '',
email: '',
city: '',
accountType: 'helper',
_id: 5c77883d8db04c921db5f635,
username: 'here2help',
__v: 0 }
This looks good to me, and suggests that the model is being created as I want (with base fields from Profile, and those associated with the Helper model - see below for models).
My POST route then looks like this:
router.put("/profile/:id", middleware.checkProfileOwnership, function(req, res){
console.log(req.body.profile);
Profile.findOneAndUpdate(req.params.id, req.body.profile, function(err, updatedProfile){
if(err){
console.log(err.message);
res.redirect("/profile");
} else {
console.log(updatedProfile);
res.redirect("/profile/" + req.params.id);
}
});
});
The information I receive from the form (console.log(req.body.profile)) is what I expect to see:
{ accountType: 'helper',
username: 'here2help',
email: 'helpingU#me.com',
city: 'New York',
jobTitle: 'CEO',
lastPosition: 'sales rep'}
However, after updating the document with req.body.profile in Profile.findOneAndUpdate(), I do not see my returned document updated:
console.log(updatedProfile)
{ jobTitle: '',
lastPosition: '',
email: 'helpingu#me.com',
city: 'New York',
accountType: 'helper',
_id: 5c77883d8db04c921db5f635,
username: 'here2help',
__v: 0 }
So, the fields that are defined in my 'Base' model (ie those defined in ProfileSchema - see below) are being updated (e.g. city), but those that are in my discriminators are not - see below.
The updated information is clearly present in req, but is not propagated to the Profile model - How can this be?
I've also tried using findByIdAndUpdate but I get the same result.
Here are the Schemas I'm defining:
Profile - my "base" schema:
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var profileSchema = new mongoose.Schema({
username: String,
complete: { type: Boolean, default: false },
email: { type: String, default: "" },
city: { type: String, default: "" }
}, { discriminatorKey: 'accountType' });
profileSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Profile", profileSchema);
Finder
var Profile = require('./profile');
var Finder = Profile.discriminator('finder', new mongoose.Schema({
position: { type: String, default: "" },
skills: Array
}));
module.exports = mongoose.model("Finder");
Helper
var Profile = require('./profile');
var Helper = Profile.discriminator('helper', new mongoose.Schema({
jobTitle: { type: String, default: "" },
lastPosition: { type: String, default: "" }
}));
module.exports = mongoose.model("Helper");
This is my first attempt at using discriminators in mongoose, so it's more than possible that I am setting them up incorrectly, and that this is the root of the problem.
Please let me know if this is unclear, or I need to add more information.
It matters what schema you use to query database
Discriminators build the mongo queries based on the object you use. For instance, If you enable debugging on mongo using mongoose.set('debug', true) and run Profile.findOneAndUpdate() you should see something like:
Mongoose: profiles.findAndModify({
_id: ObjectId("5c78519e61f4b69da677a87a")
}, [], {
'$set': {
email: 'finder#me.com',
city: 'New York',
accountType: 'helper',
username: 'User NAme', __v: 0 } }, { new: true, upsert: false, remove: false, projection: {} })
Notice it uses only the fields defined in Profile schema.
If you use Helper, you would get something like:
profiles.findAndModify({
accountType: 'helper',
_id: ObjectId("5c78519e61f4b69da677a87a")
}, [], {
'$set': {
jobTitle: 'CTO',
email: 'finder#me.com',
city: 'New York',
accountType: 'helper ',
username: 'User Name', __v: 0 } }, { new: true, upsert: false, remove: false, projection: {} })
Notice it adds the discriminator field in the filter criteria, this is documented:
Discriminator models are special; they attach the discriminator key to queries. In other words, find(), count(), aggregate(), etc. are smart enough to account for discriminators.
So what you need to do when updating is to use the discriminator field in order to know which Schema to use when calling update statement:
app.put("/profile/:id", function(req, res){
console.log(req.body);
if(ObjectId.isValid(req.params.id)) {
switch(req.body.accountType) {
case 'helper':
schema = Helper;
break;
case 'finder':
schema = Finder;
break;
default:
schema = Profile;
}
schema.findOneAndUpdate({ _id: req.params.id }, { $set : req.body }, { new: true, upsert: false, remove: {}, fields: {} }, function(err, updatedProfile){
if(err){
console.log(err);
res.json(err);
} else {
console.log(updatedProfile);
res.json(updatedProfile);
}
});
} else {
res.json({ error: "Invalid ObjectId"});
} });
Notice, above is not necessary when creating a new document, in that scenario mongoose is able to determine which discriminator to use.
You cannot update discriminator field
Above behavior has a side effect, you cannot update the discriminator field because it will not find the record. In this scenario, you would need to access the collection directly and update the document, as well as define what would happen with the fields that belong to the other discriminator.
db.profile.findOneAndUpdate({ _id: req.params.id }, { $set : req.body }, { new: true, upsert: false, remove: {}, fields: {} }, function(err, updatedProfile){
if(err) {
res.json(err);
} else {
console.log(updatedProfile);
res.json(updatedProfile);
}
});
Please add option in findOneAndUpdate - { new: true };
In Moongose findOneAndUpdate() Method have four parameters
like
A.findOneAndUpdate(conditions, update, options, callback) // executes
And you need to execute like this
var query = { name: 'borne' };
Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)
or even
// is sent as
Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)
This helps prevent accidentally overwriting your document with { name: 'jason bourne' }.
Pretty new to Node.js and Mongoose.
Trying to perform a basic set action after finding the relevant object, however I get the following error:
TypeError: {found object}.set is not a function.
The following is the code causing the error:
UserProfile.find({"user": req.params.id}, function (err, userProfile) {
if (err) {
console.log("Saving User profile - Error finding user");
} else { // no error
if (userProfile) { // if userProfile is found
console.log("Saving User profile - userProfile found for user: " + userProfile);
userProfile.set ({
gender: req.body.gender,
dob: req.body.dob,
phone: req.body.phone,
phone2: req.body.phone2,
state: req.body.state,
country: req.body.country
});
}
}
});
The following is the error i receive:
TypeError: userProfile.set is not a function
If I'm trying to use the "set" function on a new object created based on the same model, it works with no issue
var userProfile = new UserProfile ();
userProfile.set ({
gender: req.body.gender,
dob: req.body.dob,
phone: req.body.phone,
phone2: req.body.phone2,
state: req.body.state,
country: req.body.country
});
The following is the model:
var mongoose = require("mongoose");
var UserProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
gender: String,
phone: String,
phone2: String,
dob: Date
});
module.exports = mongoose.model ("UserProfile", UserProfileSchema);
Use findOne not find. The former returns an object as the 2nd argument in the callback, the latter returns an array as the 2nd argument in the callback.
.find returns an array of documents. try using .findOne which returns the first found document
I have some existing users from a non-Meteor app. I'd like to import them and keep their _id because I reference it in other documents.
I am able to create a user like this:
if (Meteor.users.find().count() === 0) {
Accounts.createUser({
username: 'test',
email: 'test#example.com',
password: 'password'
});
}
However, it doesn't seem to work to set the _id field in that block of code.
Is there a way to change the user's _id?
Try using Meteor.users.insert instead of Accounts.createUser. It's slightly more complicated and requires an extra step to set the password:
var newUserId = Meteor.users.insert({
_id: 'whatever',
profile : { fullname : 'test' },
email: ['test#example.com']
})
Accounts.setPassword(newUserId, 'password');
I am currently trying to learn how to work with NoSQL, coming from a relational database background. In this project, I am using Express with Mongoose.
I am struggling with callbacks as I try to merge two models together, which reference each other. I am trying to edit each item in a group of one model (Ribbits) to contain the attributes of another (Users who posted a Ribbit). Because the call to find the User associated with a Ribbit is asynchronous, I am unable to return the collection of edited Ribbits (with user info).
In my website, I have ribbits (a.k.a. tweets) which belong to users. Users can have many ribbits. In one of my pages, I would like to list all of the ribbits on the service, and some information associated with the user who posted that ribbit.
One solution I found was embedded documents, but I discovered that this is, in my case, limited to showing ribbits which belong to a user. In my case, I want to start by getting all of the ribbits first, and then, for each ribbit, attach info about who posted that.
Ideally, I'd want my schema function to return an array of Ribbit objects, so that I can then render this in my view.
// models/user.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var userSchema = Schema({
username: String,
email: String,
password: String,
name: String,
profile: String,
ribbits: [{
type: Schema.Types.ObjectId,
ref: 'Ribbit',
}]
});
module.exports = mongoose.model('User', userSchema);
// models/ribbit.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
User = require('./user');
var ribbitSchema = Schema({
content: { type: String, maxlength: 140 },
created: { type: Date, default: Date.now() },
owner: { type: Schema.Types.ObjectId, ref: 'User' },
});
ribbitSchema.methods.getOwnerObj = function(cb) {
return User.findOne({ _id: this.owner }, cb);
}
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}, function(err, ribbits) {
console.log('Before Transform');
console.log(ribbits);
ribbits.forEach(function(ribbit) {
ribbit.getOwnerObj(function(err, owner) {
ribbit = {
content: ribbit.content,
created: ribbit.created,
owner: {
username: owner.username,
email: owner.email,
name: owner.name,
profile: owner.profile,
}
};
});
});
});
}
module.exports = mongoose.model('Ribbit', ribbitSchema);
If I understand correctly, you can use Mongoose populate method for this scenario:
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}).populate('owner').exec(function(err, ribbits){
console.log(ribbits[0].owner)
return cb(err, ribbits);
})
}