I'm writing a node.js app that uses the pg package for accessing a PostgreSQL database. The issue I'm running into is that if I do a query like this:
select * from posts p inner join blogs b on b.id = p.blog_id
When I get the results, they're all in the same namespace, so any field repeated in the blogs table will overwrite those in the posts table.
My question is, what's the best way of binding these results to objects?
Ideally, I'd like a result like:
{
id: 1,
name: 'A post name',
published_at: (some date object),
blog_id: 1,
b: {
id: 1,
name: 'A blog name'
}
}
But I'm open to any convenient solution short of adding an alias for every column manually.
http://www.postgresql.org/docs/9.3/static/functions-json.html
http://www.postgresql.org/docs/9.4/static/functions-aggregate.html
You may want to look at the json features of Postgres. If I'm understanding you right, and without a test database something like this may be close to what you're looking for:
SELECT
p.*, /* Select all the post fields */
row_to_json(blogs.*) as b /* Use the row_to_json function on the blogs results */
FROM
posts p
INNER JOIN
blogs ON (blogs.id=p.blog_id); /* Join blogs on the proper fields */
Returns:
{
id: 3,
name: 'test',
published_at: 2015-10-08,
blog_id: 2,
b: {
id:2,
name:"test 2"
}
}
Here's a great tutorial on them:
http://bender.io/2013/09/22/returning-hierarchical-data-in-a-single-sql-query/
If you change your query to
'SELECT * FROM posts, blogs WHERE posts.id = blogs.id;'
you should have your column names prefixed with either 'posts' or 'blogs'
If you want a nested result like above, you'll have to run some manual processing.
res.map(d => {
return {
id: d.posts_id
b : {
id: d.blogs_id
}
};
});
Related
I have a JSONB column in DB.
I'd like to have request to DB where I can check if some value in this JSON it true or false:
SELECT *
FROM table
WHERE ("json_column"->'data'->>'data2')::boolean = true AND id = '00000000-1111-2222-3333-456789abcdef'
LIMIT 1
So, my sequelize request:
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`]: true,
id: someIdVariable,
},
order: [/* some order, doesn't matter */],
})
And sequelize generate bad result like:
SELECT *
FROM table
WHERE "(json_column"."->'data'->>'data2')::boolean" = true AND id = '00000000-1111-2222-3333-456789abcdef'
LIMIT 1
Split my column by . and add " to every element.
Any idea how to get rid of adding " to the column in where condition?
Edit:
Here is my query with sequelize.literal():
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[sequelize.literal(`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`)]: true,
id: someIdVariable,
},
order: [/* some order, doesn't matter */],
})
You can use Sequelize.literal() to avoid spurious quotes. IMHO, wrapping the json handling in a db function might also be helpful.
I just came across a similar use case.
I believe you can use the static sequelize.where method in combination with sequelize.literal.
Here is the corresponding documentation in sequelize API reference: https://sequelize.org/master/class/lib/sequelize.js~Sequelize.html#static-method-where
And here is an example (although I will admit hard to find) in the regular documentation:
https://sequelize.org/master/manual/model-querying-basics.html#advanced-queries-with-functions--not-just-columns-
In the end for your specific sit try something like this:
const someVariableWithColumnName = 'data2';
Model.findOne({
where: {
[Op.and]: [
// We provide the virtual column sql as the first argument of sequelize.where with sequelize.literal.
// We provide the matching condition as the second argument of sequelize.where, with the usual sequelize syntax.
sequelize.where(sequelize.literal(`$("json_column"->'data'->>'${someVariableWithColumnName}')::boolean$`), { [Op.eq]: true }),
{ id: someIdVariable }
]
})
I got the following data model: https://imgur.com/a/AwwpW9F
Basically, A User can belong to many Projects, and a Project can have many Users and I'm tying that together through a join table called UserProjects
With a raw SQL query I can go
SELECT "user".email, project.name FROM "user"
JOIN userprojects ON userprojects.user_id = "user".id
JOIN project ON project.id = userprojects.project_id
Which gives me
email(On User table) name(On Project table)
first#email.com Project X
first#email.com Project Y
second#email Project Y
How would I structure this query with Objection ORM? Perhaps I can just do a raw query straight? Something like
User.query().raw(SELECT "user".email, project.name FROM "user"
JOIN userprojects ON userprojects.user_id = "user".id
JOIN project ON project.id = userprojects.project_id)
?
instead of doing all the stuff by yourself, Objection.js can do it for you. You can just declare a ManyToManyRelation.
static relationMappings = {
projects: {
relation: Model.ManyToManyRelation,
modelClass: Project, // imported Objection class of "Project"
join: {
from: 'user.id',
through: {
from: 'userprojects.user_id',
to: 'userprojects.project_id'
},
to: 'project.id'
}
}
}
Then you can just get the projects of a User using eager loading:
User.query().eager('projects').findById(userId)
And you will get something like:
User {
id: 3,
firstname: 'firstname',
lastname: 'lastname',
email: 'email',
projects: [
{id: 1,
name: 'name1'},
{id: 2,
name: 'name2'},
]
}
2020 Update:
Since version 2 of Objection.js, eager method has been renamed as withGraphFetched:
User.query().withGraphFetched('projects').findById(userId)
Nvm, found a solution
/close
JK,
Here's what worked for me if anyone else would run into the same issue:
return User.query().where("user_id", parent.id)
.join('userprojects', 'user.id', '=', 'userprojects.user_id')
.join('project', 'project.id', '=', 'userprojects.project_id')
.select('user.id', 'userprojects.project_id', 'project.name')
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
We have an application where we're storing two types of documents in a Mongo database:
contacts, which basically represent people
filters, which are essentially a stored MongoDB query that represents a "saved search" for a user.
Here's a simplified version of what the data models would look like:
contacts: [
{ id: 1, name: 'Phil', age: 40 },
{ id: 2, name: 'Bob', age: 34 }
]
filters: [
{ query: { name: 'Phil' } }
{ query: { age: { >: 30 } } }
]
Given a filter, it's relatively easy to list all contacts that match that filter:
db.contacts.find(filter.query);
What's harder is finding all filters that match a certain contact. Right now we have something like the following:
matchedFilters = []
_.each(filters, function(filter) {
if (db.contacts.find(_.extend(filter.query, {id: contact_id}).length > 0) {
matchedFilters.push(filter.id)
}
});
Essentially, we need to ask mongo about each filter individually. This results in a huge amount of queries to Mongo.
At the time that we are evaluating this query, we have all the relevant information about the contact we are trying to find. Is there any way to apply the Mongo query syntax to an in-memory Javascript object without needing to ask Mongo about it?
Alternatively, is there a way to ask Mongo to conduct a large number of queries in a single round trip?
Have a look at sift.js. I think, it's exactly what you're looking for.
And here is a blog post about it.
I am currently using StrongLoop as my API backend server and Mongodb as data storage engine.
Let's say there is a collection called article. It has two fields title, and content. And there are two frontend pages to display a list of articles and view a single article.
Obviously the data list page only need title field and the view page need both. Currently the GET method of StrongLoop API return all fields including content. It cost extra traffic. Is there any way that can just return specific field?
Mongodb support projection in find() method for this. How can I do the same thing by StrongLoop?
Have you taken a look at the filters offered. http://docs.strongloop.com/display/LB/Querying+models
Query for NodeAPI:
server.models.Student.findOne({where: {RFID: id},fields: {id: true,schoolId: true,classId: true}}, function (err, data) {
if (err)
callback(err);
else {
callback();
}
})
Query for RestAPI :
$http.get('http://localhost:3000/api/services?filter[fields][id]=true&filter[fields][make]=true&filter[fields][model]=true')
.then(function (response) {
}, function (error) {
});
You can use fields projections,
Sample Record:
{ name: 'Something', title: 'mr', description: 'some desc', patient: { name: 'Asvf', age: 20, address: { street: 1 }}}
First Level Projection:
model.find({ fields: { name: 1, description: 1, title: 0 } })
and I think Strong loop is not yet supporting for second-level object filter, does anyone know how to filter second-level object properties or is yet to implement?.
Second Level Projection: (Need help here)
Ex: 2
model.find({ fields: { name: 1, 'patient.name': 1, 'patient.age': 1, 'patient.address': 0 } })
// Which results { name } only