Find row by UUID stored as binary in SequelizeJS - mysql

I have a Sequelize object called Org which represents a row in the organisations table stored in MySQL. This table has a UUID primary key(id) stored as a 16 byte varbinary. If I have the UUID of an object (bfaf1440-3086-11e3-b965-22000af9141e) as a string in my JavaScript code, what is the right way to pass it as a parameter in the where clause in Sequelize?
Following are the options I've tried
Model: (for an existing MySQL table)
var uuid = require('node-uuid');
module.exports = function(sequelize, Sequelize) {
return sequelize.define('Org', {
id: {
type: Sequelize.BLOB, //changing this to Sequelize.UUID does not make any difference
primaryKey: true,
get: function() {
if (this.getDataValue('id')) {
return uuid.unparse(this.getDataValue('id'));
}
}
},
name: Sequelize.STRING,
}, {
tableName: 'organisation',
timestamps: false,
}
});
};
Option 1: Pass UUID as byte buffer using node-uuid
Org.find({
where: {
id: uuid.parse(orgId)
}
}).then(function(org) {
success(org);
}).catch(function(err) {
next(err);
});
Executing (default): SELECT `id`, `name` FROM `Organisation` AS `Org`
WHERE `Org`.`id` IN (191,175,20,64,48,134,17,227,185,101,34,0,10,249,20,30);
Sequelize treats the byte buffer as multiple values and so I get multiple matches and the top most record (not the one that has the right UUID) gets returned.
Option 2: Write a raw SQL query and pass the UUID as a HEX value
sequelize.query('SELECT * from organisation where id = x:id', Org, {plain: true}, {
id: orgId.replace(/-/g, '')
}).then(function(org) {
success(org);
}).catch(function(err) {
next(err);
});
Executing (default): SELECT * from organisation
where id = x'bfaf1440308611e3b96522000af9141e'
I get the correct record, but this approach is not really useful as I have more complex relationships in the DB and writing too many queries by hand beats the purpose of the ORM.
I'm using Sequelize 2.0.0-rc3.

Solved it by supplying a fixed size empty Buffer object to uuid.parse().
Got it working initially using ByteBuffer, but then realised that the same can be achieved using uuid.parse()
Org.find({
where: {
id: uuid.parse(orgId, new Buffer(16))
}
}).then(function(org) {
console.log('Something happened');
console.log(org);
}).catch(function(err) {
console.log(err);
});
Executing (default): SELECT `id`, `name` FROM `Organisation` AS `Org`
WHERE `Org`.`id`=X'bfaf1440308611e3b96522000af9141e';

If the accepted answer didn't work for you, here's what worked for me.
Note: My objective is to find an instance of an event based on a column which is not the primary key.
// guard clause
if (!uuid.validate(uuid_code))
return
const _event = await event.findOne({ where: { uuid_secret: uuid_code } })
// yet another guard clause
if (_event === null)
return
// your code here

Related

How to update array of objects with same value in reference_id (FK Column) in Sequelize JS?

I have an array of objects which somewhat looks like this:
[
{
id: '5b29c08b-597c-460c-a3c7-ac8852b7a5dc',
option_text: 'njnj',
answer: false
},
{
id: '8ff5bda6-9335-495c-9c72-15ef258b899b',
option_text: 'jnjn',
answer: true
}
]
Here the answer column is inter-related like if any of the object's answer is set to true the other will come as false from frontend. So I've to update all the row associated with the referenced id.
What problem am facing is that the update query is not running but it is going inside the then block of the code instead of throwing error. Below is my code for the same:
// UPDATE Option
exports.updateOption = (req, res, next) => {
try {
console.log(req.body);
db.Option.update(req.body, {
where: { question_id: req.params.id }
}).then(() => {
console.log('A');
return res.status(200).send(errors.UPDATED_SUCESSFULLY);
}).catch(err => {
console.log('B');
return res.status(204).send(errors.INTERNAL_SERVER);
});
} catch(err) {
console.log('C');
return res.status(204).send(errors.INTERNAL_SERVER);
}
};
Sample Table Data for the same:
What I am thinking is that firstly to answer column false for all the rows associated with the same question_id and then update the particular row which has answer set to true.
But is this a good approach or anyone can suggest me some better solution ?
You should execute all updates in the same transaction (to avoid inconsistencies in DB):
sequelize.transaction(async transaction => {
const options = req.body;
for (const option of options) {
await db.Option.update(option, {
where: { question_id: req.params.id },
transaction
});
}
}).then(...

sequelize find and update a record without a model

I would like to update a database record using sequelize.js and mysql2
I do not have access to the models folder or either they have not been made so is there a way to update can't find solution all solutions are that i checked are with model name dot update
var Book = db.define(‘books’, {
title: {
type: Sequelize.STRING
},
pages: {
type: Sequelize.INTEGER
}
})
Book.update(
{title: req.body.title},
{returning: true, where: {id: req.params.bookId} }
)
.then(function([ rowsUpdate, [updatedBook] ]) {
res.json(updatedBook)
})
.catch(e => console.log(e));
I would like your expert solution on that please
For latest documentation based on version refer to Sequelize ORM.
Update - v6
You can use both query model(preferred) as well as raw queries to achieve this.
// Using query model(User)
await User.update({ y: 42 }, {
where: {
x: 12
}
});
// Using raw query
const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
// Results will be an empty array and metadata will contain the number of affected rows.
Old - v3
sequelize.query("UPDATE users SET y = 42 WHERE x = 12").spread(function(results, metadata) {
// Results will be an empty array and metadata will contain the number of affected rows.
})
Reference: Sequelize Raw Query

What is the idiomatic, performant way to resolve related objects?

How do you write query resolvers in GraphQL that perform well against a relational database?
Using the example schema from this tutorial, let's say I have a simple database with users and stories. Users can author multiple stories but stories only have one user as their author (for simplicity).
When querying for a user, one might also want to get a list of all stories authored by that user. One possible definition a GraphQL query to handle that (stolen from the above linked tutorial):
const Query = new GraphQLObjectType({
name: 'Query',
fields: () => ({
user: {
type: User,
args: {
id: {
type: new GraphQLNonNull(GraphQLID)
}
},
resolve(parent, {id}, {db}) {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id});
}
},
})
});
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {db}) {
return db.all(`
SELECT * FROM Story WHERE author = $user
`, {$user: parent.id});
}
}
})
});
This will work as expected; if I query a specific user, I'll be able to get that user's stories as well if needed. However, this does not perform ideally. It requires two trips to the database, when a single query with a JOIN would have sufficed. The problem is amplified if I query multiple users -- every additional user will result in an additional database query. The problem gets worse exponentially the deeper I traverse my object relationships.
Has this problem been solved? Is there a way to write a query resolver that won't result in inefficient SQL queries being generated?
There are two approaches to this kind of problem.
One approach, that is used by Facebook, is to enqueue requests happening in one tick and combine them together before sending. This way instead of doing a request for each user, you can do one request to retrieve information about several users. Dan Schafer wrote a good comment explaining this approach. Facebook released Dataloader, which is an example implementation of this technique.
// Pass this to graphql-js context
const storyLoader = new DataLoader((authorIds) => {
return db.all(
`SELECT * FROM Story WHERE author IN (${authorIds.join(',')})`
).then((rows) => {
// Order rows so they match orde of authorIds
const result = {};
for (const row of rows) {
const existing = result[row.author] || [];
existing.push(row);
result[row.author] = existing;
}
const array = [];
for (const author of authorIds) {
array.push(result[author] || []);
}
return array;
});
});
// Then use dataloader in your type
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {rootValue: {storyLoader}}) {
return storyLoader.load(parent.id);
}
}
})
});
While this doesn't resolve to efficient SQL, it still might be good enough for many use cases and will make stuff run faster. It's also a good approach for non-relational databases that don't allow JOINs.
Another approach is to use the information about requested fields in the resolve function to use JOIN when it is relevant. Resolve context has fieldASTs field which has parsed AST of the currently resolved query part. By looking through the children of that AST (selectionSet), we can predict whether we need a join. A very simplified and clunky example:
const User = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: {
type: GraphQLID
},
name: {
type: GraphQLString
},
stories: {
type: new GraphQLList(Story),
resolve(parent, args, {rootValue: {storyLoader}}) {
// if stories were pre-fetched use that
if (parent.stories) {
return parent.stories;
} else {
// otherwise request them normally
return db.all(`
SELECT * FROM Story WHERE author = $user
`, {$user: parent.id});
}
}
}
})
});
const Query = new GraphQLObjectType({
name: 'Query',
fields: () => ({
user: {
type: User,
args: {
id: {
type: new GraphQLNonNull(GraphQLID)
}
},
resolve(parent, {id}, {rootValue: {db}, fieldASTs}) {
// find names of all child fields
const childFields = fieldASTs[0].selectionSet.selections.map(
(set) => set.name.value
);
if (childFields.includes('stories')) {
// use join to optimize
return db.all(`
SELECT * FROM User INNER JOIN Story ON User.id = Story.author WHERE User.id = $id
`, {$id: id}).then((rows) => {
if (rows.length > 0) {
return {
id: rows[0].author,
name: rows[0].name,
stories: rows
};
} else {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id}
);
}
});
} else {
return db.get(`
SELECT * FROM User WHERE id = $id
`, {$id: id}
);
}
}
},
})
});
Note that this could have problem with, eg, fragments. However one can handle them too, it's just a matter of inspecting the selection set in more detail.
There is currently a PR in graphql-js repository, which will allow writing more complex logic for query optimization, by providing a 'resolve plan' in the context.

How to get json object with multiple rows

My goal is to write the Fields from the JSON object below into a MySQL Database.
var dataRow = {
Table: "promosbudget",
Fields:[{
ManagerID: "Jose",
UserID: "ife",
Budget: "50000",
Year: "2015"
},
{
ManagerID: "Jose",
UserID: "fgs",
Budget: "50000",
Year: "2015"
},
{
ManagerID : "Jose",
UserID : "brz",
Budget : "50000",
Year : "2015"
}]
};
I'm using this command to receive and write the data:
app.post('/paramsjson', jsonParser, function(req, res) {
conMySQL.query('INSERT INTO ' + req.body.Table + ' SET ?',req.body.Fields,
function(err,result) {
console.log(result);
}
);
});
The issue is that I can only write the first JSON row, the other 2 rows are omitted.
I'd like to ask if there is a recommended method to do that right when I need to export a large JSON object (100.000 row), is it necessary to make a loop and read each row sequentially?
Thanks in advance for your help!
Try to go step by step.
Don't INSERT right into the database,
first console.log the outputs, look into the results, try to INSERT directly from the code. Then try to combine it all.
Anyway - I highly recommend using knex for any DB operation.
Take this sample codes for testing:
app.post('/paramsjson', jsonParser, function(req, res) {
console.log(req.body);
var table = req.body.Table;
var fields = req.body.Fields;
//If you want to use knex:
knex( table ).insert( fields )
.then(function (result) {
console.log(result)
})
.catch(function (err) {
console.log(err)
})
});

Create or Update Sequelize

I'm using Sequelize in my Nodejs project and I found a problem that I'm having a hard time to solve.
Basically I have a cron that gets an array of objects from a server than inserts it on my database as a object ( for this case, cartoons ). But if I already have one of the objects, I have to update it.
Basically I have a array of objects and a could use the BulkCreate() method. But as the Cron starts again, it doesn't solve it so I was needing some sort of update with an upsert true flag. And the main issue: I must have a callback that fires just once after all these creates or updates. Does anyone have an idea of how can I do that? Iterate over an array of object.. creating or updating it and then getting a single callback after?
Thanks for the attention
From the docs, you don't need to query where to perform the update once you have the object. Also, the use of promise should simplify callbacks:
Implementation
function upsert(values, condition) {
return Model
.findOne({ where: condition })
.then(function(obj) {
// update
if(obj)
return obj.update(values);
// insert
return Model.create(values);
})
}
Usage
upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
res.status(200).send({success: true});
});
Note
This operation is not atomic.
Creates 2 network calls.
which means it is advisable to re-think the approach and probably just update values in one network call and either:
Look at the value returned (i.e. rows_affected) and decide what to do.
Return success if update operation succeeds. This is because whether the resource exists is not within this service's responsibility.
You can use upsert
It's way easier.
Implementation details:
MySQL - Implemented as a single query INSERT values ON DUPLICATE KEY UPDATE values
PostgreSQL - Implemented as a temporary function with exception handling: INSERT EXCEPTION WHEN unique_constraint UPDATE
SQLite - Implemented as two queries INSERT; UPDATE. This means that the update is executed regardless of whether the row already
existed or not
MSSQL - Implemented as a single query using MERGE and WHEN (NOT) MATCHED THEN Note that SQLite returns undefined for created, no
matter if the row was created or updated. This is because SQLite
always runs INSERT OR IGNORE + UPDATE, in a single query, so there
is no way to know whether the row was inserted or not.
Update 07/2019 now with async/await
async function updateOrCreate (model, where, newItem) {
// First try to find the record
const foundItem = await model.findOne({where});
if (!foundItem) {
// Item not found, create a new one
const item = await model.create(newItem)
return {item, created: true};
}
// Found an item, update it
const item = await model.update(newItem, {where});
return {item, created: false};
}
I liked the idea of Ataik, but made it a little shorter:
function updateOrCreate (model, where, newItem) {
// First try to find the record
return model
.findOne({where: where})
.then(function (foundItem) {
if (!foundItem) {
// Item not found, create a new one
return model
.create(newItem)
.then(function (item) { return {item: item, created: true}; })
}
// Found an item, update it
return model
.update(newItem, {where: where})
.then(function (item) { return {item: item, created: false} }) ;
}
}
Usage:
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(function(result) {
result.item; // the model
result.created; // bool, if a new item was created.
});
Optional: add error handling here, but I strongly recommend to chain all promises of one request and have one error handler at the end.
updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'})
.then(..)
.catch(function(err){});
This might be an old question, but this is what I did:
var updateOrCreate = function (model, where, newItem, onCreate, onUpdate, onError) {
// First try to find the record
model.findOne({where: where}).then(function (foundItem) {
if (!foundItem) {
// Item not found, create a new one
model.create(newItem)
.then(onCreate)
.catch(onError);
} else {
// Found an item, update it
model.update(newItem, {where: where})
.then(onUpdate)
.catch(onError);
;
}
}).catch(onError);
}
updateOrCreate(
models.NewsItem, {title: 'sometitle1'}, {title: 'sometitle'},
function () {
console.log('created');
},
function () {
console.log('updated');
},
console.log);
User.upsert({ a: 'a', b: 'b', username: 'john' })
It will try to find record by hash in 1st param to update it, if it will not find it - then new record will be created
Here is example of usage in sequelize tests
it('works with upsert on id', function() {
return this.User.upsert({ id: 42, username: 'john' }).then(created => {
if (dialect === 'sqlite') {
expect(created).to.be.undefined;
} else {
expect(created).to.be.ok;
}
this.clock.tick(1000);
return this.User.upsert({ id: 42, username: 'doe' });
}).then(created => {
if (dialect === 'sqlite') {
expect(created).to.be.undefined;
} else {
expect(created).not.to.be.ok;
}
return this.User.findByPk(42);
}).then(user => {
expect(user.createdAt).to.be.ok;
expect(user.username).to.equal('doe');
expect(user.updatedAt).to.be.afterTime(user.createdAt);
});
});
Sound likes you want to wrap your Sequelize calls inside of an async.each.
This can be done with the custom event emitter.
Assuming your data is in a variable called data.
new Sequelize.Utils.CustomEventEmitter(function(emitter) {
if(data.id){
Model.update(data, {id: data.id })
.success(function(){
emitter.emit('success', data.id );
}).error(function(error){
emitter.emit('error', error );
});
} else {
Model.build(data).save().success(function(d){
emitter.emit('success', d.id );
}).error(function(error){
emitter.emit('error', error );
});
}
}).success(function(data_id){
// Your callback stuff here
}).error(function(error){
// error stuff here
}).run(); // kick off the queries
you can use findOrCreate and then update methods in sequelize. here is a sample with async.js
async.auto({
getInstance : function(cb) {
Model.findOrCreate({
attribute : value,
...
}).complete(function(err, result) {
if (err) {
cb(null, false);
} else {
cb(null, result);
}
});
},
updateInstance : ['getInstance', function(cb, result) {
if (!result || !result.getInstance) {
cb(null, false);
} else {
result.getInstance.updateAttributes({
attribute : value,
...
}, ['attribute', ...]).complete(function(err, result) {
if (err) {
cb(null, false);
} else {
cb(null, result);
}
});
}
}]
}, function(err, allResults) {
if (err || !allResults || !allResults.updateInstance) {
// job not done
} else {
// job done
});
});
Here is a simple example that either updates deviceID -> pushToken mapping or creates it:
var Promise = require('promise');
var PushToken = require("../models").PushToken;
var createOrUpdatePushToken = function (deviceID, pushToken) {
return new Promise(function (fulfill, reject) {
PushToken
.findOrCreate({
where: {
deviceID: deviceID
}, defaults: {
pushToken: pushToken
}
})
.spread(function (foundOrCreatedPushToken, created) {
if (created) {
fulfill(foundOrCreatedPushToken);
} else {
foundOrCreatedPushToken
.update({
pushToken: pushToken
})
.then(function (updatedPushToken) {
fulfill(updatedPushToken);
})
.catch(function (err) {
reject(err);
});
}
});
});
};
2022 update:
You can use the upsert function:
https://sequelize.org/api/v6/class/src/model.js~model#static-method-upsert
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.
Implementation details:
MySQL - Implemented with ON DUPLICATE KEY UPDATE`
PostgreSQL - Implemented with ON CONFLICT DO UPDATE. If update data contains PK field, then PK is selected as the default conflict key.
Otherwise first unique constraint/index will be selected, which can satisfy conflict key requirements.
SQLite - Implemented with ON CONFLICT DO UPDATE
MSSQL - Implemented as a single query using MERGE and WHEN (NOT) MATCHED THEN
Note that Postgres/SQLite returns null for created, no matter if the row was created or updated