Sequelize (or clear SQL) query for selecting rows what includes value in JSON field? - mysql

I have rows in my MYSQL and I Need Sequelize.js query.
Every row have col of type JSON what include this for example:
[
{id: 1234, blah: "test"},
{id: 3210, blah: "test"},
{id: 5897, blah: "test"}
]
I have id and I need to select row what include this id in at least one object in array.

Raw mysql query will be like this:
SELECT * FROM `user` WHERE JSON_CONTAINS(`comments`, '{"id": 1234}');
Simple sequelize example:
const { fn, col, cast } = this.sequelize;
const User = this.sequelize.define('user', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
comments: DataTypes.JSON,
defaultValue: [],
})
User.findAll({
where: fn('JSON_CONTAINS', col('comments'), cast('{"id": 1234}', 'CHAR CHARACTER SET utf8')),
})
.then(users => console.log('result', users.map(u => u.get())))
.catch(err => console.log('error', err));
Cast function is used to unescape double quotes around "id" to avoid wrong query string like this:
SELECT * FROM `user` WHERE JSON_CONTAINS(`comments`, '{\"id\": 1234}');
There is one more dirty mysql query example (don't use it):
SELECT * FROM `user` WHERE `comments` LIKE '%"id": 1234%';

Related

how to group data entries with same id into a single entry?

I am a beginner in MySQL as well as Typeorm. So my query returns data with the same ID like:
[
{
id: "1",
name: "john",
place: "San Francisco"
},
{
id: "1",
name: "john",
place: "Mumbai"
}
]
Now I want data where there is an entry with a unique id, let's say:
[
{
id: "1",
name: "john",
place: ["San Francisco", "Mumbai"]
}
]
can someone help me, how do I groupBy to achieve this result?
I doubt that you can get an array, but you could use group_concat.
https://mariadb.com/kb/en/group_concat/
The query would be something like
SELECT `id`, group_concat(`name`), group_concat(`place`) FROM <table_name> GROUP BY `id`
if the name doesn't need to be concatenated
SELECT `id`, `name`, group_concat(`place`) FROM <table_name> GROUP BY `id`
And then in your code you can split that string in array. Either use ',' which I think it's the default separator or use a custom one like '!#$!'
With MySQL you can use GROUP_CONCAT:
SELECT
id, name, GROUP_CONCAT(place)
FROM
<table_name>
GROUP BY
id
With TypeScript you can use Array.prototype.reduce():
const data = [{id: "1",name: "john",place: "San Francisco"},{id: "1",name: "john",place: "Mumbai"}]
const dataHash = data.reduce((a, { id, name, place }) => {
a[id] = a[id] || { id, name, place: [] }
a[id].place.push(place)
return a
}, {})
const result = Object.values(dataHash)
console.log(result)

How to fetch a serial number from a mysql table using Sequelize in nodeJS?

I have a mysql Query:-
SELECT #a:=#a+1 serial_number,rule FROM pledges,(SELECT #a:= 0) AS a;
This gives me a serial number along with the rule from the table.
How can I do that in Sequelize?
This is the query I wrote in the model which gives me id and rule:-
Pledge.getPledgeList = function(lang_code='EN') {
return this.findAll({
attributes:['id','rule'],
where: {
status:'a',
deleted_at:null
},
include:[
{ association: 'local', where: {lang_code:lang_code} ,required: false},
]
})
}

Sequelize Upsert is Creating instead of Updating

According to the documentation found here it states as follows
upsert(values, [options]) -> Promise.<created>
Insert or update a single row. An update will be executed if a row which matches the supplied values on either the primary key or a unique key is found. Note that the unique index must be defined in your sequelize model and not just in the table. Otherwise you may experience a unique constraint violation, because sequelize fails to identify the row that should be updated.
So my expectation is that upserting using a unique key should replace the existing value. However when my code runs instead of updating the existing database record, it adds a new one. What am I doing wrong?
here is a sample of my model
'use strict'
module.exports = (db, dataTypes) => {
const titanJob = db.define('titanJob', {
titanId: {
type: dataTypes.STRING,
allowNull: false,
unique: true
},
name: {
type: dataTypes.STRING,
allowNull: false
}
}, {
timestamps: true
})
return titanJob
}
and here is an example of my upsert
await asyncForEach(res.data.hits.hits, async es => {
const src = es._source
try {
await titanJob.upsert({
name: src.name,
titanId: src.id,
}, { titanId: src.id })
logger.debug(`[${file}] upsert successful`)
} catch (err) {
logger.warn(`[${file}] failed to save to database`)
logger.warn(`[${file}] ${err}`)
}
})
First you should add a unique index (constraint) to your table. The data you upserting should contain the field set of the unique index (constraint).
It should work. Here is an example using "sequelize": "^5.21.3":
index.ts:
import { Model, DataTypes } from 'sequelize';
import { sequelize } from '../../db';
import assert from 'assert';
class TitanJob extends Model {}
TitanJob.init(
{
titanId: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{ sequelize, modelName: 'titanJob', timestamps: true },
);
(async function test() {
try {
await sequelize.sync({ force: true });
const datas = [
{ titanId: '1', name: 'programmer' },
{ titanId: '2', name: 'teacher' },
];
const jobs = await TitanJob.bulkCreate(datas);
assert.deepEqual(
jobs.map((job) => ({ titanId: job.id, name: job.name })),
datas,
'Should bulk create programmer and teacher datas',
);
const rval = await TitanJob.upsert({ titanId: '1', name: 'driver' }, { returning: true });
assert.equal(rval[0].titanId, '1', 'Should update the row which titanId is "1"');
} catch (error) {
console.log(error);
} finally {
await sequelize.close();
}
})();
Execution results:
{ POSTGRES_HOST: '127.0.0.1',
POSTGRES_PORT: '5430',
POSTGRES_PASSWORD: 'testpass',
POSTGRES_USER: 'testuser',
POSTGRES_DB: 'node-sequelize-examples' }
Executing (default): DROP TABLE IF EXISTS "titanJob" CASCADE;
Executing (default): DROP TABLE IF EXISTS "titanJob" CASCADE;
Executing (default): CREATE TABLE IF NOT EXISTS "titanJob" ("id" SERIAL , "titanId" VARCHAR(255) NOT NULL UNIQUE, "name" VARCHAR(255) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'titanJob' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): INSERT INTO "titanJob" ("id","titanId","name","createdAt","updatedAt") VALUES (DEFAULT,'1','programmer','2020-02-14 08:09:45.506 +00:00','2020-02-14 08:09:45.506 +00:00'),(DEFAULT,'2','teacher','2020-02-14 08:09:45.506 +00:00','2020-02-14 08:09:45.506 +00:00') RETURNING *;
Executing (default): CREATE OR REPLACE FUNCTION pg_temp.sequelize_upsert(OUT created boolean, OUT primary_key text) AS $func$ BEGIN INSERT INTO "titanJob" ("titanId","name","createdAt","updatedAt") VALUES ('1','driver','2020-02-14 08:09:45.524 +00:00','2020-02-14 08:09:45.524 +00:00') RETURNING "id" INTO primary_key; created := true; EXCEPTION WHEN unique_violation THEN UPDATE "titanJob" SET "titanId"='1',"name"='driver',"updatedAt"='2020-02-14 08:09:45.524 +00:00' WHERE ("id" IS NULL OR "titanId" = '1') RETURNING "id" INTO primary_key; created := false; END; $func$ LANGUAGE plpgsql; SELECT * FROM pg_temp.sequelize_upsert();
Executing (default): SELECT "id", "titanId", "name", "createdAt", "updatedAt" FROM "titanJob" AS "titanJob" WHERE "titanJob"."id" = '1';
No assertion fails. It works as expected. Check the data rows in the database:
node-sequelize-examples=# select * from "titanJob";
id | titanId | name | createdAt | updatedAt
----+---------+---------+----------------------------+----------------------------
2 | 2 | teacher | 2020-02-14 08:09:45.506+00 | 2020-02-14 08:09:45.506+00
1 | 1 | driver | 2020-02-14 08:09:45.506+00 | 2020-02-14 08:09:45.524+00
(2 rows)
source code: https://github.com/mrdulin/node-sequelize-examples/tree/master/src/examples/stackoverflow/59686743

Sequelize attribute with alias seems undefined

I'm trying to get id of AttributeValue model with an alias of attribute_value_id as below but attribute_value_id seems undefined in otherOptions constant.
const otherOptions = await AttributeValue.findAll({
where: {
id: {
[Op.in]: attributeValueIds
} },
attributes: [
['id', 'attribute_value_id'],
'attribute_id',
'value'
]
})
I'm fallowing sequlize documantation about querying and the below query is built by sequelize but there is no alias attribute in the resulting object.
SELECT `id` AS `attribute_value_id`, `attribute_id`, `value` FROM `attribute_values` AS `attribute_value` WHERE `attribute_value`.`id` IN (1, 1, 1, 1, 19);
You can get it with otherOptions[0].getDataValue('attribute_value_id')

Sequelize generates a not working SQL when use order option

when I use order option in #findAll it generates SQL:
SELECT
`id`, `first_name` AS `firstName`,
`last_name` AS `lastName` FROM `customers` AS `Customer`
ORDER BY `Customer`.`firstName` DESC;
but this SQL causes error:
ER_BAD_FIELD_ERROR: Unknown column 'Customer.firstName' in 'order clause'
Code example:
var Customer = sequelize.define("Customer", {
id: {
type: Sequelize.INTEGER({unsigned: true}),
primaryKey: true
},
firstName: {
type: Sequelize.STRING(32),
field: "first_name"
},
lastName: {
type: Sequelize.STRING(32),
field: "last_name"
}
}, {
name: {
singular: "customer",
plural: "customers"
},
tableName: "customers",
timestamps: false,
underscored: true
});
Customer.findAll({
order: [["firstName", "DESC"]]
}).then(function(list) {
console.log(list);
}).catch(function(err) {
console.log(err);
});
Mysql: 5.6.20
Sequelize: 3.14.2
Are there any solutions of this issue?
Use order: [["first_name", "DESC"]]; the ORDER BY looks at column names, not your SELECT alias firstName.