There are 3 tables: artwork, sale, trade in sequelizejs 6.x. Both sale and trade belongs to artwork (artwork_id in both sale and trade table). The app needs to select artworks with sale or trade or both. There is no good support of union in sequelize and I have difficulty to come out with a single SQL to select in all 3 cases (artwork with sale, artwork with trade, or artwork with both sale and trade). Here is the sql to select artwork with both sale and trade:
let dataset = await Artwork.findAll(
include:[
{model:Sale},
{model:Trade}. //<<==both sale and trade are required
],
);
For selecting artwork with sale (only), the sql may look like:
let dataset = await Artwork.findAll(
include:[
{model:Sale, required:true},
{model:Trade, required:false}. //<<==trade not required
],
);
And with trade (only):
let dataset = await Artwork.findAll(
include:[
{model:Sale, required:false}, //<<==sale not required
{model:Trade, required:true}
],
);
Is there a way above 3 select can be combined into one SQL in sequelize?
UPDATE:
Here is model def:
Artwork.init({
name: {
type: DataTypes.STRING,
validate:{
len:{
args:[1,55],
msg:"max 55 chars",
},
},
},
author: {
type:DataTypes.STRING
},
category_id:{ //such as paiting, photo, calligraphy
type:DataTypes.INTEGER,
},
wt_g: {
type:DataTypes.DECIMAL(10,2),
},
production_year: {
type: DataTypes.STRING,
},
dimension: {
type:DataTypes.STRING,
},
uploader_id: {
type: DataTypes.INTEGER,
notNull:true,
},
description: {
type: DataTypes.TEXT,
},
note: {
type:DataTypes.TEXT
},
tag: {
type:DataTypes.ARRAY(DataTypes.STRING),
},
deleted: {
type:DataTypes.BOOLEAN,
defaultValue:false,
},
status: {
type: DataTypes.STRING,
defaultValue:"active"
},
artwork_data: {
type: DataTypes.JSONB
},
last_updated_by_id: {type: DataTypes.INTEGER},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE
}
Here is forsale:
ForSale.init({
buyer_id: {
type:DataTypes.INTEGER
},
artwork_id: {
type:DataTypes.INTEGER,
allowNull:false,
},
status: {
type:DataTypes.STRING,
},
price: {
type:DataTypes.INTEGER,
allowNull:false
},
shipping_cost: {
type:DataTypes.INTEGER,
},
shipping_method: {
type:DataTypes.INTEGER, //1-express, 2-air, 3-ground
},
transaction_closed:{
type: DataTypes.BOOLEAN,
defaultValue: false,
},
seller_refund_value:{type: DataTypes.INTEGER},
buyer_refund_value:{type: DataTypes.INTEGER},
escrow_value:{type: DataTypes.INTEGER},
deposit_date:{type: DataTypes.DATE},
buy_date:{type: DataTypes.DATE},
dispute_date:{type: DataTypes.DATE},
active_date:{type: DataTypes.DATE},
close_date:{type: DataTypes.DATE},
seller_receive_payment_date:{type: DataTypes.DATE},
buyer_receive_product_date:{type: DataTypes.DATE},
forsale_data: {
type: DataTypes.JSONB, //buyer_hashaddress, seller_hashaddress, trade_expense ...
},
deployed_address: {
type: DataTypes.STRING
},
smartcontract_name: {
type: DataTypes.STRING,
},
last_updated_by_id: {type: DataTypes.INTEGER},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE
}
Here is for trade:
ForTrade.init({
bidder_id: {
type:DataTypes.INTEGER
},
artwork_id: {
type:DataTypes.INTEGER,
allowNull:false,
},
status: {
type:DataTypes.STRING,
},
price: {
type:DataTypes.INTEGER,
allowNull:false
},
shipping_method: { //1-express, 2-air, 3-ground
type:DataTypes.INTEGER,
},
shipping_cost: {
type:DataTypes.INTEGER,
},
transaction_closed:{
type: DataTypes.BOOLEAN,
defaultValue:false,
},
poster_refund_value:{type: DataTypes.INTEGER},
bidder_refund_value:{type: DataTypes.INTEGER},
escrow_value:{type: DataTypes.INTEGER}, //total deposit is 2*escrow_value
poster_deposit_date:{type: DataTypes.DATE},
bidder_deposit_date:{type: DataTypes.DATE},
bid_date:{type: DataTypes.DATE},
active_date:{type: DataTypes.DATE},
poster_receive_product_date:{type: DataTypes.DATE},
poster_ship_product_date:{type: DataTypes.DATE},
bidder_receive_product_date:{type: DataTypes.DATE},
bidder_ship_product_date:{type: DataTypes.DATE},
close_date:{type: DataTypes.DATE},
fortrade_data: {
type: DataTypes.JSONB, //buyer_hashaddress, seller_hashaddress, trade_expense ...
},
deployed_address: {
type: DataTypes.STRING
},
smartcontract_name: {
type: DataTypes.STRING,
},
last_updated_by_id: {type: DataTypes.INTEGER},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE
}
Here is association:
Artwork.hasMany(ForSale, {foreignKey: 'artwork_id'});
Artwork.hasMany(ForTrade, {foreignKey: 'artwork_id'});
ForSale.belongsTo(Artwork, {foreignKey: "artwork_id"});
ForTrade.belongsTo(Artwork, {foreignKey: "artwork_id"})
You should indicate required: false
for both included models and add conditions for them on the root level like this:
let dataset = await Artwork.findAll(
include:[
{model:Sale, required:false},
{model:Trade, required:false}
],
where: {
// either sale record exists or trade record exists or both exist.
[Op.or]: [{
'$Sale.id$': {
[Op.ne]: null
}
},{
'$Trade.id$': {
[Op.ne]: null
}
}]
}
);