使用Supabase和React Upsert对象数组仅插入首个元素的问题
问题:Supabase中实现NFT数组的Upsert功能
我是SQL新手,正在用Supabase开发Web应用,尝试将ownedNFTs对象数组存入数据库,但目前仅能插入数组的首个元素。目标是实现完整数组的upsert功能——检测到变化时将对应对象添加到数组中。
当前实现代码:
const nakedDB = () => { try { setLoading(true); ownedNFTs.forEach(async (nft) => { let { data, error, status } = await supabase.from("Users").upsert({ wallet_address: wallet?.accounts[0].address, NFTS: { name: nft?.metadata?.name || "", description: nft?.metadata?.description || "", image: nft?.metadata?.image || "", externalUrl: nft?.metadata?.external_url || "", contractAddress: nft?.contract.address || "", tokenId: nft?.id.tokenId || "", blockchain: "Ethereum", metaverse: nft?.metadata?.name.split(" ")[0] || "", metadata: nft?.metadata || "", symbol: nft?.contract.symbol || "", }, }).eq('wallet_address', wallet?.accounts[0].address); console.log(data) if (error && status !== 406) { throw error; } }); } catch (error) { console.table(error); } finally { setLoading(false); } };
我曾参考Supabase的批量upsert文档,但那是用于批量创建多行数据;也尝试过标准for循环和当前的forEach(),均未达到预期效果。
解决方案
核心问题分析
之前的代码失效,本质是因为Users表的NFTS字段不是数组类型(比如jsonb[]),每次upsert都用单个NFT对象覆盖字段值,加上forEach不等待异步操作完成,最终只保留了单个元素。
方案1:用PostgreSQL数组操作实现合并(推荐)
步骤1:确认/修改表字段类型
先确保Users表的NFTS字段是jsonb[]类型,若不是则执行SQL修改:
ALTER TABLE Users ALTER COLUMN NFTS TYPE jsonb[] USING NFTS::jsonb[];
步骤2:修改前端代码
无需循环单个NFT,直接将整个数组传入,结合PostgreSQL的数组合并逻辑完成upsert:
const nakedDB = async () => { try { setLoading(true); const walletAddress = wallet?.accounts[0].address; if (!walletAddress) return; // 转换ownedNFTs为符合数据库结构的数组 const nftsArray = ownedNFTs.map(nft => ({ name: nft?.metadata?.name || "", description: nft?.metadata?.description || "", image: nft?.metadata?.image || "", externalUrl: nft?.metadata?.external_url || "", contractAddress: nft?.contract.address || "", tokenId: nft?.id.tokenId || "", blockchain: "Ethereum", metaverse: nft?.metadata?.name?.split(" ")[0] || "", metadata: nft?.metadata || "", symbol: nft?.contract.symbol || "" })); // 执行upsert,合并数组 const { data, error, status } = await supabase .from("Users") .upsert( { wallet_address: walletAddress, NFTS: nftsArray }, { onConflict: "wallet_address", update: { // 合并现有数组与新数组,如需去重则添加array_distinct NFTS: "array_cat(NFTS, excluded.NFTS)" // 去重写法:NFTS: "array_distinct(array_cat(NFTS, excluded.NFTS))" } } ) .eq("wallet_address", walletAddress); if (error && status !== 406) throw error; console.log(data); } catch (error) { console.table(error); } finally { setLoading(false); } };
方案2:自定义函数实现精准去重(按NFT唯一标识)
如果需要根据contractAddress和tokenId判断NFT唯一性(而非整个对象完全一致),可以创建PostgreSQL自定义函数:
CREATE OR REPLACE FUNCTION merge_nfts(existing jsonb[], new_nfts jsonb[]) RETURNS jsonb[] AS $$ BEGIN FOREACH nft IN ARRAY new_nfts LOOP -- 仅添加数据库中不存在的NFT IF NOT EXISTS ( SELECT 1 FROM unnest(existing) e WHERE e->>'contractAddress' = nft->>'contractAddress' AND e->>'tokenId' = nft->>'tokenId' ) THEN existing = array_append(existing, nft); END IF; END LOOP; RETURN existing; END; $$ LANGUAGE plpgsql;
之后修改前端代码的update配置:
update: { NFTS: "merge_nfts(NFTS, excluded.NFTS)" }
内容的提问来源于stack exchange,提问作者AS11
相关产品推荐
相关产品推荐

