how to do subquery or backlink query in realm react-native? - react-native

The problem I'm trying to solve is this. If I have product, I'd like to know what other products I can purchase as a combination that's on-sale. If this were MySQL, I'd set up my tables and queries like so:
t_product: product_id, product_name
t_combination: combination_id, on_sale
t_product_combination: combination_id, product_id
// what other products should I buy along with my product_id == 1 to get a good deal?
SELECT product_id
FROM t_product
WHERE product_id IN (
SELECT product_id
FROM t_combination_product
WHERE combination_id IN (
SELECT combination_id
FROM t_combination
WHERE on_sale = 'yes'
AND combination_id IN (SELECT combination_id FROM t_combination_product WHERE product_id = 1)
)
)
I tried to do this in realm react-native as shown below:
I have two collections: Product and a Combination, set up as follows
class Product {};
Product.schema = {
name: 'Product',
primaryKey:'productId',
properties: {
productId:'int',
product:'string'
}
};
class Combination {};
Combination.schema = {
name: 'Combination',
properties: {
onSale: {type: 'string'},
products: {type: 'list',objectType:'Product'},
}
};
What I want to do is given that I have Product.productId=1, I want to find all other products that belong to Combinations that have Combination.onSale='yes' and Product.productId IS IN (Combination.products ).
This is the query I tried to do in realm with react-native:
let queryFilter = "ANY Combination.onSale = 'yes' AND Combination.products = 1";
let data = realm.objects('Product').objectsWhere(queryFilter);
But when I run this, I get the error
undefined is not a function (evaluating
'realm.objects('Product').objectsWhere(queryFilter)')
If I remove the objectsWhere(queryFilter), then it just returns me the list of all Products that hasn't been filtered.
So it seems I'm using objectsWhere incorrectly? How do I fix this to find a list of all products that are onsale if purchased with product 1?

If you had
class Product {};
Product.schema = {
name: 'Product',
primaryKey:'productId',
properties: {
productId:'int',
product:'string'
combinations: {type: 'linkingObjects', objectType: 'Combination', property: 'products'}
}
};
Backlink queries are not yet supported, so I think what you have to do is something like this:
let queryFilter = 'productId = 1';
let data = realm.objects('Product').filtered(queryFilter);
let combinations = data.combinations.filtered('onSale = true');
let linkedProducts = [];
for(let i = 0; i < combinations.length; i++) {
linkedProducts.push(combinations[i]);
}
// linked products should contain what you need

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!

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

Postgres query - return flattened JSON

I have the following working query:
const getPromos = async (limit = 10, site: string, branch: string) => {
const query = `SELECT
json_build_object(
'id', p.id,
'description', p.description,
'discounted_price', p.discounted_price,
'items', jsonb_agg((i.id, i.price, i.title))
)
FROM promotions p
INNER JOIN promotion_items pi ON p.id = pi.promotion_id
INNER JOIN items i ON pi.item_code = i.item_code WHERE site_id = ${site} and store_id = ${branch}
GROUP BY p.id LIMIT ${limit}`;
return await db.query(query);
};
The issue is simple - each item (in this example - promotion) is returned with an object that wraps it - named json_build_object. I don't want any object to wrap my promotions - just like this:
[{id:1, .... items: [...items here...]}, {id:2, .... items: [...items here...]}]
Any idea?
You can get the desired result directly from the query when you aggregate the resultset using jsonb_agg (like items further down the query).
SELECT
jsonb_agg(jsonb_build_object(
'id', p.id,
'description', p.description,
'discounted_price', p.discounted_price,
'items', jsonb_agg((i.id, i.price, i.title))
))
--- the rest of your query

Fetching data from the database based on different categories

I am setting up the query that fetch data from 3 tables based on filters that user select. filters can be multiple or can be none.
I made stored procedures with different combination. But I know that was the worst thing that I did.
var result = (from product in context.Products
from img in context.ProductImage
from saved in context.SavedProduct
where (cat.Color.Contains(product.Color)
& cat.BrandName.Contains(product.Brand_Name)
& cat.Fabric.Contains(product.Fabric)
& cat.Design.Contains(product.Design))
select new
{
product.ProductID,
product.Price,
product.Brand_Name,
product.Title,
product.Color,
product.Fabric,
product.Design,
img.Image,
saved.ProductSavedCounter,
}).ToList();
Product related details in a Product Table. Product images in a ProductImage table. And How many people saved this product are in SavedProduct table.
It returns the products only if user select all filters means when user select red color, Nike brand, cotton fabric etc. If one is missed than this query returns nothing. I want when 1 or 2 are missed than it should return data according to other selected fitters.
Pardon me if there is any mistake I am new bee.
And I missed the joins.
This may help:-
Note: I assumed the ProductID is in the other tables to make the joins, in general if your model have navigation properties for the Product image and save product you would not need the joins.
var result = from product in context.Products
join img in context.ProductImage on product.ProductID equals img.ProductID
join saved in context.SavedProduct on product.ProductID equals saved.ProductID
where (
(string.IsNullOrEmpty(cat.Color) || cat.Color.Equals(product.Color))
& (string.IsNullOrEmpty(cat.BrandName) || cat.Brand_Name.Equals(product.Brand_Name))
& (string.IsNullOrEmpty(cat.Fabric) || cat.Fabric.Equals(product.Fabric))
& (string.IsNullOrEmpty(cat.Design) || cat.Design.Equals(product.Design))
& (
((string.IsNullOrEmpty(cat.Color) ? 1 : 0) +
(string.IsNullOrEmpty(cat.BrandName) ? 1 : 0) +
(string.IsNullOrEmpty(cat.Fabric) ? 1 : 0) +
(string.IsNullOrEmpty(cat.Design) ? 1 : 0)) >= 2) //at least two conditions
)
select new {
product.ProductID,
product.Price,
product.Brand_Name,
product.Title,
product.Color,
product.Fabric,
product.Design,
img.Image,
saved.ProductSavedCounter,
};
I think a better solution would to build it gradually, as below:-
//build the joins
var result = (from product in context.Products
join img in context.ProductImage on product.ProductID equals img.ProductID
join saved in context.SavedProduct on product.ProductID equals saved.ProductID
select new { product, img, saved }).AsQueryable();
//get number of conditions avaliable
var conditionCount = (string.IsNullOrEmpty(cat.Color) ? 1 : 0) +
(string.IsNullOrEmpty(cat.Brand_Name) ? 1 : 0) +
(string.IsNullOrEmpty(cat.Fabric) ? 1 : 0) +
(string.IsNullOrEmpty(cat.Design) ? 1 : 0);
if (conditionCount >= 2)
{
//add the condition if they exists
if (!string.IsNullOrEmpty(cat.Color))
result = result.Where(x => cat.Color.Equals(x.product.Color));
if (!string.IsNullOrEmpty(cat.Brand_Name))
result = result.Where(x => cat.Brand_Name.Equals(x.product.Brand_Name));
if (!string.IsNullOrEmpty(cat.Fabric))
result = result.Where(x => cat.Fabric.Equals(x.product.Fabric));
if (!string.IsNullOrEmpty(cat.Design))
result = result.Where(x => cat.Design.Equals(x.product.Design));
//make the final select
var finalResult = result.Select(x => new
{
x.product.ProductID,
x.product.Price,
x.product.Brand_Name,
x.product.Title,
x.product.Color,
x.product.Fabric,
x.product.Design,
x.img.Image,
x.saved.ProductSavedCounter,
}).ToList();
}
And if you meant that whenever any filter is missing the query should ignore it remove the condition count and the if statement for it.

Dynamic column search in multiple tables with gorm golang

My scenario is i have a grid with search option where user can select the column and can do the search, the grid data is coming from various tables. I have attached a sample screen of grid.
User Screen
So i'm trying to create a dynamic query for search but the problem is i can able to search only in main table (schema.Robot) not in Preload tables. whenever i trying to search data data from Preload tables let say from RobotModel table that time getting below error
pq: missing FROM-clause entry for table "robot_models"
Here is my go code
func (r *RobotsRepository) GetRobotsSummary(listParams viewmodel.ListParams, companyID uint) ([]*schema.Robot, int, error) {
mrobots := []*schema.Robot{}
var count int
var order string
if listParams.SortColumn == "" {
listParams.SortColumn = "id"
listParams.SortOrder = 1
} else {
listParams.SortColumn = util.Underscore(listParams.SortColumn)
}
if listParams.SortOrder == 0 {
order = "ASC"
} else {
order = "DESC"
}
var searchQuery string
if listParams.SearchText != "" {
switch listParams.SearchColumn {
case "Robot":
listParams.SearchColumn = "name"
case "Model":
listParams.SearchColumn = "robot_models.name"
}
searchQuery = listParams.SearchColumn +" LIKE '%"+ listParams.SearchText +"%' and Company_ID = " + fmt.Sprint(companyID)
}else{
searchQuery = "Company_ID = " + fmt.Sprint(companyID)
}
orderBy := fmt.Sprintf("%s %s", listParams.SortColumn, order)
err := r.Conn.
Preload("RobotModel", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Name")
}).
Preload("Task", func(db *gorm.DB) *gorm.DB {
return db.Where("Task_Status in ('In-Progress','Pending')").Select("ID, Task_Status")
}).
Preload("CreatedUser", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Display_Name")
}).
Preload("UpdatedUser", func(db *gorm.DB) *gorm.DB {
return db.Select("ID,Display_Name")
}).
Where(searchQuery).
Order(orderBy).
Offset(listParams.PageSize * (listParams.PageNo - 1)).
Limit(listParams.PageSize).
Find(&mrobots).Error
r.Conn.Model(&schema.Robot{}).Where(searchQuery).Count(&count)
return mrobots, count, err
}
In searchQuery variable i'm storing my dynamic query.
My question is how can i search data for preload table columns
Here is the sql query which i'm trying to achieve using gorm
SELECT robots.id,robots.name,robot_models.name as
model_name,count(tasks.task_status) as task_on_hand,
robots.updated_at,users.user_name as updated_by
FROM rfm.robots as robots
left join rfm.tasks as tasks on tasks.robot_id = robots.id and
tasks.task_status in ('In-Progress','Pending')
left join rfm.robot_models as robot_models on robot_models.id =
robots.robot_model_id
left join rfm.users as users on users.id = robots.updated_by
WHERE robot_models.name::varchar like '%RNR%' and robots.deleted_at is null
GROUP BY robots.id,robot_models.name,users.user_name
ORDER BY task_on_hand DESC LIMIT 2 OFFSET 0
and sorry for bad English!
Even though you are preloading, you are still required to explicitly use joins when filtering and ordering on columns on other tables. Preloading is used to eagerly load the data to map into your models, not to join tables.
Chain on something like this:
.Joins("LEFT JOIN rfm.robot_models AS robot_models ON robot_models.id = robots.robot_model_id")
I'm not positive if you can use the AS keyword using this technique, but if not, it should be easy enough to adjust your query accordingly.