From 883cf1dbbc2189d82a0e80c9899011f61ebc2d25 Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Tue, 10 Dec 2024 12:23:57 +0100 Subject: [PATCH 1/5] feat: fix bids and sales queries --- src/ports/bids/queries.ts | 2 +- src/ports/sales/queries.ts | 66 +++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/ports/bids/queries.ts b/src/ports/bids/queries.ts index f392d93..5ddf322 100644 --- a/src/ports/bids/queries.ts +++ b/src/ports/bids/queries.ts @@ -69,7 +69,7 @@ export function getBidsQuery(options: GetBidsParameters) { const FILTER_BY_BIDDER = options.bidder ? SQL` LOWER(bidder) = LOWER(${options.bidder}) ` : null const FILTER_BY_SELLER = options.seller ? SQL` LOWER(seller) = LOWER(${options.seller}) ` : null - const FILTER_BY_CONTRACT_ADDRESS = options.contractAddress ? SQL` LOWER(contract_address) = LOWER(${options.contractAddress}) ` : null + const FILTER_BY_CONTRACT_ADDRESS = options.contractAddress ? SQL` contract_address = ${options.contractAddress.toLowerCase()} ` : null const FILTER_BY_TOKEN_ID = options.tokenId ? SQL` LOWER(token_id) = LOWER(${options.tokenId}) ` : null const FILTER_BY_ITEM_ID = options.itemId ? SQL` LOWER(item_id) = LOWER(${options.itemId}) ` : null const FILTER_BY_NETWORK = options.network ? SQL` network = ANY (${getDBNetworks(options.network)}) ` : null diff --git a/src/ports/sales/queries.ts b/src/ports/sales/queries.ts index 195bf66..c6396d0 100644 --- a/src/ports/sales/queries.ts +++ b/src/ports/sales/queries.ts @@ -24,7 +24,7 @@ function getSalesSortByStatement(sortBy?: SaleSortBy) { } } -function getLegacySalesQuery(filters: SaleFilters): SQLStatement { +function getLegacySalesQueryWhereStatement(filters: SaleFilters): SQLStatement { const FILTER_BY_TYPE = filters.type ? SQL` type = ${filters.type} ` : null const FILTER_BY_BUYER = filters.buyer ? SQL` buyer = ${filters.buyer} ` : null const FILTER_BY_SELLER = filters.seller ? SQL` seller = ${filters.seller.toLowerCase()} ` : null @@ -40,7 +40,7 @@ function getLegacySalesQuery(filters: SaleFilters): SQLStatement { const FILTER_FROM_TIMESTAMP = filters.from ? SQL` (timestamp * 1000) >= ${filters.from} ` : null const FILTER_TO_TIMESTAMP = filters.to ? SQL` (timestamp * 1000) <= ${filters.to} ` : null - const where = getWhereStatementFromFilters([ + return getWhereStatementFromFilters([ FILTER_BY_TYPE, FILTER_BY_BUYER, FILTER_BY_SELLER, @@ -54,7 +54,9 @@ function getLegacySalesQuery(filters: SaleFilters): SQLStatement { FILTER_FROM_TIMESTAMP, FILTER_TO_TIMESTAMP ]) +} +function getLegacySalesQuery(filters: SaleFilters): SQLStatement { return SQL` SELECT id, @@ -71,9 +73,8 @@ function getLegacySalesQuery(filters: SaleFilters): SQLStatement { search_category as category FROM squid_marketplace.sale ` - .append(where) + .append(getLegacySalesQueryWhereStatement(filters)) .append(getSalesLimitAndOffsetStatement(filters)) - // .append(SQL` LIMIT ${filters.first} OFFSET ${filters.skip} `) } function getTradeSalesQuery(filters: SaleFilters): SQLStatement { @@ -234,15 +235,21 @@ function getTradeSalesQuery(filters: SaleFilters): SQLStatement { LEFT JOIN marketplace.trade_assets_erc20 as erc20_asset ON ta.id = erc20_asset.asset_id LEFT JOIN marketplace.trade_assets_item as item_asset ON ta.id = item_asset.asset_id LEFT JOIN squid_marketplace.item as item ON (ta.contract_address = item.collection_id AND item_asset.item_id = item.blockchain_id::text) - LEFT JOIN squid_marketplace.nft as nft ON (ta.contract_address = nft.contract_address AND erc721_asset.token_id = nft.token_id::text) + LEFT JOIN ` + .append( + filters.contractAddress && filters.tokenId ? SQL`filtered_nfts` : SQL`squid_marketplace.nft` + ) + .append( + SQL` as nft ON (ta.contract_address = nft.contract_address AND erc721_asset.token_id::numeric = nft.token_id) LEFT JOIN squid_marketplace.account as account ON (account.id = nft.owner_id) ) as assets_with_values ON trade.id = assets_with_values.trade_id ` - .append(where) - .append( - SQL` GROUP BY trade_status.id, trade_status.timestamp, trade_status.network, trade_status.tx_hash, trade_status.sent_beneficiary, trade_status.received_beneficiary, trade.type ` + .append(where) + .append( + SQL` GROUP BY trade_status.id, trade_status.timestamp, trade_status.network, trade_status.tx_hash, trade_status.sent_beneficiary, trade_status.received_beneficiary, trade.type ` + ) + .append(having) ) - .append(having) ) ) ) @@ -258,15 +265,42 @@ function getTradeSalesQuery(filters: SaleFilters): SQLStatement { ) } +const getNFTCTE = (filters: SaleFilters) => { + console.log('filters', filters) + const FILTER_BY_CONTRACT_ADDRESS = filters.contractAddress ? SQL` contract_address = ${filters.contractAddress.toLowerCase()} ` : null + const FILTER_BY_ITEM_ID = filters.itemId ? SQL` item_id = ${filters.itemId} ` : null + const FILTER_BY_TOKEN_ID = filters.tokenId ? SQL` token_id = ${filters.tokenId} ` : null + const FILTER_BY_NETWORK = filters.network ? SQL` network = ANY (${getDBNetworks(filters.network)}) ` : null + const FILTER_BY_CATEGORY = filters.categories && filters.categories.length ? SQL` category = ANY (${filters.categories}) ` : null + + const where = getWhereStatementFromFilters([ + FILTER_BY_CONTRACT_ADDRESS, + FILTER_BY_ITEM_ID, + FILTER_BY_TOKEN_ID, + FILTER_BY_NETWORK, + FILTER_BY_CATEGORY + ]) + + return SQL` + WITH filtered_nfts AS ( + SELECT * + FROM squid_marketplace.nft + `.append(where).append(SQL` + ) + `) +} + export function getSalesQuery(filters: SaleFilters = {}) { const LEGACY_SALES = SQL`(`.append(getLegacySalesQuery(filters)).append(SQL` ) as legacy_sales `) const TRADE_SALES = SQL`(`.append(getTradeSalesQuery(filters)).append(SQL` ) as trade_sales `) - return SQL`SELECT *, COUNT(*) OVER() as count` - .append(SQL` FROM `) - .append(LEGACY_SALES) - .append(SQL` NATURAL FULL OUTER JOIN `) - .append(TRADE_SALES) - .append(getSalesSortByStatement(filters.sortBy)) - .append(getSalesLimitAndOffsetStatement(filters)) + return getNFTCTE(filters).append( + SQL`SELECT *, COUNT(*) OVER() as count` + .append(SQL` FROM `) + .append(LEGACY_SALES) + .append(SQL` NATURAL FULL OUTER JOIN `) + .append(TRADE_SALES) + .append(getSalesSortByStatement(filters.sortBy)) + .append(getSalesLimitAndOffsetStatement(filters)) + ) } From f6c90e9df858471b32dfc8d0b815834eff19f91e Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Tue, 10 Dec 2024 12:27:59 +0100 Subject: [PATCH 2/5] feat: fix test --- src/controllers/handlers/utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/controllers/handlers/utils.ts b/src/controllers/handlers/utils.ts index cdd406b..b087aa9 100644 --- a/src/controllers/handlers/utils.ts +++ b/src/controllers/handlers/utils.ts @@ -23,6 +23,9 @@ import { AssetType, PriceFilterCategory, PriceFilters } from '../../ports/prices export const getItemsParams = (params: Params) => { const maxPrice = params.getString('maxPrice') const minPrice = params.getString('minPrice') + const isOnSale = params.getBoolean('isOnSale') ? params.getString('isOnSale') === 'true' : undefined + console.log('params.getBoolean', params.getBoolean('isOnSale')); + console.log('isOnSale: ', isOnSale); return { category: params.getValue('category', NFTCategory), creator: params.getList('creator'), From c72c23ffaa4fa8e77918b99762ee333e2ec7a040 Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Tue, 10 Dec 2024 12:34:20 +0100 Subject: [PATCH 3/5] feat: fix tests and prettier issue --- src/ports/sales/queries.ts | 1 + test/unit/bids-queries.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ports/sales/queries.ts b/src/ports/sales/queries.ts index c6396d0..51d61b5 100644 --- a/src/ports/sales/queries.ts +++ b/src/ports/sales/queries.ts @@ -74,6 +74,7 @@ function getLegacySalesQuery(filters: SaleFilters): SQLStatement { FROM squid_marketplace.sale ` .append(getLegacySalesQueryWhereStatement(filters)) + .append(getSalesSortByStatement(filters.sortBy)) .append(getSalesLimitAndOffsetStatement(filters)) } diff --git a/test/unit/bids-queries.spec.ts b/test/unit/bids-queries.spec.ts index e479d19..b6b8b4b 100644 --- a/test/unit/bids-queries.spec.ts +++ b/test/unit/bids-queries.spec.ts @@ -38,7 +38,7 @@ describe('when querying for bids', () => { describe('and the contract address filter is defined', () => { it('should add the filter to the query', () => { const query = getBidsQuery({ contractAddress: '0x123', offset: 1, limit: 1 }) - expect(query.text).toContain('LOWER(contract_address) = LOWER($1)') + expect(query.text).toContain('contract_address = $1') expect(query.values).toEqual(expect.arrayContaining(['0x123'])) }) }) From d4620016e344516c5e8a1b167f8d4555b07bb317 Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Tue, 10 Dec 2024 12:36:05 +0100 Subject: [PATCH 4/5] feat: rollback utils change --- src/controllers/handlers/utils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/controllers/handlers/utils.ts b/src/controllers/handlers/utils.ts index b087aa9..cdd406b 100644 --- a/src/controllers/handlers/utils.ts +++ b/src/controllers/handlers/utils.ts @@ -23,9 +23,6 @@ import { AssetType, PriceFilterCategory, PriceFilters } from '../../ports/prices export const getItemsParams = (params: Params) => { const maxPrice = params.getString('maxPrice') const minPrice = params.getString('minPrice') - const isOnSale = params.getBoolean('isOnSale') ? params.getString('isOnSale') === 'true' : undefined - console.log('params.getBoolean', params.getBoolean('isOnSale')); - console.log('isOnSale: ', isOnSale); return { category: params.getValue('category', NFTCategory), creator: params.getList('creator'), From 06f00220dee2566966a227001155bde492c0e160 Mon Sep 17 00:00:00 2001 From: Juanma Hidalgo Date: Tue, 10 Dec 2024 12:40:53 +0100 Subject: [PATCH 5/5] feat: remove the trades sales since they are included in the sales db already --- src/ports/sales/queries.ts | 194 +------------------------------------ 1 file changed, 1 insertion(+), 193 deletions(-) diff --git a/src/ports/sales/queries.ts b/src/ports/sales/queries.ts index 51d61b5..93ea361 100644 --- a/src/ports/sales/queries.ts +++ b/src/ports/sales/queries.ts @@ -1,7 +1,6 @@ import SQL, { SQLStatement } from 'sql-template-strings' -import { NFTCategory, SaleFilters, SaleSortBy, SaleType, TradeAssetType, TradeType } from '@dcl/schemas' +import { SaleFilters, SaleSortBy } from '@dcl/schemas' import { getDBNetworks } from '../../utils' -import { ItemType } from '../items' import { getWhereStatementFromFilters } from '../utils' const DEFAULT_LIMIT = 100 @@ -78,194 +77,6 @@ function getLegacySalesQuery(filters: SaleFilters): SQLStatement { .append(getSalesLimitAndOffsetStatement(filters)) } -function getTradeSalesQuery(filters: SaleFilters): SQLStatement { - const FILTER_BY_TYPE = filters.type - ? filters.type == SaleType.ORDER - ? SQL` trade.type = '`.append(TradeType.PUBLIC_NFT_ORDER).append(SQL`'`) - : filters.type == SaleType.BID - ? SQL` c'`.append(TradeType.PUBLIC_NFT_ORDER).append(SQL`'`) - : SQL` ` - : null - - const FILTER_BY_BUYER = filters.buyer ? SQL` trade_status.sent_beneficiary = ${filters.buyer} ` : null - const FILTER_BY_SELLER = filters.seller ? SQL` trade_status.received_beneficiary = ${filters.seller.toLowerCase()} ` : null - const HAVING_BY_CONTRACT_ADDRESS = filters.contractAddress - ? SQL` ( - array_agg(assets_with_values.contract_address) FILTER ( - WHERE - assets_with_values.asset_type = '3' - OR assets_with_values.asset_type = '4' - ) - ) [1] = ${filters.contractAddress.toLowerCase()} ` - : null - const HAVING_BY_ITEM_ID = filters.itemId - ? SQL` ( - array_agg(assets_with_values.item_id) FILTER ( - WHERE - assets_with_values.item_id IS NOT NULL - ) - ) [1] = ${filters.itemId} ` - : null - const HAVING_BY_TOKEN_ID = filters.tokenId - ? SQL` ( - array_agg(assets_with_values.token_id) FILTER ( - WHERE - assets_with_values.token_id IS NOT NULL - ) - ) [1] = ${filters.tokenId} ` - : null - const FILTER_BY_NETWORK = filters.network ? SQL` trade_status.network = ANY (${getDBNetworks(filters.network)}) ` : null - const HAVING_BY_MIN_PRICE = filters.minPrice - ? SQL` ( - array_agg(assets_with_values.amount) FILTER ( - WHERE - assets_with_values.asset_type = '1' - ) - ) [1] >= ${filters.minPrice} ` - : null - const HAVING_BY_MAX_PRICE = filters.maxPrice - ? SQL` ( - array_agg(assets_with_values.amount) FILTER ( - WHERE - assets_with_values.asset_type = '1' - ) - ) [1] <= ${filters.maxPrice} ` - : null - const HAVING_BY_CATEGORY = - filters.categories && filters.categories.length - ? SQL` ( - array_agg(assets_with_values.category) FILTER ( - WHERE - assets_with_values.category IS NOT NULL - ) - ) [1] = ANY (${filters.categories}) ` - : null - const FILTER_FROM_TIMESTAMP = filters.from ? SQL` trade_status.timestamp >= ${filters.from} ` : null - const FILTER_TO_TIMESTAMP = filters.to ? SQL` trade_status.timestamp <= ${filters.to} ` : null - - const where = getWhereStatementFromFilters([ - SQL`trade_status.action = 'executed' AND trade.type != '`.append(TradeType.PUBLIC_ITEM_ORDER).append(SQL`'`), - FILTER_BY_TYPE, - FILTER_BY_BUYER, - FILTER_BY_SELLER, - FILTER_BY_NETWORK, - FILTER_FROM_TIMESTAMP, - FILTER_TO_TIMESTAMP - ]) - - const having = getWhereStatementFromFilters( - [HAVING_BY_CONTRACT_ADDRESS, HAVING_BY_ITEM_ID, HAVING_BY_TOKEN_ID, HAVING_BY_MIN_PRICE, HAVING_BY_MAX_PRICE, HAVING_BY_CATEGORY], - true - ) - - return SQL` - SELECT - trade_status.id as id, - CASE - WHEN trade.type = '` - .append(TradeType.PUBLIC_NFT_ORDER) - .append(SQL`' THEN '`) - .append(SaleType.ORDER) - .append( - SQL`' - ELSE '` - .append(SaleType.BID) - .append( - SQL`' END as type, - trade_status.received_beneficiary as seller, - trade_status.sent_beneficiary as buyer, - trade_status.timestamp, - trade_status.network, - trade_status.tx_hash, - (array_agg(assets_with_values.amount) FILTER (WHERE assets_with_values.asset_type = '` - .append(TradeAssetType.ERC20) - .append( - SQL`'))[1] as price, - (array_agg(assets_with_values.item_id) FILTER (WHERE assets_with_values.item_id IS NOT NULL))[1] as item_id, - (array_agg(assets_with_values.token_id) FILTER (WHERE assets_with_values.token_id IS NOT NULL))[1] as token_id, - (array_agg(assets_with_values.contract_address) FILTER (WHERE assets_with_values.asset_type = '` - .append(TradeAssetType.ERC721) - .append( - SQL`' OR assets_with_values.asset_type = '`.append(TradeAssetType.COLLECTION_ITEM).append( - SQL`'))[1] as contract_address, - (array_agg(assets_with_values.category) FILTER (WHERE assets_with_values.category IS NOT NULL))[1] as category - FROM squid_trades.trade as trade_status - JOIN marketplace.trades as trade ON trade_status.signature = trade.hashed_signature - JOIN ( - SELECT - ta.trade_id, - ta.contract_address, - ta.direction, - ta.beneficiary, - ta.asset_type, - ta.extra, - erc721_asset.token_id, - coalesce(item_asset.item_id, nft.item_blockchain_id::text) as item_id, - erc20_asset.amount, - item.creator, - item.item_type, - account.address as owner, - CASE - WHEN item.item_type = '` - .append(ItemType.WEARABLE_V1) - .append( - SQL`' THEN '`.append(NFTCategory.WEARABLE).append( - SQL`' - WHEN item.item_type = '` - .append(ItemType.WEARABLE_V2) - .append( - SQL`' THEN '`.append(NFTCategory.WEARABLE).append( - SQL`' - WHEN item.item_type = '` - .append(ItemType.SMART_WEARABLE_V1) - .append( - SQL`' THEN '`.append(NFTCategory.WEARABLE).append( - SQL`' - WHEN item.item_type = '` - .append(ItemType.EMOTE_V1) - .append( - SQL`' THEN '`.append(NFTCategory.EMOTE).append( - SQL`' - ELSE nft.category - END as category, - nft.id as nft_id, - nft.issued_id as issued_id, - nft.name as nft_name - FROM marketplace.trade_assets as ta - LEFT JOIN marketplace.trade_assets_erc721 as erc721_asset ON ta.id = erc721_asset.asset_id - LEFT JOIN marketplace.trade_assets_erc20 as erc20_asset ON ta.id = erc20_asset.asset_id - LEFT JOIN marketplace.trade_assets_item as item_asset ON ta.id = item_asset.asset_id - LEFT JOIN squid_marketplace.item as item ON (ta.contract_address = item.collection_id AND item_asset.item_id = item.blockchain_id::text) - LEFT JOIN ` - .append( - filters.contractAddress && filters.tokenId ? SQL`filtered_nfts` : SQL`squid_marketplace.nft` - ) - .append( - SQL` as nft ON (ta.contract_address = nft.contract_address AND erc721_asset.token_id::numeric = nft.token_id) - LEFT JOIN squid_marketplace.account as account ON (account.id = nft.owner_id) - ) as assets_with_values ON trade.id = assets_with_values.trade_id - ` - .append(where) - .append( - SQL` GROUP BY trade_status.id, trade_status.timestamp, trade_status.network, trade_status.tx_hash, trade_status.sent_beneficiary, trade_status.received_beneficiary, trade.type ` - ) - .append(having) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) -} - const getNFTCTE = (filters: SaleFilters) => { console.log('filters', filters) const FILTER_BY_CONTRACT_ADDRESS = filters.contractAddress ? SQL` contract_address = ${filters.contractAddress.toLowerCase()} ` : null @@ -293,14 +104,11 @@ const getNFTCTE = (filters: SaleFilters) => { export function getSalesQuery(filters: SaleFilters = {}) { const LEGACY_SALES = SQL`(`.append(getLegacySalesQuery(filters)).append(SQL` ) as legacy_sales `) - const TRADE_SALES = SQL`(`.append(getTradeSalesQuery(filters)).append(SQL` ) as trade_sales `) return getNFTCTE(filters).append( SQL`SELECT *, COUNT(*) OVER() as count` .append(SQL` FROM `) .append(LEGACY_SALES) - .append(SQL` NATURAL FULL OUTER JOIN `) - .append(TRADE_SALES) .append(getSalesSortByStatement(filters.sortBy)) .append(getSalesLimitAndOffsetStatement(filters)) )