Mimic COALESCE behaviour using groups of conditions when querying with Prisma client - prisma

I'm trying to replace a SQL query (Postgres) with a Prisma client query, and I'm struggling to implement this COALESCE logic:
COALESCE(date_1, date_2,'1970-01-01 00:00:00') <= CURRENT_TIMESTAMP
As fas as I can tell, Prisma client doesnt currently support COALESCE, so there's no simple way for me to do this. I think I have to group conditions together to replicate this. The logic would be along the lines of:
ANY of the following
date_1 <= CURRENT_TIMESTAMP
ALL of the following
date_1 IS NULL
date_2 <= CURRENT_TIMESTAMP
ALL of the following
date_1 IS NULL
date_2 IS NULL
But I cant work out how to group conditions like this using the Prisma client, because you cant have multiple AND's within an OR (or vis versa):
const records = await client.thing.findMany({
where: {
OR: {
date_1: { lte: new Date() },
AND: {
date_1: null,
date_2: { lte: new Date() }
},
AND: { // <--- Cant do this, because we've already used AND above
date_1: null,
date_2: null
}
}
},
});
We obviously cant repeat the same object key twice, so I cant have two AND statements at the same level, otherwise I get the error:
An object literal cannot have multiple properties with the same name
I'm probably missing something obvious, but I cannot work out how to achieve groups of conditions like this. If anyone can help I'd appreciate it!
I should mention that I'm using:
nodejs v14.19.1
prisma ^3.14.0

I was indeed missing something obvious. We can pass in an array of groups to an AND or OR group, like so:
const records = await client.thing.findMany({
where: {
OR: {
date_1: { lte: new Date() },
AND: [
{
date_1: null,
date_2: { lte: new Date() }
},
{
date_1: null,
date_2: null
}
]
}
},
});
https://www.prisma.io/docs/concepts/components/prisma-client/filtering-and-sorting#filter-conditions-and-operators

Related

JSONB filter on select via Supabase

I have such a logic (attributes column's type is JSONB - array of objects) that works:
But I want to implement logical OR here if trait_type is equal ... not AND:
JSONB's column structure:
[
{
"value":"Standard Issue Armor 1 (Purple)",
"trait_type":"Clothes"
},
{
"value":"Standard Issue Helmet 1 (Red)",
"trait_type":"Full Helmet"
},
{
"value":"Chrome",
"trait_type":"SmartSkin"
},
{
"value":"Base Drone (Blue)",
"trait_type":"Drone"
},
{
"value":"Thick",
"trait_type":"Eyebrows"
}
]
How that could be done?
Thanks in advance!
I didn't verify the code, so might not work, but I believe at least is in the right direction. You can use the .or() filter to connect multiple filters with logical or operator. For contains(), you can use the cs keyword inside the or filter like this:
const { data, error } = await supabase.from('NTFs')
.select('name, id_in_collection, owner_address')
.eq('collection_id', Number(id))
.contains('attributes', JSON.stringify([{trait_type: 'SmartSkin', value: 'Chrome'}]))
.or(`attributes.cs.${JSON.stringify([{trait_type: 'Drone', value: 'Armed Drone (Blue)'}])}`, `attributes.cs.${JSON.stringify([{trait_type: 'Drone', value: 'Armed Drone (Green)'}])}`)
.order('id_in_collection')
.range(fromIndex, toIndex)

How do I write a Prisma findFirst where clause that OR's two AND's -- or how does prisma treat undefined's?

I am using Prisma
I have a postgres table I want to search, finding only the record that matches where (id and accessKey) or (id and ownerId)
It keeps finding where (id) if the accessKey or ownerId are undefined
I've found that if I use impossible strings then the search works
I can't find the appropriate answer in the prisma docs so I thought I'd ask here
return await db.attendee.findFirst({
where: {
OR: [
{ AND: { id, ownerId } },
{ AND: { id, accessKey } },
]
}
})
it makes sense that if accessKey or ownerId are undefined that they then effectively disappear from the query
I used this and it worked -- the values are guid's so they will never match, but if my secret phrase got loose (it's in the repo, not in an env var) then that would be an exploit akin to sql-injection...
const userId = context?.currentUser?.sub || `can't touch this`;
const accessKey = vaccessAuth?.accessKey || `can't touch this`;
What might be more the "prisma-way" ?
From what I understand, we can phrase your problem like this (please correct me if I'm wrong).
Find the first attendee where:
id field matches AND ( accessKey OR ownerId field matches)
Try this:
return await prisma.attendee.findFirst({
where: {
OR: [
{ ownerId: ownerIdValue },
{ accessKey: accessKeyValue }
],
AND: { id: idValue }
}
});

Updating multiple columns in PostgreSQL using Knex.js

I am trying to update multiple columns within a table using Knex.js
I have tried looking at suggestions in the Knex docs as well as online, but so far have not found any working solution.
My coding attempt:
const userUpdateHandler = (req,res,database)=>{
const { id } = req.params;
const { name, age } = req.body.formInput
database('users')
.where({ id })
.update({ name }).update({ age})
.then(resp => {
if (resp) {
res.json("success")
} else {
res.status(400).json('Not found')
}
})
.catch(err => res.status(400).json('error updating user'))
}
The above is something I've tried out of sheer desperation, but what I would like it to do is to update multiple columns at once.
Could you please advise me on the best approach?
That should work https://runkit.com/embed/6ulv4hy93fcj
knex('table').update({foo: 1}).update({bar: 2}).toSQL()
// generates SQL: update "table" set "foo" = ?, "bar" = ?
and so should:
database('users')
.where({ id })
.update({ name, age })
Check out if your generated queries are what you are expecting...

Sequelize set timezone to query

I'm currently using the Sequelize with postgres in my project. I need to change the query, so it return created_at column with timezone offset.
var sequelize = new Sequelize(connStr, {
dialectOptions: {
useUTC: false //for reading from database
},
timezone: '+08:00' //for writing to database
});
But this affects on entire database. But I need to use timezone for select queries only. Does anyone know how to do that?
This is how I configured it:
dialectOptions: {
dateStrings: true,
typeCast: true,
},
timezone: 'America/Los_Angeles',
http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html
I suggest combining moment.js with one of the following methods:
If you need to parameterize the timezone, then you will probably want to add the offset for each individual query or add an additional field to your table that indicates the timezone, as it does not seem as though sequelize allows parameterized getters.
For example:
const moment = require('moment.js');
const YourModel = sequelize.define('your_model', {
created_at: {
type: Sequelize.DATE,
allowNull: false,
get() {
return moment(this.getDataValue('created_at'))
.utcOffset(this.getDataValue('offset'));
},
},
});
Another possibility would be to extend the model prototype's instance methods in a similar fashion, which allows you to specify the offset at the time that you require the created_at value:
const moment = require('moment.js');
YourModel.prototype.getCreatedAtWithOffset = function (offset) {
return moment(this.created_at).utcOffset(offset);
};
For correct using queries with timezone, prepare your pg driver, see details here:
const pg = require('pg')
const { types } = pg
// we must store dates in UTC
pg.defaults.parseInputDatesAsUTC = true
// fix node-pg default transformation for date types
// https://github.com/brianc/node-pg-types
// https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js
types.setTypeParser(types.builtins.DATE, str => str)
types.setTypeParser(types.builtins.TIMESTAMP, str => str)
types.setTypeParser(types.builtins.TIMESTAMPTZ, str => str)
It's must be initialized before initiating your Sequelize instance.
You can now run a query indicating the timezone for which you want to get the date.
Suppose we make a SQL query, select all User's fields, and "createdAt" in timezone 'Europe/Kiev':
SELECT *, "createdAt"::timestamptz AT TIME ZONE 'Europe/Kiev' AS "createdAt" FROM users WHERE id = 1;
# or with variables
SELECT *, "createdAt"::timestamptz AT TIME ZONE :timezone AS "createdAt" FROM users WHERE id = :id;
For Sequelize (for User model) it will be something like this:
sequelize.findOne({
where: { id: 1 },
attributes: {
include: [
[sequelize.literal(`"User"."createdAt"::timestamptz AT TIME ZONE 'Europe/Kiev'`), 'createdAt'],
// also you can use variables, of course remember about SQL injection:
// [sequelize.literal(`"User"."updatedAt"::timestamptz AT TIME ZONE ${timeZoneVariable}`), 'updatedAt'],
]
}
});

Compare two fields in Waterline/Sails.js query

I want to compare two fields in my Waterline query in Sails.js application, e.g.: SELECT * FROM entity E WHERE E.current < E.max.
I've tried the following code, but it's expecting integer value to be passed to it instead of column name:
Entity.find({
where: {
current: {'<': 'max'}
}
});
So, how do I compare two columns?
I have ran some tests and at the same time read the Waterline documentation. There is no indication of anything that could possibly do comparison of two fields/columns via .find() or .where() methods. Reference: http://sailsjs.org/documentation/concepts/models-and-orm/query-language
Instead, I have used .query() method to compare two fields via SQL string such as :
Entity.query("SELECT * FROM `Entity` E WHERE E.current < E.max", function( err, res) {
if(err) {
//exception
} else {
console.log('response',res);
}
});
The other way would be to use one query to get the max before putting it in the criteria.
EntityOne.find({
sort: 'column DESC'
}).limit(1)
.exec(function(err,found){
var max = found[0]
EntityTwo.find({
where: {
current: {'<': found}
}
}).exec((err,found) {
// Do stuff here
});
});
The query method is ultimately going to be faster however