Firebase查询优化:如何按ID数组获取指定商品数据
解决AngularFire + Firebase实时DB获取用户关注商品的问题
嘿,很高兴帮你梳理这个问题!首先得说你的思路方向是对的——只拉取用户关注的商品确实能大幅减少数据消耗,完全没必要加载全部3000个商品。下面给你具体的解决方案和结构优化建议:
一、核心解决方案:批量获取单个商品节点
Firebase实时DB没有直接的"多ID匹配"查询,但我们可以利用并行请求单个商品节点的方式实现需求,结合RxJS的forkJoin就能轻松合并结果,正好适配你的NgRx状态规范化需求。
代码示例
假设你已经拿到当前用户的waresIds数组,用以下代码就能精准获取对应商品:
import { forkJoin } from 'rxjs'; import { AngularFireDatabase } from '@angular/fire/compat/database'; // 先获取当前用户的关注商品ID列表(从users节点拉取) this.db.object(`users/${currentUserId}`).valueChanges().subscribe(userData => { const userWaresIds = userData['waresIds'] || Object.keys(userData['wares']); // 为每个ID创建单独的商品查询Observable const wareObservables = userWaresIds.map(id => this.db.object(`warehouse/wares/${id}`).valueChanges() ); // 并行发起请求,合并结果为规范化对象 forkJoin(wareObservables).subscribe(waresArray => { const normalizedWares = userWaresIds.reduce((acc, id, index) => { acc[id] = waresArray[index]; return acc; }, {} as { [key: string]: Ware }); // 这里把normalizedWares dispatch到NgRx store即可 this.store.dispatch(loadUserWaresSuccess({ entities: normalizedWares, ids: userWaresIds })); }); });
这种方式的优势:
- 只拉取用户关注的商品,数据量骤减
- 每个商品的
valueChanges()会实时监听价格更新,完美满足你5分钟刷新的需求 - 结果天然适配NgRx的规范化状态结构(
entities对象+ids数组)
二、数据结构优化建议
你的用户数据结构整体没问题,但可以做一点冗余优化:
当前你同时存储了
wares对象(带order)和waresIds数组,其实可以去掉waresIds数组,通过Object.entries(user.wares)结合order排序来生成有序ID列表,减少维护成本:
// 从wares对象生成有序的ID列表 const sortedWaresIds = Object.entries(userData['wares']) .sort(([,a], [,b]) => a.order - b.order) .map(([id]) => id);
这样既保留了用户自定义的商品顺序,又不用额外维护waresIds数组,避免数据不一致的风险。
三、关于NgRx的补充提示
在NgRx里推荐用Effect来处理这个异步请求逻辑,避免把订阅逻辑写在组件里:
// 简化的Effect示例 loadUserWares$ = createEffect(() => this.actions$.pipe( ofType(loadUserWares), withLatestFrom(this.store.select(selectCurrentUser)), switchMap(([_, user]) => { return this.db.object(`users/${user.uid}`).valueChanges().pipe( switchMap(userData => { const sortedWaresIds = Object.entries(userData['wares']) .sort(([,a], [,b]) => a.order - b.order) .map(([id]) => id); const wareObservables = sortedWaresIds.map(id => this.db.object(`warehouse/wares/${id}`).valueChanges() ); return forkJoin(wareObservables).pipe( map(waresArray => { const entities = sortedWaresIds.reduce((acc, id, idx) => { acc[id] = waresArray[idx]; return acc; }, {} as { [key: string]: Ware }); return loadUserWaresSuccess({ entities, ids: sortedWaresIds }); }) ); }) ); }) ) );
内容的提问来源于stack exchange,提问作者Jaroslaw K.
相关产品推荐
相关产品推荐

