Ionic3+Angularfire2数据库列表排序与多条件查询问题
嘿,刚好我对AngularFire2 5.0.0-rc.6这个版本比较熟悉,帮你一步步解决这两个问题:
1. 实现评分从高到低的降序排序
Firebase Realtime Database本身并没有提供直接的orderByChild降序方法,不过有两种实用的方案可以实现需求:
方案一:查询后在客户端反转数组
这是最直接的方法,先按默认的升序查询,然后利用RxJS的map操作符反转结果数组:
import { map } from 'rxjs/operators'; // 先按rating升序查询 this.restaurantsRef = afDb.list('/restaurants', ref => ref.orderByChild('rating')); // 获取数据后反转数组,得到降序结果 this.restaurants$ = this.restaurantsRef.valueChanges().pipe( map(restaurants => restaurants.reverse()) );
这种方法的优点是不需要修改数据库结构,适合快速实现;缺点是如果餐厅数据量很大,拉取全部数据再反转可能会有性能损耗。
方案二:存储反向数值字段
如果需要监听实时数据变化且追求更高性能,可以在存储餐厅数据时额外保存一个负数值的评分字段(比如negativeRating),然后对这个字段做升序查询,就等价于原评分的降序:
// 存储餐厅数据时,同时保存negativeRating const restaurantData = { name: 'XX餐厅', rating: 4.8, negativeRating: -4.8 // 原评分取反 }; afDb.object('/restaurants/' + restaurantId).set(restaurantData); // 查询时直接orderByChild('negativeRating'),得到的就是原评分从高到低的结果 this.restaurantsRef = afDb.list('/restaurants', ref => ref.orderByChild('negativeRating'));
这个方案的好处是数据库返回的直接就是降序结果,不需要客户端额外处理,适合数据量大或需要实时监听的场景;唯一需要注意的是要维护好negativeRating和rating的同步,更新评分时记得同时更新反向字段。
2. 多条件查询筛选餐厅
这里要注意:Firebase Realtime Database不支持同时调用多个orderBy*方法(比如同时按rating和category排序),所以需要根据你的筛选需求选择不同的方案:
场景一:先排序,再客户端筛选
如果你的筛选条件是简单的等值匹配(比如筛选某个分类的餐厅),可以先按rating排序,然后在客户端用RxJS的操作符过滤结果:
import { map } from 'rxjs/operators'; // 先获取降序的餐厅列表 const sortedRestaurants$ = this.restaurantsRef.valueChanges().pipe( map(restaurants => restaurants.reverse()) ); // 筛选分类为"中餐"的餐厅 this.filteredRestaurants$ = sortedRestaurants$.pipe( map(restaurants => restaurants.filter(restaurant => restaurant.category === '中餐')) );
这种方法适合数据量不大的场景,实现简单,但会拉取全部餐厅数据再筛选。
场景二:利用复合索引实现服务器端筛选
如果需要同时做范围查询(比如rating >= 4)和等值查询(比如category='中餐'),可以创建复合字段来实现服务器端的筛选,避免拉取无关数据:
// 存储餐厅数据时,添加复合字段(格式:[等值条件]_[范围条件]) const restaurantData = { name: 'XX中餐厅', rating: 4.8, category: '中餐', category_rating: '中餐_4.8' // 复合字段,把分类和评分拼接 }; afDb.object('/restaurants/' + restaurantId).set(restaurantData); // 查询时,按复合字段排序,并设置范围条件 this.restaurantsRef = afDb.list('/restaurants', ref => ref.orderByChild('category_rating') .startAt('中餐_4') // 匹配分类为中餐且评分>=4的餐厅 .endAt('中餐_\uf8ff') // \uf8ff是Unicode末尾字符,匹配所有以"中餐_"开头的字段 ); // 最后反转得到评分降序结果 this.filteredRestaurants$ = this.restaurantsRef.valueChanges().pipe(map(restaurants => restaurants.reverse()));
这种方案的核心是利用复合字段将多个条件合并成一个排序键,让数据库能直接筛选出符合条件的数据,性能更优,适合数据量大的场景。
内容的提问来源于stack exchange,提问作者Sireini

