I am quite new to sequelize and mySQL and feel like I have tried everything in order to pass a search term ('query') to both the books table (searching against titles) and the authors table (searching against first_name or last_name). In the event of matching any of those values substrings it is to return the whole book and author information as a JSON object. When I just have the query focused on book title, it returns everything just fine. The problem comes in when I try to pass in Author columns. I have tried aliasing, nesting where clauses, everything I can think of to do and nothing I come across on here or online seems to help me figure it out.
search: (req, res) => {
const { query } = req.query;
Book.findAll({
include: [Author],
where: {
[Op.or]: [
{ title: { [Op.substring]: query } },
]},
})
.then((Book) => res.json(Book))
.catch((err) => {
console.log(err);
res.status(500).json(err);
});
},
Here is the working code. In the where clause, I want to do { first_name: { [Op.substring]: query } }, for example but it isn't accessing the Author table. In the include statement I have tried aliasing and calling it in the where clause, but that throws a aliasing error saying I have already declared an alias (Author) but when I try to use that as { 'Author.first_name' { [Op.substring]: query } }, it returns that there is no Book.Author.first_name.
I am probably missing something simple, so anyone that might be able to help, let me know what I am doing wrong here!
Solved and it was super easy. I was missing the syntax for accessing the separate table which is '$Author.first_name$'.
Related
I'm using Sequelize in Node.js with Apollo-Server and Express.js.
When making queries that go deeper and deeper, GraphQL is looping my models and doing a separate query by ID on each of those.
For example, if I get user(userId) > playthroughs > scores, this will do a lookup for that user (no problem), then a lookup for all the playthroughs with that userId (still no a big deal), but then to get the scores, it loops each playthroughId and does a completely separate query on each. This is ridiculously inefficient and causes my queries to take way longer than they should.
Instead of looping:
SELECT scoreValue
FROM scores
WHERE playthroughId = id
I'd really like to grab the array myself and do that loop like this:
SELECT scoreValue
FROM scores
WHERE playthroughId IN (...ids)
This also happened when I used the reference GraphQL from Facebook last year, so I don't think it's specific to Apollo's implementation.
I'd like to know how I can tweak these queries so they're not taking such a performance hit.
Example resolvers:
const resolvers = {
Query: {
user: (_, values) => User.findOne(formatQuery(values))
.then(getDataValues),
},
Playthrough: {
score: ({ playthroughId }) => Score.findOne(formatQuery({ playthroughId }))
.then(getDataValues),
},
User: {
playthroughs: ({ userId }, { take }) => Playthrough.findAll(formatQuery({ userId, take, order: 'playthroughId DESC' }))
.then(getAllDataValues),
},
}
In addition to graphql, facebook has also released a much lesser known project, dataloader.
What it does it batch several requests in the same tick into one. So your code would be something like
scoreLoader = new Dataloader(keys => {
return Score.findAll({ where: { id: keys } }).then(() => {
//Map the results back so they are in the same order as keys
})
});
score: ({ playthroughId }) => scoreLoader.load(playthroughId).then(getDataValues)
Of course, having a load for each field is going to be tedious. So instead you can use dataloader-sequelize, which wrap all calls to association.get (i.e. Playthrough.getScores()) and calls to findOne / findById to dataloader calls, so several calls are batched in one.
Since you are building a graphql API backed by sequelize, you might also be interested in https://github.com/mickhansen/graphql-sequelize/, which provides sequelize specific helpers for grahpql, and uses dataloader-sequelize below the hood
So, I'm using sequelize with a mysql instance and I have this hierarchy : a task has n images and also n metadata key value pairs.
I want to get all images based on userId column of task, and afterwards get them grouped by 'createdAt' column taking into consideration the day, since a normal groupby will be pointless ( no objects share the same datetime ). I did lots of test to try to group, and I ended up using this query, which gives bad results ( I have like 11 images for a task, and it retrieves 4 ). Honestly, i don't know what I'm doing wrong. Any of you have any idea?
This is the code snippet:
var userId = security.utils.getKeycloakSubject(req);
var where = {
userId: userId
};
db.image.findAll({
include: [{
model: db.task,
include: [{
model: db.metadata,
as: 'metadata'
}],
where: where
}],
group: [db.Sequelize.fn('DAY', db.Sequelize.col('image.createdAt'))]
}).then(function (images) {
return res.json(images);
}, function (error) {
return res.status(500).json(error);
})
I saw your question and also found this: Sequelize grouping by date, disregarding hours/minutes/seconds
It is a question about group the DAY(createdAt), looks similar with yours.
And my solution of GROUP BY DAY() is look like:
item.findAll({
attributes:
[[sequelize.fn('DATE_FORMAT', sequelize.col('TimeStamp'), '%H'), 'dates']],
group: [sequelize.fn('DAY', sequelize.col('TimeStamp'))]
}).
then(function(result){console.log(result)
}).
catch(function(error){}).catch(function(error) {
console.log(error);
});
So the raw SQL likes: SELECT DATE_FORMAT('TimeStamp', '%H') as 'dates' FROM tableName GROUP BY DAY('TimeStamp');
Hope it helps you, or you can show us the SQL you want to use, maybe it is easier to help you too.
Good luck.
I am trying to build a simple application using loopback.io as process of my learning. I have set up the project, created models and apis are working fine.
Now I am trying to create a custom api which can get the data from two different models by making a join query. So i have a two models
stories : id, title, noteId
notes : id , desc
i have stories.js file as
module.exports = function(Stories) {
Stories.list = function(cb) {
// make a join query
};
Stories.remoteMethod(
'list', {
http: {
path: '/list',
verb: 'get'
},
returns: {
arg: 'list',
type: 'array'
}
}
);
};
In general i will make a join in php api but here i am bit confused.Can i pass a raw query to database here or does loopback has some different way of achieving this. Any help would be appreciated.
You don't need to pass sql query. You can query data using PersistedModel find method by using include filter
In order to use include filter you have to create model relation.
For example:
Note relation:
"relations": {
"stories": {
"type": "hasMany",
"model": "Story",
"foreignKey": "noteId"
}
},
Query:
Note.find({include: ['stories']}, function(err, data) { ... });
Please share your experience with partial updating of JSON document.At now I'm storing my JSON documents in MongoDB which looks like the following:
{
id: ObjectId(507f191e810c19729de860ea),
title: 'Sterling Archer',
comments: [
{text: 'Comment text', tags: ['tag1', 'tag2', 'tag3']},
{text: 'Comment test', tags: ['tag2', 'tag5']}
]
}
I need to frequently update my documents by using rfc6902 specification. Now, I do it not optimized way that looks the following (I use nodejs/express/mongoose and fast-json-patch module in this example):
var jsonpatch = require('fast-json-patch');
app.patch('/document/:id', function (req, res, next) {
var document_id = req.params.id;
// req.body.patch: { "op": "add", "path": "/comments/2", "value": {text: 'Comment test3', tags: ['tag4']}" }
var patches = req.body.patch;
// find document
Document.findById(document_id, function (err, document) {
// Applying patches
jsonpatch.apply(document, patches);
// Update whole document in MongoDB
Document.update({_id: document_id}, document, function (err) {
return res.status(200).send();
});
});
});
This is not optimize approach to patch documents due two queries in MongoDB and replacing whole document. So I'm looking for optimized approach and want to try RethinkDB for this task. Can you help me to inspect possibility of atomic document updating by using single query with RethinkDB? Or should I looks for another way of resolving my problem?
Please share your experience with partial updating of JSON document.
You just need one query in RethinkDB. Suppose you want to update the document whose id is 1 with the values {foo: 1, bar: 2}, and increment the field "count", you would do
r.table("data").get(1).update(function(doc) {
return doc.merge({foo: 1, bar:2, count: doc("count").add(1) })
})
While this update requires a unique query, the whole document will be updated.
If you have big documents, you can split them in multiple tables and perform joins later to retrieve the data.
You may be interested in reading this article about data modeling: http://www.rethinkdb.com/docs/data-modeling/
Alright so I have a project in NodeJS where I'm utilizing Sequelize for a MySQL ORM. The thing works fantastically however I'm trying to figure out if there is a way to specify what fields are being returned on a query basis or if there's even a way just to do a .query() somewhere.
For example in our user database there can be ridiculous amounts of records and columns. In this case I need to return three columns only so it would be faster to get just those columns. However, Sequelize just queries the table for everything "*" to fulfill the full object model as much as possible. This is the functionality I'd like to bypass in this particular area of the application.
You have to specify the attributes as a property in the object that you pass to findAll():
Project.findAll({attributes: ['name', 'age']}).on('success', function (projects) {
console.log(projects);
});
How I found this:
The query is first called here: https://github.com/sdepold/sequelize/blob/master/lib/model-definition.js#L131
Then gets constructed here: https://github.com/sdepold/sequelize/blob/master/lib/connectors/mysql/query-generator.js#L56-59
Try this in new version
template.findAll({
where: {
user_id: req.params.user_id
},
attributes: ['id', 'template_name'],
}).then(function (list) {
res.status(200).json(list);
})
Use the arrays in the attribute key. You can do nested arrays for aliases.
Project.findAll({
attributes: ['id', ['name', 'project_name']],
where: {id: req.params.id}
})
.then(function(projects) {
res.json(projects);
})
Will yield:
SELECT id, name AS project_name FROM projects WHERE id = ...;
All Answers are correct but we can also use include and exclude as well
Model.findAll({
attributes: { include: ['id'] }
});
Model.findAll({
attributes: { exclude: ['createdAt'] }
});
Source