Query concatenated fields in Sequelize - mysql

I am implementing an application using Sequelize to handle communication with the database.
I have a number of users in the database and I want to implement some search-functionality which allows users to find other users based on their full name.
A user has (amongst other attributes) a firstName and a lastName in its model.
A user who is looking for another user can search for "John Doe", where John is the first name and Doe is the last name.
Unfortunately, the first name and last name are stored in separate fields in my model. Because of this, I need to concatenate the firstName and lastName field in the "where"-clause as I tried below.
In the where-clause I am just concatenating firstName and lastName and then check whether that is "like" the full name that is passed as the argument name. I think the intention of this code below is clear. It is however not working (error says it doesn't expect the '(' after concat so this syntax isn't allowed). Is there an easy trick to do this or should I write my own query using sequelize.query?
var findUserByName = function(name) {
return models.User.find({where: {concat(firstName,' ',lastName): like: name}});
}

This you can try in where clause
where: {
$and: [
Sequelize.where(
Sequelize.fn('concat', Sequelize.col('firstName'), ' ', Sequelize.col('lastName')), {
like: '%'+name+'%'
}
)
]
}

You could do that like this:
User.find({
where: {
$or: [
name: {
$like: ['%', firstName, '%'].join('')
},
lastName: {
$like: ['%', lastName, '%'].join('')
}
]
}).success(function (user) {});

Related

Query across two tables in sequelize

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$'.

Get data from Firebase db, comes back without key value

I am retrieving data from Firebase db using REST call. So i have a data looking like this
user
{
123456:
{
Email: "test#hotmail.com",
Password: "John Doe"
}
654321:
{
Email: "test2#hotmail.com",
Password: "Jane Doe"
}
}
The query I used is
firebaseurl/user.json?orderBy="Email"&equalTo="test#hotmail.com"
Is it possible if I query without the key value 123456 being retrieve? Because whenever I try to use the data, for instance console.log(user.Email), I am getting undefined. But when I do it likeconsole.log(user[123456].Email) then will I get the result I desire.
May I know if there's a way to do it? Or a way to access to the child element without user[keyvalue].
As it is expected only single record in return you can use:
const user = obj[Object.keys(rslt)[0]]
Or you can loop over the keys
const usersArr = Object.keys(rslt).map(usr => {
... manipulate usr
return usr
})

Sequelize: Concat fields in WHERE LIKE clause

I am using the sequelize ORM for a node.js project I am working on. One query I have, I need to perform a like operation on the concatenated result of multiple columns.
For instance, something like the following:
SELECT * FROM People WHERE (CONCAT(firstname, ' ', lastname)) LIKE '%John Do%'.
I am using the following syntax and would like to know if this is possible without having to resort to using RAW queries (which is nowhere else in my solution).
var criteria = {
include: [
occupation
],
where: {
is_active: 1
},
nest: false
};
db.people.findAll(criteria, {}).then(function(people) {
success(people);
}).catch(function(err) {
error(err);
});
Any ideas?
You'll need something like this
var criteria = {
where: Sequelize.where(Sequelize.fn("concat", Sequelize.col("firstname"), Sequelize.col("lastname")), {
like: '%John Do%'
})
}
Note: untested
Original source
Inspired by #code-jaff but you need to concatenate a space string in between first and last names to make this work correctly. Otherwise it would only return for 'JohnDoe' and not for 'John Doe'. Here's the code.
Sequelize.where(Sequelize.fn('concat', Sequelize.col('firstName'), ' ', Sequelize.col('lastName')), {
like: '% John Doe %'
})
To provide some context for people who might not understand where this would fit into your query, this is an example of the above code in a where or statement. req.body.query being the variable search term that you're POSTing.
Users.findAll({
where: {
$or: [
Sequelize.where(Sequelize.fn('concat', Sequelize.col('firstName'), ' ', Sequelize.col('lastName')), {
like: '%' + req.body.query + '%'
}),
{ email: { $like: '%' + req.body.query + '%' } },
{ companyName: { $like: '%' + req.body.query + '%' } }
]
}
})
Update for Sequelize 4.0
String based operators ($like and $or in the above example) have been deprecated in favour of symbol based operators. It's a good thing for security
See: http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
These operators would be replaced with [Sequelize.Op.like] and [Sequelize.Op.or]. There are also other ways to configure it in your sequelize options highlighted in their documentation
I was able to achieve this with the new sequelize version 5.21.13 based on #yjimk answer.
Users.findAll({
where: {
[sequelize.Op.or]:{
namesQuery: sequelize.where(
sequelize.fn(
"concat",
sequelize.col("firstName"),
" ",
sequelize.col("lastName")
),
{
[sequelize.Op.like]: `%${req.body.query}%`,
}
),
email: {[sequelize.Op.like]: `%${req.body.query}%`},
companyName: {[sequelize.Op.like]: `%${req.body.query}%`},
}
})
db.models.users.findOne({
where: {
[db.sequelize.Op.and]: [
db.sequelize.where(
db.sequelize.fn('CONCAT', db.sequelize.col('first_name'), ' ', db.sequelize.col('last_name')),
{ like: `%${name}%` },
),
{ status: 'ACTIVE' },
]
}
});

Sequelize include (how to structure query)?

I have a query I'm trying to perform based on a one to many relationship.
As an example there is a model called Users and one called Projects.
Users hasMany Projects
Projects have many types which are stored in a type (enum) column. There are 4 different types that potentially a user may have that I want to load. The catch is I want to include the most recent project record (createdAt column) for all networks that potentially will be there. I have not found a way to structure the query for it to work as an include. I have however found a way to do a raw query which does what I want.
I am looking for a way without having to do a raw query. By doing the raw query I have to map the returned results to users I've returned from the other method, or I have to do a simple include and then trim off all the results that are not the most recent. The latter is fine, but I see this getting slower as a user will have many projects and it will keep growing steadily.
This allow serialize a json for anywhere action about a model. Read it, very well
sequelize-virtual-fields
// define models
var Person = sequelize.define('Person', { name: Sequelize.STRING });
var Task = sequelize.define('Task', {
name: Sequelize.STRING,
nameWithPerson: {
type: Sequelize.VIRTUAL,
get: function() { return this.name + ' (' + this.Person.name + ')' }
attributes: [ 'name' ],
include: [ { model: Person, attributes: [ 'name' ] } ],
order: [ ['name'], [ Person, 'name' ] ]
}
});
// define associations
Task.belongsTo(Person);
Person.hasMany(Task);
// activate virtual fields functionality
sequelize.initVirtualFields();

Convert Mysql query to Mongodb find()

I have a mongodb collection structured like this:
{ "client" : "CLIENTIDHERE", "amount" : 90, "invoice": "SOMEIDHERE", "date" : ISODate("2014-07-09T11:13:49.273Z") }
And need to select somehow all the payments made from a client.
In mysql I would do something like this, but with mongodb I have really no clue.
SELECT SUM(amount) payments,client FROM invoices GROUP BY client;
What I tried:
db.invoices.find({
$group: {
amount: {$sum: "$amount"}
}
}, {
$project:{
amount: "$amount",
client: "$client"
}
})
But it didn't work. What did I do wrong?
EDIT: I get the following error:
error: { "$err" : "Unsupported projection option: amount", "code" : 13097 }
Your $group step doesn't define what to GROUP BY. MongoDB's equivalent for the SQL GROUP BY is the _id field of the $group statement which is missing from your query. Also, when you want to use aggregation, you need to use .aggregate( instead of .find(.
db.invoices.aggregate({
$group: {
_id: "$client",
amount: { $sum: "$amount"}
}
});
This will write the client into the _id field. When you would rather want to have the field named client, you can fix that in the $project step, but otherwise the projection is not required in this case.