diff --git a/src/ports/catalog/queries.ts b/src/ports/catalog/queries.ts index 3d87480..8bbafa3 100644 --- a/src/ports/catalog/queries.ts +++ b/src/ports/catalog/queries.ts @@ -114,11 +114,28 @@ const getLatestMetadataJoin = (filters: CatalogQueryFilters) => { const getLatestMetadataCTE = () => { return SQL`latest_metadata AS ( - SELECT DISTINCT ON (wearable_id) wearable_id as item_id, id AS latest_metadata_id, item_type, wearable_id, emote_id - FROM `.append(MARKETPLACE_SQUID_SCHEMA).append(SQL`.metadata - ORDER BY wearable_id DESC + SELECT DISTINCT ON (wearable_id) + CASE + WHEN m.network = 'ETHEREUM' + THEN w.collection || '-' || m.id -- Use collection + '-' + metadata.id for L1 items + ELSE m.wearable_id::text + END AS item_id, + m.id AS latest_metadata_id, + m.item_type, + m.wearable_id, + m.emote_id + FROM + ` + .append(MARKETPLACE_SQUID_SCHEMA) + .append( + SQL`.metadata as m + JOIN `.append(MARKETPLACE_SQUID_SCHEMA).append(SQL`.wearable AS w + ON w.id = m.wearable_id + ORDER BY + wearable_id DESC ) `) + ) } const getSearchCTEs = (filters: CatalogQueryFilters) => { @@ -642,7 +659,14 @@ export const getCollectionsItemsCatalogQueryWithTrades = (filters: CatalogQueryF items.network, offchain_orders.open_item_trade_id, offchain_orders.open_item_trade_price, - LEAST(items.first_listed_at, ROUND(EXTRACT(EPOCH FROM offchain_orders.min_item_created_at))) as first_listed_at, + ` + .append( + filters.isOnSale // When filtering for NOT on sale, calculating this from the offchain orders is very expensive, we just avoid it + ? SQL`LEAST(items.first_listed_at, ROUND(EXTRACT(EPOCH FROM offchain_orders.min_item_created_at))) as first_listed_at,` + : SQL`items.first_listed_at as first_listed_at,` + ) + .append( + SQL` items.urn, CASE WHEN offchain_orders.min_order_amount_received IS NULL AND nfts_with_orders.min_price IS NULL THEN NULL @@ -661,7 +685,7 @@ export const getCollectionsItemsCatalogQueryWithTrades = (filters: CatalogQueryF ROUND(EXTRACT(EPOCH FROM offchain_orders.max_created_at)), nfts_with_orders.max_order_created_at ) AS max_order_created_at,` - + ) .append(filters.isOnSale === false ? SQL`nfts.owners_count,` : SQL``) .append( ` @@ -697,7 +721,7 @@ export const getCollectionsItemsCatalogQueryWithTrades = (filters: CatalogQueryF JOIN `.append(MARKETPLACE_SQUID_SCHEMA).append(SQL`.nft ON orders.nft_id = nft.id WHERE orders.status = 'open' - AND orders.expires_at_normalized > NOW() AND nft.owner_address = orders.owner`) + AND orders.expires_at_normalized > NOW()`) ) .append(getOrderRangePriceWhere(filters)) .append( diff --git a/src/ports/nfts/ensQueries.ts b/src/ports/nfts/ensQueries.ts index 1b74caa..0c4cafe 100644 --- a/src/ports/nfts/ensQueries.ts +++ b/src/ports/nfts/ensQueries.ts @@ -9,10 +9,9 @@ function geENSWhereStatement(nftFilters: GetNFTsFilters): SQLStatement { return SQL`` } - // Keep only filters that need JOINed tables - const FILTER_BY_OWNER = nftFilters.owner - ? SQL` nft.owner_id IN (SELECT id FROM squid_marketplace.account WHERE address = ${nftFilters.owner.toLocaleLowerCase()}) ` - : null + const ownerEthereumAddress = nftFilters.owner ? `${nftFilters.owner.toLocaleLowerCase()}-ETHEREUM` : null + const FILTER_BY_OWNER = nftFilters.owner ? SQL` owner_id = ${ownerEthereumAddress}` : null + const FILTER_BY_TOKEN_ID = nftFilters.tokenId ? SQL` token_id = ${nftFilters.tokenId} ` : null const FILTER_BY_SEARCH = nftFilters.search ? SQL` search_text % ${nftFilters.search} ` : null const FILTER_BY_IDS = nftFilters.ids?.length ? SQL` id = ANY (${nftFilters.ids}) ` : null @@ -41,7 +40,6 @@ export function getENSs(nftFilters: GetNFTsFilters): SQLStatement { .append(ids ? SQL` AND id = ANY(${ids}) ` : SQL``) .append( SQL` - ORDER BY created_at ) ` .append(getTradesCTE(nftFilters, false)) diff --git a/src/ports/nfts/landQueries.ts b/src/ports/nfts/landQueries.ts index e0db90c..d06cf56 100644 --- a/src/ports/nfts/landQueries.ts +++ b/src/ports/nfts/landQueries.ts @@ -35,11 +35,13 @@ function getAllLANDWheres(filters: GetNFTsFilters & { rentalAssetsIds?: string[] search, rentalAssetsIds } = filters + const ownerEthereumAddress = owner ? `${owner.toLocaleLowerCase()}-ETHEREUM` : null + const ownerPolygonAddress = owner ? `${owner.toLocaleLowerCase()}-POLYGON` : null const FILTER_BY_OWNER = owner && rentalAssetsIds?.length - ? SQL` (nft.owner_id IN (SELECT id FROM squid_marketplace.account WHERE address = ${owner.toLowerCase()}) OR nft.id = ANY(${rentalAssetsIds})) ` + ? SQL` ((nft.owner_id = ${ownerEthereumAddress} OR nft.owner_id = ${ownerPolygonAddress}) OR nft.id = ANY(${rentalAssetsIds})) ` : owner && !rentalAssetsIds?.length - ? SQL` nft.owner_id IN (SELECT id FROM squid_marketplace.account WHERE address = ${owner.toLowerCase()}) ` + ? SQL` (nft.owner_id = ${ownerEthereumAddress} OR nft.owner_id = ${ownerPolygonAddress}) ` : null const FILTER_BY_MIN_PRICE = minPrice ? SQL` (nft.search_order_price >= ${minPrice} OR (trades.assets -> 'received' ->> 'amount')::numeric(78) >= ${minPrice})` diff --git a/src/ports/nfts/queries.ts b/src/ports/nfts/queries.ts index 1786dfb..5045f7b 100644 --- a/src/ports/nfts/queries.ts +++ b/src/ports/nfts/queries.ts @@ -53,10 +53,9 @@ function getRarityWhereStatement(rarities?: Rarity[]): SQLStatement | null { } function getFilteredNFTCTE(nftFilters: GetNFTsFilters, uncapped = false): SQLStatement { - // Define each filter condition separately - const FILTER_BY_OWNER = nftFilters.owner - ? SQL` owner_id IN (SELECT id FROM squid_marketplace.account WHERE address = ${nftFilters.owner.toLocaleLowerCase()}) ` - : null + const ownerEthereumAddress = nftFilters.owner ? `${nftFilters.owner.toLocaleLowerCase()}-ETHEREUM` : null + const ownerPolygonAddress = nftFilters.owner ? `${nftFilters.owner.toLocaleLowerCase()}-POLYGON` : null + const FILTER_BY_OWNER = nftFilters.owner ? SQL` owner_id = ${ownerEthereumAddress} OR owner_id = ${ownerPolygonAddress} ` : null const FILTER_BY_CATEGORY = nftFilters.category ? SQL` category = ${nftFilters.category.toLocaleLowerCase()} ` : null const FILTER_BY_TOKEN_ID = nftFilters.tokenId ? SQL` token_id = ${nftFilters.tokenId} ` : null const FILTER_BY_ITEM_ID = nftFilters.itemId ? SQL` LOWER(item_id) = LOWER(${nftFilters.itemId}) ` : null @@ -220,6 +219,13 @@ export function getTradesCTE(filters: GetNFTsFilters, addHavingStatement = true) 'issued_id', assets_with_values.issued_id, 'nft_name', assets_with_values.nft_name )) as assets, + /* CASE #1: Single NFT (if you only expect ONE 'sent' per trade) */ + MAX(assets_with_values.contract_address) + FILTER (WHERE assets_with_values.direction = 'sent') + AS sent_contract_address, + MAX(assets_with_values.token_id) + FILTER (WHERE assets_with_values.direction = 'sent') + AS sent_token_id, CASE WHEN COUNT(CASE WHEN trade_status.action = 'cancelled' THEN 1 END) > 0 THEN 'cancelled' WHEN ( @@ -389,7 +395,7 @@ export function getNFTsQuery(nftFilters: GetNFTsFilters & { rentalAssetsIds?: st LEFT JOIN squid_marketplace.ens ens ON ens.id = nft.ens_id LEFT JOIN squid_marketplace.account account ON nft.owner_id = account.id LEFT JOIN squid_marketplace.item item ON item.id = nft.item_id - LEFT JOIN trades ON (trades.assets -> 'sent' ->> 'token_id')::numeric = nft.token_id AND trades.assets -> 'sent' ->> 'contract_address' = nft.contract_address AND trades.status = 'open' AND trades.signer = account.address + LEFT JOIN trades ON trades.sent_contract_address = nft.contract_address AND trades.sent_token_id::numeric = nft.token_id AND trades.status = 'open' AND trades.signer = account.address ` .append(getNFTWhereStatement(nftFilters)) .append(getMainQuerySortByStatement(nftFilters.sortBy)) @@ -455,9 +461,9 @@ function getNFTWhereStatement(nftFilters: GetNFTsFilters): SQLStatement { } function getRecentlyListedNFTsCTE(nftFilters: GetNFTsFilters): SQLStatement { - const FILTER_BY_OWNER = nftFilters.owner - ? SQL` owner_id IN (SELECT id FROM squid_marketplace.account WHERE address = ${nftFilters.owner.toLocaleLowerCase()}) ` - : null + const ownerEthereumAddress = nftFilters.owner ? `${nftFilters.owner.toLocaleLowerCase()}-ETHEREUM` : null + const ownerPolygonAddress = nftFilters.owner ? `${nftFilters.owner.toLocaleLowerCase()}-POLYGON` : null + const FILTER_BY_OWNER = nftFilters.owner ? SQL` owner_id = ${ownerEthereumAddress} OR owner_id = ${ownerPolygonAddress} ` : null const FILTER_BY_CATEGORY = nftFilters.category ? SQL` category = ${nftFilters.category.toLowerCase()} ` : null const FILTER_BY_TOKEN_ID = nftFilters.tokenId ? SQL` token_id = ${nftFilters.tokenId} ` : null const FILTER_BY_ITEM_ID = nftFilters.itemId ? SQL` LOWER(item_id) = LOWER(${nftFilters.itemId}) ` : null diff --git a/src/ports/orders/queries.ts b/src/ports/orders/queries.ts index f52bd92..4a34f62 100644 --- a/src/ports/orders/queries.ts +++ b/src/ports/orders/queries.ts @@ -109,7 +109,7 @@ export function getLegacyOrdersQuery(): string { ord.expires_at, ord.network FROM squid_marketplace."order" ord - JOIN squid_marketplace."nft" nft ON ord.nft_id = nft.id AND nft.owner_address = ord.owner` + JOIN squid_marketplace."nft" nft ON ord.nft_id = nft.id` } export interface OrderQueries {