Firestore文档排序异常:先按未指定字段排序再按请求字段
问题描述
尝试使用以下查询语句按预订记录的from日期(Firestore中存储为Timestamp类型)对文档进行降序排序:
query(this.colRef,orderBy("from","desc"));
但结果不符合预期:显示的记录先按user字段排序,之后才按from日期排序,并非仅按指定的from字段排序。
前端展示代码(Angular PrimeNG)
<p-table [value]="bookings" dataKey="id" [responsive]="true" [scrollable]="true" *ngIf="globals.windowWidth > 640"> <ng-template pTemplate="caption"> <div class="table-header"> <p-dropdown [options]="bookingYears" (onChange)="loadBookings()" [(ngModel)]="selectedYear" optionLabel="name" optionValue="code" autoWidth="true" *ngIf="selectedYear!=undefined && !isShowingRequests"></p-dropdown> <button pButton pRipple type="button" style="float: right;" icon="pi pi-refresh" (click)="loadBookings()" *ngIf="loadedBookings"></button> <button icon="pi pi-spin pi-spinner" style="float: right;" class="p-button-rounded p-button-primary rightButton" pButton type="button" *ngIf="!loadedBookings"></button> <div></div> <br><br> </div> </ng-template> <ng-template pTemplate="header"> <tr> <th *ngIf="bookerID == undefined">Booker</th> <th>From</th> <th>To</th> <th>Price</th> <th>Status</th> <th></th> </tr> <tr *ngIf="bookings.length == 0 && loadedBookings"> <th colspan="6"> No Bookings found! </th> </tr> </ng-template> <ng-template pTemplate="body" let-booking > <tr> <td *ngIf="bookerID == undefined"> {{booking.user.forename}} {{booking.user.lastname}} </td> <td> {{booking.from.toLocaleDateString()}} </td> <td> {{booking.to.toLocaleDateString()}} </td> <td> {{bookingCalculationService.getTotalPrice(booking)}} € </td> <td > {{bookingsStatus[booking.status-1].name}} </td> <td style="text-align:center"><!--pi-window-maximize--> <button (click)="showBookingInfoDialog(booking)" icon="pi pi-window-maximize" pButton pRipple type="button" class="p-button-rounded p-button-outlined" title="Edit"></button><!----> </td> </tr> </ng-template>
TypeScript处理代码
getDocs(query) .then((snapchot) => { let counter = 0; if(snapchot.docs.length ==0 )this.loadedBookings = true; snapchot.docs.forEach((doc) => { this.fireStoreService.bookingConverter.fromFirestore(doc).then((booking: Booking) => { if(this.isShowingRequests){ if(this.authService.isLoggedInAndHost() && booking.status == 1)this.bookings.push(booking) } else if(this.isShowingBookingsAsHost){ if(this.authService.isLoggedInAndHost() && booking.status != 1)this.bookings.push(booking) } else{ if(this.authService.isLoggedIn())this.bookings.push(booking) } counter++; if(snapchot.docs.length == counter){ this.loadedBookings = true; } }); }); }) .catch((e)=>{ console.log(e) this.showToastMessage('error', 'Error', 'An error orccured!'); }) ;
问题原因
Firestore查询返回的快照本身是按from字段降序排序的,但代码中使用forEach遍历快照文档时,每个文档的fromFirestore转换是异步操作(返回Promise)。异步操作的完成时间不固定,导致bookings数组中元素的顺序是异步回调完成的顺序,而非快照的原始排序顺序,这就造成了看起来像是按user字段排序的错觉。
解决方案
改用Promise.all等待所有文档转换完成,严格保持快照的排序顺序,之后再根据条件过滤并赋值给bookings数组:
getDocs(query) .then(async (snapchot) => { if(snapchot.docs.length === 0) { this.loadedBookings = true; return; } // 收集所有转换Promise,严格保留快照的原始顺序 const bookingPromises = snapchot.docs.map(doc => this.fireStoreService.bookingConverter.fromFirestore(doc) ); // 等待所有转换完成,得到与查询结果顺序一致的booking数组 const allBookings = await Promise.all(bookingPromises); // 根据条件过滤出需要展示的记录 this.bookings = allBookings.filter(booking => { if(this.isShowingRequests){ return this.authService.isLoggedInAndHost() && booking.status === 1; } else if(this.isShowingBookingsAsHost){ return this.authService.isLoggedInAndHost() && booking.status !== 1; } else { return this.authService.isLoggedIn(); } }); this.loadedBookings = true; }) .catch((e)=>{ console.log(e); this.showToastMessage('error', 'Error', 'An error occurred!'); });
关键改进点
- 用
map替代forEach,将每个文档的转换操作转为Promise数组,严格保留快照的排序 - 通过
Promise.all等待所有异步转换完成,确保最终数组顺序与Firestore查询结果一致 - 统一过滤逻辑,直接生成符合条件的数组,避免逐个push导致的顺序混乱
内容的提问来源于stack exchange,提问作者ReneDeicker
相关产品推荐
相关产品推荐

