Knex : update a slice of randomly selected records - sql

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)
);

Related

How to count number of user id in a list without duplicate in sequelize [duplicate]

I am trying to get a distinct count of a particular column using sequelize. My initial attempt is using the 'count' method of my model, however it doesn't look like this is possible.
The DISTINCT feature is needed because I am joining other tables and filtering the rows of the parent based on the related tables.
here's the query I would like:
SELECT COUNT(DISTINCT Product.id) as `count`
FROM `Product`
LEFT OUTER JOIN `Vendor` AS `vendor` ON `vendor`.`id` = `Product`.`vendorId`
WHERE (`vendor`.`isEnabled`=true );
using the following query against my Product model:
Product.count({
include: [{model: models.Vendor, as: 'vendor'}],
where: [{ 'vendor.isEnabled' : true }]
})
Generates the following query:
SELECT COUNT(*) as `count`
FROM `Product`
LEFT OUTER JOIN `Vendor` AS `vendor` ON `vendor`.`id` = `Product`.`vendorId`
WHERE (`vendor`.`isEnabled`=true );
UPDATE: New version
There are now separate distinct and col options. The docs for distinct state:
Apply COUNT(DISTINCT(col)) on primary key or on options.col.
You want something along the lines of:
MyModel.count({
include: ...,
where: ...,
distinct: true,
col: 'Product.id'
})
.then(function(count) {
// count is an integer
});
Original Post
(As mentioned in the comments, things have changed since my original post, so you probably want to ignore this part.)
After looking at Model.count method in lib/model.js, and tracing some code, I found that when using Model.count, you can just add any kind of aggregate function arguments supported by MYSQL to your options object. The following code will give you the amount of different values in MyModel's someColumn:
MyModel.count({distinct: 'someColumn', where: {...}})
.then(function(count) {
// count is an integer
});
That code effectively generates a query of this kind: SELECT COUNT(args) FROM MyModel WHERE ..., where args are all properties in the options object that are not reserved (such as DISTINCT, LIMIT and so on).
The Sequelize documentation on count links to a count method that doesn't let you specify which column to get the count of distinct values:
Model.prototype.count = function(options) {
options = Utils._.clone(options || {});
conformOptions(options, this);
Model.$injectScope(this.$scope, options);
var col = '*';
if (options.include) {
col = this.name + '.' + this.primaryKeyField;
expandIncludeAll.call(this, options);
validateIncludedElements.call(this, options);
}
Utils.mapOptionFieldNames(options, this);
options.plain = options.group ? false : true;
options.dataType = new DataTypes.INTEGER();
options.includeIgnoreAttributes = false;
options.limit = null;
options.offset = null;
options.order = null;
return this.aggregate(col, 'count', options);
};
Basically SELECT COUNT(DISTINCT(*)) or SELECT COUNT(DISTINCT(primaryKey)) if you've got a primary key defined.
To do the Sequelize equivalent of SELECT category, COUNT(DISTINCT(product)) as 'countOfProducts' GROUP BY category, you'd do:
model.findAll({
attributes: [
'category',
[Sequelize.literal('COUNT(DISTINCT(product))'), 'countOfProducts']
],
group: 'category'
})
Looks like this is now supported in Sequelize versions 1.7.0+.
the count and findAndCountAll methods of a model will give you 'real' or 'distinct' count of your parent model.
I was searching for SELECT COUNT(0) query for sequelize, below is the answer for that.
let existingUsers = await Users.count({
where: whereClouser,
attributes: [[sequelize.fn('COUNT', 0), 'count']]
});
This helped me to get distinct count from another table rows,
dataModel.findAll({
attributes: {
include: [[Sequelize.literal("COUNT(DISTINCT(history.data_id))"), "historyModelCount"]]
},
include: [{
model: historyModel, attributes: []
}],
group: ['data.id']
});
Ref 1, Ref 2.
With respect to your question in order to get the distinct counts of products based on the id of product
you just need to pass the key 'distinct' with value 'id' to your count object , Here is the example
To generate this sql query as you asked
SELECT COUNT(DISTINCT(`Product`.`id`)) as `count`
FROM `Product`
LEFT OUTER JOIN `Vendor` AS `vendor` ON `vendor`.`id` = `Product`.`vendorId`
WHERE (`vendor`.`isEnabled`=true );
Add 'distinct' key in your Sequelize query
Product.count({
include: [{model: models.Vendor, as: 'vendor'}],
where: [{ 'vendor.isEnabled' : true }],
distinct: 'id' // since count is applied on Product model and distinct is directly passed to its object so Product.id will be selected
});
This way of using 'distinct' key to filter out distinct counts or rows , I tested in Sequelize Version 6.
Hope this will help you or somebody else!

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']]
}
})

Supabase, filter by column value of foreign key row

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')

NODE JS Passing characters in get request

I am using Node and Express with MSNODESQLV8 to write an API demo (my first) to get some rows from a remote SQL Server instance. My other get queries work fine when searching for an ID which is a number but I am unsure how to pass a value in the form of characters to a parameter in my query. Pretty sure req.params.id is not appropriate.
app.get("/productsname/:id", (req, res) => {
const productName = req.params.id;
const productsNameQuery = "SELECT * FROM Products WHERE ProductName = ?";
sql.query(connStr, productsNameQuery, [productName], (err, rows) => {
if (err) {
console.log(`Failed to get product by id ${req.params.id}. ${err}`);
res.sendStatus(500);
}else {
res.json(rows);
}
})
});
I want to take a product name (string?) in at the end of the url where it reads "id" and pass it as a value to the productName const. The end goal is to retrieve all rows from the SQL table where the product name is "processor" in the get url (http://localhost:2000/productname/proccesor). Perhaps I am passing the url incorrectly?
Apologies if this is really basic. I am very new to this.
Thanks in advance

The nested query does not have the appropriate keys

Firstly I have a function which takes 2 parameters( longitude, latitude).
RETURNS TABLE
AS
RETURN
(
select dbo.GeoCalculateDistance (#lat1Degrees,#lon1Degrees,Latitude,Longitude) as Distance, PKRestaurantId as PkKeyId from StRestaurant
)
And as you realise, I have a table that called StRestaurant. In this table I have 4 columns(PkRestaurantId, RegionId , Longitude, Latitude).
And, I need a method that takes 4 parameters.
public List<RestaurantDetailDto> GetRestaurant(int regionid, decimal latitude, decimal longitude, OrderType orderType)
{}
This method will give the restauants around me. But if I want to systematize this list with distance, I must join my Restaurant table and the function. Here is my query.
var query = from restaurant in context.StRestaurant
join distance in context.CalculateDistanceTable(latitude, longitude) on restaurant.PKRestaurantId equals distance.PkKeyId
where restaurant.FKRegionId == regionid
select new
{
Restaurant = restaurant,
DistanceTable = distance,
};
And then I am checking the orderType,
switch (orderType)
{
case OrderType.Distance:
query = query.OrderBy(x => x.DistanceTable.Distance);
break;
// and the anothers
}
Lastly, I am trying to take this list as;
var queryResult = query.ToList();
All the time I took this error :
The nested query does not have the appropriate keys.
I also try the above query but it return with the same error :s
var query = context.StRestaurant.Where(x => x.FKRegionId == regionid && x.IsActive).Join(
context.CalculateDistanceTable(latitude, longitude),
restaurant => restaurant.PKRestaurantId,
result => result.PkKeyId,
(restaurant, result) => new
{
Restaurant = restaurant,
MinumumPackagePrice = restaurant.StRestaurantRegionRelation.FirstOrDefault(x => x.FKRestaurantId == restaurant.PKRestaurantId).MinumumPackageCharge,
DistanceTable = result,
RestaurantImage = restaurant.StRestaurantImage.Where(x => x.IsDefault && x.FKRestaurantId == restaurant.PKRestaurantId),
}
);
Please help!!
I've seen this before when doing an .Include() on the result. I imagine your projection (in the second example) might be doing this internally. Could you add this to the first part?
In this case, I've had to add the .Include() on the source table:
from a in context.A.Include("relationship")
join b in context.MyFunction(...)
...
There are a some things that you can try here. First, rewrite your SQL function so that it has a primary key:
CREATE FUNCTION CalculateDistanceTable
(
-- Add the parameters for the function here
#lat1Degrees float,
#lon1Degrees float
)
RETURNS
#RestaurantDistances TABLE
(
-- Add the column definitions for the TABLE variable here
PkKeyId int NOT NULL primary key,
Distance float NOT NULL
)
AS
BEGIN
INSERT INTO #RestaurantDistances
SELECT dbo.GeoCalculateDistance(#lat1Degrees, #lon1Degrees, Latitude, Longitude) AS Distance, PKRestaurantId AS PkKeyId
FROM StRestaurant
RETURN
END
GO
Also, you can try changing your LINQ join to use anonymous types to perform the join.
var query = from restaurant in context.StRestaurant
join distance in context.CalculateDistanceTable(latitude, longitude) on new { Key = restaurant.PKRestaurantId } equals new { Key = distance.PkKeyId }
where restaurant.FKRegionId == regionid
select new
{
Restaurant = restaurant,
DistanceTable = distance,
};
If neither one of these helps let me know and I'll try to update this answer as appropriate.