Supabase, filter by column value of foreign key row - sql

I am trying to figure out how to implement a query in supabase:
Schema
CREATE TABLE cars
(
id SERIAL PRIMARY KEY,
brand TEXT
);
CREATE TABLE stores
(
id SERIAL PRIMARY KEY,
car INT REFERENCES car(id),
name TEXT
);
I want to get all stores which carry the car of brand "x"
In Supabase I can filter like this:
let { data: stores } = await supabase
.from('stores')
.select("*")
.eq('name', 'Ford car shop')
// Returns
{
id: 123456,
car:"Ford",
name:"Ford car shop"
}
or join like this:
let { data: stores } = await supabase
.from('stores')
.select(`
*,
cars (
brand
)
`)
.eq('name', 'Ford car shop')
// Returns
{
id: 123456,
car:"Ford",
cars: {
id: 654321,
brand: "Ford"
}
name:"Ford car shop"
}
But how can I filter stores by the brand of the car they carry using the supabase sdk?

2022: This is now possible with supabase client. You can use the !inner() function.
let { data: stores } = await supabase
.from('stores')
.select('*, cars!inner(*)')
.eq('cars.brand', 'Ford')
Doc here: Filtering with inner joins

You can use the built in postgrest api supabase gives you to get this info. e.g:
/projects?select=*,clients!inner(*)&clients.id=eq.12
this isn't added to the supabase client yet.
the patch is shown here: https://github.com/PostgREST/postgrest/releases/tag/v9.0.0

Edit:
Supabase recently added this feature. The new accepted answer is below.
I did some more research and found out that this is currently not possible. However, it seems like it has been implemented and will make it into the next Supabase release.
An interim solution is using views and then querying those.
CREATE VIEW stores_expanded AS
SELECT
stores.id,
stores.name,
cars.brand
FROM
stores
LEFT JOIN cars
ON
stores.car = cars.id;
let { data: stores } = await supabase
.from('stores')
.select("*")
.eq('brand', 'x')

Related

Knex : update a slice of randomly selected records

I have a table that stores gifts :
export interface Gift {
id: number
type : string
claim_status: string
user_id?: number
}
When a user claims one or multiple gifts, i want to select randomly some gifts and update them with the user_id. I tried using the knex limitfunction but it doesn't work for updating.
export const claimGifts = async (
user : User,
numberToClaim: number,
trx: Knex.Transaction
) => {
const gifts = await db<Gift>('gift')
.where({claim_status : 'available'})
// limit the amount of updated to numberToClaim be slicing randomly
.update({user_id : user.id, claim_status : 'claimed'}, '*')
.transacting(trx)
return gifts
}
Any idea ?
You should first construct a SQL query, and then convert it to Knex usage.
I would use nested query for selecting random entries, something like:
Update gift set user_id = 'MY_USER_ID' Where id IN (Select inner_g.id from gifts as inner_g where claim_status='available' Order by RAND() Limit 3)
When it converted to Knex, it looks like:
const gifts = await db<Gift>('gift')
.update({
user_id: user.id,
claim_status: 'claimed',
})
.whereIn(
'id',
db('gift as inner_g')
.columns('inner_g.id')
.where({ claim_status: 'available' })
.orderBy(db.raw('RAND()') as any)
.limit(3)
);

apply count on inner join in sql using sequelize

If i have Posts model and Likes model that PostsLikes model has postID as foreign key
How to create orm sequelize query to get post by id and number of likes on it
You should use a subquery in attributes options like this:
const posts = await Posts.findByPk(postId, {
attributes: {
include: [[Sequelize.literal('SELECT COUNT(*) FROM PostsLikes where PostsLikes.postID = Posts.ID)'), 'LikesCount']]
}
})

Why is POST request with pool.query only works intermittently when using :id in the middle of URL?

I wasn't quite sure how to phrase this question so feel free to make corrections to improve it as desired.
My goal is to make an HTTP POST that will create comments for a post and add the comment to the database comments table. I believe this necessitates doing an INSERT as well as a JOIN to add the specific POST id to the comment.
This is my first time including two requests in one query so I am unsure if this is correct. I had read about using a UNION but haven't been able to figure out the correct syntax as none of the examples included quotes '' around their requests.
My post route:
router.post(`/posts/:id/comments`, (request, response, next) => {
const { id } = request.params; // tried with and without brackets {}
const { comment_body } = request.body;
// Testing for correct params
console.log(id);
console.log(comment_body);
pool.query(
'INSERT INTO comments(comment_body) VALUES($1)',
[post_id, comment_body],
'SELECT * FROM comments JOIN posts ON posts.post_id = commments.post_id',
(err, res) => {
if (err) return next(err);
}
);
});
What is strange is that this worked twice then stopped working. There are two entries in the comments table but any further posts don't do anything. This only worked from the comments form and not yet in Postman
This worked in two separate tests. When using brackets around the id, the post was created in the table but no post_id was joined on this table:
const { id } = request.params;
If I didn't use the brackets, the post_id was created in the data table:
const id = request.params;
Here are my tables:
CREATE TABLE posts(
post_id SERIAL,
user_id INT,
post_body CHARACTER varying(20000)
);
CREATE TABLE comments(
id SERIAL,
post_id INT,
user_id INT,
comment_body CHARACTER varying(20000)
);
Originally I had the post_id for comments set as serial but figured if that is supposed to be joined from the posts.post_id, it would probably need to be INT.
Thanks much for any direction.
I managed to solve this with the following:
router.post(`/posts/:id/comments`, async (request, response, next) => {
try {
const { id } = request.params;
const { comment_body } = request.body;
await pool.query
('INSERT INTO comments(post_id, comment_body) VALUES($1, $2)',
[id, comment_body]);
} catch(error) {
console.log(error.message)
}
});
Rather than using the JOIN, I just included the posts ID parameter in the original INSERT and imported it that way. I had initially thought I had to do it as a join but couldn't get a second SQL request to work. Thanks to snakecharmerb for the idea.
I also added async/await.

hasmany in express js not working with sequelize

I have created models Skills and Members
Member Table:
id username password created
1 gagandeep 99703dd91da5b0cabf496d49af0ab03c2dfe7788 2017-08-14 05:59:46
Skills Table:
id member_id skill
1 1 programming
2 1 music
Code:
Model
var Member=Seq.define('members',{}, {});
var Skills=Seq.define('skills',{}, {});
Member.hasMany(Skills,{as: 'skills', through: 'skills', foreignKey:'member_id'});
Call:
router.route('/test').get(function(req, resp){
Member.findAll({include: [{model: Skills}]},{
raw:true
}).success(onSuccess).error(onError);
function onSuccess(data){
resp.json(data);
}
function onError(data){
resp.json(data);
}
});
But Node js showing error as below:
Error: skills is not associated to members! at DAOFactory.validateIncludedElement (C:\Users\promatics\Desktop\netpar\node_modules\sequelize\lib\dao-factory.js:1476:13) at DAOFactory.validateIncludedElements (C:\Users\promatics\Desktop\netpar\node_modules\sequelize\lib\dao-factory.js:1388:59) at DAOFactory.module.exports.DAOFactory.findAll (C:\Users\promatics\Desktop\netpar\node_modules\sequelize\lib\dao-factory.js:458:34) at Object.handle (C:\Users\promatics\Desktop\netpar\demo.js:72:9) at next_layer (C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\route.js:103:13) at Route.dispatch (C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\route.js:107:5) at C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\index.js:195:24 at Function.proto.process_params (C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\index.js:251:12) at next (C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\index.js:189:19) at next (C:\Users\promatics\Desktop\netpar\node_modules\express\lib\router\index.js:166:38)
You don't actually need that through statement. You only use through when you have an association between two tables through a junction table. What I would do here is something like...
var Member = Seq.define('members', {
// Some attributes...
});
var Skills = Seq.define('skills', {
// Some attributes...
});
Member.hasMany(Skills, {
as: 'skills',
// Remove `through`!
foreignKey: 'member_id'
});
// Want to query for all members with the skills joined?
Member.findAll({
include: {
model: Skills,
as: 'skills'
}
});
That should get you the results you are looking for. Each member object will have reference to an inner skills key, which will be an array of skills associated to the user.
Good luck :)

SQL to Insert data into multiple tables from one POST in WebMatrix Razor Syntax

I've got two form fields from which the user submits a 'category' and an 'item'.
The following code inserts the category fine (I modified it from the WebMatrix intro PDF) but I've no idea how to then insert the 'item' into the Items table. I'll also need to add the Id of the new category to the new item row.
This is the code that's working so far
#{ var db = Database.OpenFile("StarterSite.sdf");
var Category = Request["Category"]; //was name
var Item = Request["Item"]; //was description
if (IsPost) {
// Read product name.
Category = Request["Category"];
if (Category.IsEmpty()) {
Validation.AddFieldError("Category", "Category is required");
}
// Read product description.
Item = Request["Item"];
if (Item.IsEmpty()) {
Validation.AddFieldError("Item",
"Item type is required.");
}
// Define the insert query. The values to assign to the
// columns in the Products table are defined as parameters
// with the VALUES keyword.
if(Validation.Success) {
var insertQuery = "INSERT INTO Category (CategoryName) " +
"VALUES (#0)";
db.Execute(insertQuery, Category);
// Display the page that lists products.
Response.Redirect(#Href("~/success"));
}
}
}
I'm guessing/hoping this is a very easy question to answer so hopefully there isn't much more detail required - but please let me know if there is. Thanks.
There's a Database.GetLastInsertId method within WebMatrix which returns the id of the last inserted record (assuming it's an IDENTITY column you are using). Use that:
db.Execute(insertQuery, Category);
var id = (int)db.GetLastInsertId(); //id is the new CategoryId
db.Execute(secondInsertQuery, param1, id);