How do i achieve the following relation in bookshelf.js
SELECT user_accounts.user_id, `event_id`
FROM `calendar_events`
JOIN user_accounts ON user_accounts.user_id = calendar_events.`created_by`
LIMIT 10
My Model
var CalendarEvent = bookshelf.Model.extend({
tableName: 'calendar_events',
hasTimestamps: ['created_on'],
user: function() {
return this.hasOne('UserAccount','user_id');
}
});
var UserAccount = bookshelf.Model.extend({
tableName: 'user_account'
});
If you wanted to get that exact style query, you could use the knex query builder and try to build a query to match your needs
have not tested this but should be something like this
CalendarEvent.forge().query(function(qb){
qb.join('user_accounts', 'user_accounts.user_id', '=', 'calendar_events.created_by');
//qb.where() //if you wanted
//qb.andWhere(); //and if you wanted more
qb.limit(10);
})
.fetchAll();
(you dont need the .where or .andWhere, just added those for fun)
It might be possible to do it purely in bookshelf, but i'm not sure of how at the moment.
I suppose you found the solution since 2016 but here is the answer
var CalendarEvent = bookshelf.Model.extend({
tableName: 'calendar_events',
hasTimestamps: ['created_on'],
user: function() {
return this.belongsTo(UserAccount, 'user_id', 'created_by');
}
});
var UserAccount = bookshelf.Model.extend({
tableName: 'user_account'
});
Use belongsTo() method because the foreign key is in the CalendarEvent Model and not in the UserAccount model. Append the name of the foreign key to belongsTo() parameters to specify the name of the foreign key.
Then, use the Bookshelf model like this :
CalendarEvent
.forge()
.fetchPage({ pageSize: 10, withRelated: ['user'] })
.then(data => {
// Data retrieval
})
.catch(exc => {
// Error management
});
The fetchPage method uses page and pageSize to create a pagination. You will retrieve a data.pagination object containing rows info (number to rows, page number, page size, page total count) and data.toJSON() will retrieve your models.
Related
lets say I have post Model and schema contains UserId , Title, Desc and likes array which takes userId's as ref
when I make a query I get a virtual property like this to find out num of like of a post would have
schema.virtual("numLikes").get(function () {
return this.likes.length;
});
But the problem is when I run the findById() Method I dont want to get likes from database because likes array would contain large list of users
const post = await Post.findById(postId).select("-likes -shares");
so how do I get Likes count without fetching likes array ?
I believe this can be done using aggregation, by using the $size operators in a projection:
const aggregate = Post.aggregate([
{ $match: {_id: postId}},
{ $project: {
numberOfLikes: { $size: "$likes" }
}
}
]);
https://docs.mongodb.com/manual/reference/operator/aggregation/size/
https://mongoosejs.com/docs/api/aggregate.html#aggregate_Aggregate-project
How to append a list of JSON filed in nodejs Sequelize
database: Postgres
I have one model field like that :
student_list:
{
type: Sequelize.ARRAY(Sequelize.JSON),
allowNull: true
},
I am trying to append Sequelize Array of JSON like this way but not get sucessfully append list:
var data = ({'share_by':"aaa",'list_name':"list_name"})
student.update(
{'student_list': Sequelize.fn('array_append', Sequelize.col('student_list'), data)},
{'where': {studet_id: student_id}}
);
how to do this?
You can do a find() and get the element value, update the element and do a update with data updated. Like this:
// Get the actual student data
const student = await student.findOne(studentId);
// Spread and add the new value
const student_list = {
...student_list,
data
};
// Update the field
await student.update(
{
student_list
},
{
where: {
student_id
}
}
);
I'd like to know if there's a way to directly search within a linked collection.
I'm using Express with mongoosejs
I have the following situation.
I have 3 collections, deal, product and store and I have associated product and store with the deal collection.
const DealSchema = new Schema({
...
product: {
type: Schema.Types.ObjectId,
ref: 'product'
},
shop: {
type: Schema.Types.ObjectId,
ref: 'shop'
},
...
});
In my collection product, I have a field called upc.
I have a controller that handles deals creation, however before I create a deal, I want to check whether or not there's already a deal with the same UPC in this store if so I'll only update the confirmedBy field.
This is my controller
async create(req, res) {
const dealProps = req.body;
const product = await Products.findOne({ upc: dealProps.upc });
let deal;
if (product) {
deal = await Deals.findOne({ product: product._id });
if (deal.shop.toString() === dealProps.shop.toString()) {
const updatedDeal = await Deals.findOneAndUpdate({ product: product._id }, {
$push: {confirmedBy: dealProps.user }
});
res.send(updatedDeal);
}
} else {
deal = await (new Deals(dealProps)).save();
res.send(deal);
}
}
I've tried to search directly within the product collection like this:
const product = await Deals.findOne({'product.upc': dealProps.upc });
However it returns null.
Is there a way to search directly within a linked collection? Do I need to create an index for the upc field in the product collection?
If not, should I rethink my deals collection to add the upc and storeId to simplify the lookup?
Thank you for your help, much appreciated.
A few questions about storing user data in MongoDB. What is the best place in mongo to store user specific data, such as User settings, User photo url, User friends, User events?
In Mongo, user data is stored in:
Meteor
/ Collections
/ users
/ _id
/ profile
/ services
Should I add there a new collections? In a following way:
/ events / _id's
/ friends / _id's
/ messages / _id's
/ settings
How should I publish user's private data and manipulate this collections, to be sure it's save and no one else will modify or have access to private data of another person.
You can add data to the users profile field like this:
Meteor.users.update( id, { $set: { 'profile.friends': someValue } } );
To only publish specific fields you can do something like this:
Meteor.publish( 'users', function () {
return Meteor.users.find( {}, { fields: { 'profile.friends': 1 } } );
});
Hope this helps.
Normalization
"Database normalization is the process of organizing the attributes and tables of a relational database to minimize data redundancy."
MongoDB is a non relational database. This makes normalized data hard to query for. This is why in MongoDB we denormalize data. That makes querying for it easier.
It depends on your use-case. The question is basically when to demormalize. It's mostly a matter of opinion. But objective here are some pros and cons:
Pros to demormalization
It's easier to retrieve data (Due to Mongo not beeing a relational DB)
It performs better if you are always getting the data in bulk
Cons to demormalization
It doesn't scale well for things like user.messages (You can't just publicize some messages)
In your case I'd definitly go for seperate collections for events, friends and messages. Setting can't expand infinitly. So I'd put it into the users collection.
Security
I'd use a publications and allow and deny for this. Let me make an example for Messages:
Collection
Messages = new Mongo.Collection('Messages')
Messages.insert({
sender: Meteor.userId,
recipient: Meteor.users.findOne()._id,
message: 'Hello world!'
})
Publication
Meteor.publish('userMessages', function (limit) {
return Messages.subscribe({
$or: [
{sender: this.userId},
{recipient: this.userId}
]
}, {limit: limit})
})
Allow
function ownsMessage (user, msg) {
return msg.sender === user ? true : false
}
Messages.allow({
insert: function (userId, newDoc) {
!!userId
},
update: function (userId, oldDoc, newDoc) {
if(
ownsMessage(userId, oldDoc) &&
ownsMessage(userId, newDoc)
) return true
return false
},
remove: function () {
return false
}
})
This code is untested, so it might contain small errors
I'm building a simple database with node, express and sequelize. I have created my models, and sequelize created the tables in my database.
I have the models User and City, with a many to many relationship. Sequelize created the tables Users, Cities and a join table CitiesUsers: with UserId and CityId.
My question is when I create a new user how do I update that join table? The CityId property gets ignored on create.
//Models use
//City.hasMany(User);
//User.hasMany(City);
var user = User.build({
first_name: 'John',
last_name: 'Doe',
CityId: 5
});
user.save();
After digging further into the documentation, I believe I've found the answer.
When creating a many to many relationship sequelize creates get, set and add methods to each model.
From the docs assuming models User and Project with many to many:
http://docs.sequelizejs.com/en/latest/docs/associations/#belongs-to-many-associations
This will add methods getUsers, setUsers, addUsers to Project, and
getProjects, setProjects and addProject to User.
So in my case I did the following where "city" is a specific City model returned from City.find...
//user.setCities([city]);
models.User.find({ where: {first_name: 'john'} }).on('success', function(user) {
models.City.find({where: {id: 10}}).on('success', function(city){
user.setCities([city]);
});
});
You can create a new instance of the model used as the join table once both City and User models have been created.
const User = sequelize.define('user')
const City = sequelize.define('city')
const UserCity = sequelize.define('user_city')
User.belongsToMany(City, { through: UserCity })
City.belongsToMany(User, { through: UserCity })
const user = await User.create()
const city = await City.create()
const userCity = await UserCity.create({
userId: user.userId,
cityId: city.cityId,
})
Just to add on to the many excellent answers in this thread, I find generally that when I have one entity referencing another, I want to create the referenced entity if (and only if) it does not already exist. For this I like to use findOrCreate().
So imagine you were storing articles, and each article could have any number of tags. What you'd typically want to do is:
Iterate through all the desired tags, and check if they exist. Create them if they don't already exist.
Once all the tags have been found or created, create your article.
Once your article has been created, link it to the tags you looked up (or created) in step 1.
For me, this winds up looking like:
const { article, tags } = model.import("./model/article");
let tagging = [
tags.findOrCreate({where: {title: "big"}}),
tags.findOrCreate({where: {title: "small"}}),
tags.findOrCreate({where: {title: "medium"}}),
tags.findOrCreate({where: {title: "xsmall"}})
];
Promise.all(tagging).then((articleTags)=> {
article.create({
title: "Foo",
body: "Bar"
}).then((articleInstance) => {
articleInstance.setTags(articleTags.map((articleTag) => articleTag[0]));
})
})
From The docs v3:
// Either by adding a property with the name of the join table model to the object, before creating the association
project.UserProjects = {
status: 'active'
}
u.addProject(project)
// Or by providing a second argument when adding the association, containing the data that should go in the join table
u.addProject(project, { status: 'active' })
// When associating multiple objects, you can combine the two options above. In this case the second argument
// will be treated as a defaults object, that will be used if no data is provided
project1.UserProjects = {
status: 'inactive'
}
u.setProjects([project1, project2], { status: 'active' })
// The code above will record inactive for project one, and active for project two in the join table