Sequelize - use sequelize functions to convert iLike queries to be MySQL friendly - mysql

I generally use PostgreSQL, and this is my first time using MySQL with Sequelize. I learned that MySQL does not support iLike, and I'm trying to convert my iLike to be MySQL friendly (so-to-speak).
I managed to successfully accomplish the below with one of my queries.
where: {
[Op.or]: [
{
name: {
[Op.iLike]: `%${searchTerm || ''}`,
},
},
{
description: {
[Op.iLike]: `%${searchTerm || ''}`,
},
},
],
},
**Converted To**
where: {
[Op.or]: [
Sequelize.where(Sequelize.fn('lower', Sequelize.col('name')), {
[Op.like]: `%${searchTerm || ''}`,
}),
Sequelize.where(Sequelize.fn('lower', Sequelize.col('description')), {
[Op.like]: `%${searchTerm || ''}`,
}),
],
},
What I'm struggling with is converting the following. I'd appreciate any input I could get.
ingredients = ['%salt%', '%pepper%'];
where: {
ingredient: {
[Op.iLike]: {
[Op.any]: ingredients,
},
},
},
SQL - WHERE "Ingredient"."ingredient" ILIKE ANY (ARRAY['%salt%','%pepper%']);
**Attempt to Convert**
where: Sequelize.where(
Sequelize.fn('lower', Sequelize.col('ingredient')),
{
[Op.like]: {
[Op.any]: ingredients,
},
}
),
SQL - WHERE lower(`ingredient`) LIKE ANY ('%salt%', '%pepper%') It seems like ingredients is not passed as an array compared to the above code.

This way worked to me, maybe this way help you.
where: {
title: {
[Op.like]: ['%' + title + '%'],
},
},

Related

Multiple searches in same column MySQL Sequelize,

I am making a search in a database which have a table productDetails.
I want a query something like this in sequelize,
SELECT * FROM productDetails WHERE title LIKE '%search1%' OR title LIKE '%search2%'
currently using
const pd= await ProductDetail.findAll({
where: {
[Op.or]: {
title: { [Op.like]: `%search1%` },
title: { [Op.like]: `%search2%` },
},
},
Just indicate all conditions as an array as a value of Op.or like this:
where: {
[Op.or]: [
{ title: { [Op.like]: `%search1%` } },
{ title: { [Op.like]: `%search2%` } },
],
},
See examples here

is it possible to user `where` and `count` to return the count for a specified field?

I am trying to use Prisma to return a count for a boolean field where it equals 'true'.
To give some context, on the frontend I am trying to calculate the workouts that have been completed by a user as a percentage, so ideally I would like prisma to return a count for the total workouts (which I have successfully done) and the count for the userWorkouts where 'isCompleted' equal true (which I am unable to achieve), currently the count is returning all userWorkouts not just the completed ones.
Here is my current Prisma Query:
const response = await prisma.user.findUnique({
where: {
id: 1,
},
select: {
id: true,
programs: {
select: {
program: {
select: {
name: true,
blocks: {
select: {
id: true,
name: true,
week: {
select: {
id: true,
number: true,
workouts: {
select: {
userWorkouts: {
where: {
isCompleted: true,
},
},
_count: {
select: {
userWorkouts: true,
},
},
},
},
_count: {
select: {
workouts: true,
},
},
},
},
},
},
},
},
},
},
},
});
res.json(response);
};
Is this possible to achieve using Primsa? Or should I just return all userWorkouts and filter for isCompleted: true on the frontend?
I have spoken with the Prisma team and this can't be achieved yet, although there is a feature request open for it.
https://github.com/prisma/prisma/issues/8413
If you would like to help get this feature added please add you +1 to the feature request.

Sequelize - Check the manual that corresponds to your MySQL server version for the right syntax to use

I'm trying to deploy a web-app and the database is a MySQL database that I'm connected to via Sequelize.
Generally I use PostgreSQL, but the web hosting uses MySQL with access via phpMyAdmin.
I have a api & db query that works locally and in Heroku (I assume because both uses PostgreSQL), but not with this web hosting probably because they use MySQL.
When I make the API call, I get this error below.
"sqlMessage": "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ILIKE '%%' OR `Cocktail`.`description` ILIKE '%')' at line 1",
"sql": "SELECT count(*) AS `count` FROM `Cocktails` AS `Cocktail` WHERE (`Cocktail`.`name` ILIKE '%%' OR `Cocktail`.`description` ILIKE '%');"
Below is the query I'm making. It seems like this only works if I remove the iLike, the modulus operator %, and Op.or; which basically strips the search functionality.
I initially have mysql2 installed, but I also added mysql, hoping it would somehow resolve this; but it didn't.
How would I resolve this issue? Sorry if a trivial issue.. I'm literally at a loss on how to proceed..
const paginator = async (req, res, limit) => {
const { searchIngredients, searchTerm } = req.query;
const ingredients =
searchIngredients &&
searchIngredients.split(',').map((ingredient) => `%${ingredient}%`);
const { page } = req.query;
if (!searchIngredients) {
await Cocktail.findAndCountAll({
where: {
[Op.or]: [
{
name: {
[Op.iLike]: `%${searchTerm || ''}%`,
},
},
{
description: {
[Op.iLike]: `%${searchTerm || ''}%`,
},
},
],
},
}).then(async (data) => {
const offset = limit * (page === undefined ? 1 - 1 : page - 1);
await Cocktail.findAll({
limit,
offset,
order: [[`createdAt`, 'DESC']],
where: {
[Op.or]: [
{
name: {
[Op.iLike]: `%${searchTerm || ''}%`,
},
},
{
description: {
[Op.iLike]: `%${searchTerm || ''}%`,
},
},
],
},
include: [
{
model: Ingredient,
as: 'ingredients',
attributes: {
exclude: ['createdAt', 'updatedAt', 'Cocktail_Ingredient'],
},
},
{
model: Step,
as: 'steps',
attributes: {
exclude: ['createdAt', 'updatedAt', 'Cocktail_Step'],
},
},
],
})
.then((cocktails) => {
return res.status(200).send({ count: data.count, limit, cocktails });
})
.catch(() => res.status(500).send({ message: 'error here too' }));
});
EDIT:
Converted my iLike queries to be MySQL friendly using the below:
Sequelize.where(Sequelize.fn('lower', Sequelize.col('name')), {
[Op.like]: `%${searchTerm || ''}%`,
}),
In substitution of ILIKE, you can use LOWER() with LIKE to ignore case sensitivity.
Example: SELECT * FROM products WHERE LOWER(productName) LIKE LOWER('%my search terms%');
There is only LIKE and no ILIKE.
Case sensitivity is covered differently.

Stuck to figure out how to access a certain field to update

I have this JSON FILE
{
"_id": "GgCRguT8Ky8e4zxqF",
"services": {
"emails": [
{
"address": "Abunae#naa.com",
"verified": false,
"verifiedMail": "Toto#hotmail.com"
}
],
"profile": {
"name": "Janis"
},
"pushIds": []
}
I want to update my verifiedMail field but couldn't figure out how to do it in Meteor, it's always returning me an error
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'emails.verifiedEmail': emailRefactor} }, { upsert: true })
Couldn't figure out how to access the emails.verifiedEmail field
Tried this exemlpe worked like a charm
let VerifiedEmail = "Exemple1"
await Meteor.users.update({ _id: user._id }, { $set: { 'profile.name': emailRefactor} }, { upsert: true })
but couldn't figure out how to access emails.verifiedEmail .
Could you please help me ?
Emails is an array, while profile is an object. You have to access the first object of the email array instead
This updates the exact email address from emails
Meteor.users.update({
"emails.address": emailRefactor
}, {
$set: {
"emails.$.verified": true
}
});
Or update the first element
Meteor.users.update({
_id: user._id,
"emails.address": emailRefactor
}, {
$set: {
"emails.0.verified": true
}
});
You're trying to set verifiedEmail while the actual field is verifiedMail.

How to name the fields of related model in filter object?

In my project, two models "UserProfile" and "UserAccount" are with a relation that the former "has one" the later. The .json files look like:
userprofile.json:
{
"name": "Userprofile",
"base": "PersistedModel",
//...
"properties": {
"userid": {
"type": "Number"
},
"phoneno": {
"type": "String"
}
},
//...
"relations": {
"userAccounts": {
"type": "hasOne",
"model": "UserAccount",
"foreignKey": "id",
"options": {
"validate": true,
"forceId": false
}
}
}
}
useraccount.json:
{
"name": "UserAccount",
"base": "User",
"idInjection": true,
"restrictResetPasswordTokenScope": true,
"emailVerificationRequired": true,
"properties": {},
"relations": {}
//...
}
The models have corresponding tables in a MariaDB.
Now the quest is to "GET" UserProfile with a keyword that match any one field of UserProfile.phoneno or UserAccount.email (yes, the key point is or). In SQL terms, that is:
SELECT * FROM UserProfile INNER JOIN UserAccount
ON UserProfile.userid = UserAccount.id
WHERE UserProfile.phoneno LIKE '%keyword%'
OR UserAccount.email LIKE '%keyword%'
It should be a common and simple query in SQL but seems become difficult in LookBack. My implementation is:
userprofile.js:
'use strict';
module.exports = function (Userprofile) {
Userprofile.remoteMethod('profileByEmailOrPhoneno', {
description: '...',
http: {path:'/profileByEmailOrPhoneno', verb: 'get'},
accepts: {arg: 'keyword', type: 'string', required: true},
returns: {arg: 'profile', type: 'array' }
})
Userprofile.profileByEmailOrPhoneno = function (keyword, cb) {
let filter = {
fields: {userid: true, nickname: true, phoneno: true},
include: {
relation: 'userAccounts',
scope: {
fields: {username: true, email: true}
}
},
where: {or: [
{phoneno: {like: `%${keyword}%`}},
{'userAccount.email': {like: `%${keyword}%`}}
]}
}
Userprofile.find(
filter,
function (err, records) {
if (err) console.log(err)
else cb(null, records)
}
)
}
};
I tested it on StrongLoop API Explorer and it always returned the whole records in UserProfile no matter whatever keyword. If the criterium
{'userAccount.email': {like: `%${keyword}%`}}
was removed the codes worked correctly. I think this criterium is wrong so LookBack ignores it and evaluate the where section to be true. I modified it to:
{'email': {like: `%${keyword}%`}}
and it was still wrong.
So, I wonder how to correctly name the relation model's field (eg.'email'), or, how to write the correct filter. Anybody can give some help? I'll very appreciated for it. ^^
The include statement in Loopback is a left-outer-join, so the query will always return ALL the Userprofile records. Some will have userAccounts with an array of values, other's wont. You need to further filter the Userprofile records.
Also, you need to put he userAccoutns filter in the scope statement of your filter:
Userprofile.profileByEmailOrPhoneno = function (keyword, cb) {
let filter = {
fields: {userid: true, nickname: true, phoneno: true},
include: {
relation: 'userAccounts',
scope: {
fields: {username: true, email: true},
where: {'email':{'like': `%${keyword}%`}} // userAccounts filter goes here
}
},
where: {phoneno: {like: `%${keyword}%`}}
}
Userprofile.find(filter, function (err, records) {
if (err) console.log(err)
else {
// filter the records for those that have userAccounts
var filteredResults = records.filter(record =>
record.userAccounts &&
Array.isArray(record.userAccounts()) &&
record.userAccounts().length);
cb(null, filteredResults)
}
})
}