如何预加载数据以避免在 Angular 中多次读取 firebase
How to pre-load data to avoid multiple firebase reads in Angular
问题:每次用户导航到我的网络应用程序中的特定组件时,它都会从 Firebase Firestore 读取大量数据。这会导致对我的 Firestore 数据库进行数十万次不必要的读取。
问题:我如何预加载或“缓存”Firestore 数据,以便网络应用程序可以使用它不管何时任何用户loads/visits站点?
附加信息:
- 特定组件从包含大约 850 个文档的 Firestore 集合中加载所有文档。此集合中的数据变化不大,具体是包含城市子集合的名称列表。这些城市子集合中的每一个都包含此人居住过的地址的另一个集合。此组件使用动态搜索栏中的名称,每次用户转到该组件时都会重新加载。
- 此组件还读取并显示拥有最多地址的人数最多的
x
人。这导致更多的阅读,因为我使用相同的功能让所有人阅读所有人(然后排序和 return 顶部 x
数字)。
- 我使用 Angular 13,我的网站由 Firebase 托管。我不太熟悉一般的虚拟主机,所以我什至不知道我能“做”到什么程度。
尝试的解决方案:我考虑过让网络应用程序在初始加载时读取集合,然后在用户加载组件时用该数据填充。这将节省一些读取,因为每次用户单击特定组件时都不会读取集合,但它仍然会导致大量读取,因为每次用户刷新/重新访问该站点时都会读取数据。
目标:总的来说,我正在尝试找到一种解决方案来减少我的 Firestore 数据库上的读取次数。我对可能解决问题的其他解决方案持开放态度,即使我必须重新考虑我的方法或使用不同的资源。
代码:
firebase.service.ts:
export class FirebaseService {
private peoplePath = '/people';
constructor(private firestore: AngularFirestore) { }
getAllPeople(): Observable<Person[]> {
let colRef: AngularFirestoreCollection<any>;
colRef = this.firestore.collection(this.peoplePath);
return colRef.snapshotChanges().pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data() as any;
const id = a.payload.doc.id;
return { id, ...data };
}))
);
}
getPerson(personId: string) {
let itemDoc: AngularFirestoreDocument<Person>;
let path = this.peoplePath + "/" + personId;
itemDoc = this.firestore.doc<Person>(path);
return itemDoc.valueChanges();
}
getTopPeople(amount: number): Person[] {
let topPeople: Person[] = [];
this.getAllPeople().subscribe(aPeople => {
let people = aPeople as Person[];
people.sort((a,b) => (a.total_addresses < b.total_addresses) ? 1 : -1);
let count = 0;
for (let p of people) {
topPeople.push(p);
count++;
if (count == amount) {
break;
}
}
return topPeople;
});
return topPeople;
}
人-search.component.ts:
export class PersonSearchComponent implements OnInit {
term: string;
showSearch: boolean = false;
numOfPeople: number = 20;
numOfPeopleDisplay: number = 20;
constructor(private fbService: FirebaseService) { }
allPeople: Observable<Person[]> = this.fbService.getAllPeople();
topPeople: Person[] = this.fbService.getTopPeople(20);
ngOnInit(): void { }
getPeople() {
this.topPeople = this.fbService.getTopPeople(this.numOfPeople);
this.numOfPeopleDisplay = this.numOfPeople;
}
}
人-search.component.html:
<div>
<input name="search" type="text" (focus)="showSearch = true" placeholder="Search for people" [(ngModel)]="term">
<div class="personSearchResults" *ngIf="showSearch">
<table>
<tbody>
<tr *ngFor="let person of allPeople | async | personfilter:term">
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.first_name}}</a></td>
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.last_name}}</a></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<span>Enter any number: <input type="number" [(ngModel)]="numOfPeople"></span>
<button (click)="getPeople()">Sort</button>
<p>Top {{numOfPeopleDisplay}} people by number of addresses:</p>
</div>
<div>
<table>
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th>Number of addresses</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of topPeople; let i=index;">
<td>{{i + 1}}</td>
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.first_name}} {{person.last_name}}</a></td>
<td>{{person.total_addresses}}</td>
</tr>
</tbody>
</table>
</div>
如果需要任何其他信息,请告诉我。
您要查找的是 Firestore 的 data bundles, which (according to this page on serving bundled Firestore content from a CDN):
Cloud Firestore bundles allow you to assemble data bundles from common query results on the backend using the Firebase Admin SDK, and serve these pre-computed blobs cached on a CDN. This gives your users a much faster first load experience and reduces your Cloud Firestore query costs.
所以:
- 创建一个包含您希望在缓存中的文档的包
- 将该包存储在比 Firestore 的 API.
更便宜的地方
- 下载包并将其推送到客户端的本地缓存中。
- 然后就用普通的API读取文档,现在都是本地缓存的
问题:每次用户导航到我的网络应用程序中的特定组件时,它都会从 Firebase Firestore 读取大量数据。这会导致对我的 Firestore 数据库进行数十万次不必要的读取。
问题:我如何预加载或“缓存”Firestore 数据,以便网络应用程序可以使用它不管何时任何用户loads/visits站点?
附加信息:
- 特定组件从包含大约 850 个文档的 Firestore 集合中加载所有文档。此集合中的数据变化不大,具体是包含城市子集合的名称列表。这些城市子集合中的每一个都包含此人居住过的地址的另一个集合。此组件使用动态搜索栏中的名称,每次用户转到该组件时都会重新加载。
- 此组件还读取并显示拥有最多地址的人数最多的
x
人。这导致更多的阅读,因为我使用相同的功能让所有人阅读所有人(然后排序和 return 顶部x
数字)。 - 我使用 Angular 13,我的网站由 Firebase 托管。我不太熟悉一般的虚拟主机,所以我什至不知道我能“做”到什么程度。
尝试的解决方案:我考虑过让网络应用程序在初始加载时读取集合,然后在用户加载组件时用该数据填充。这将节省一些读取,因为每次用户单击特定组件时都不会读取集合,但它仍然会导致大量读取,因为每次用户刷新/重新访问该站点时都会读取数据。
目标:总的来说,我正在尝试找到一种解决方案来减少我的 Firestore 数据库上的读取次数。我对可能解决问题的其他解决方案持开放态度,即使我必须重新考虑我的方法或使用不同的资源。
代码:
firebase.service.ts:
export class FirebaseService {
private peoplePath = '/people';
constructor(private firestore: AngularFirestore) { }
getAllPeople(): Observable<Person[]> {
let colRef: AngularFirestoreCollection<any>;
colRef = this.firestore.collection(this.peoplePath);
return colRef.snapshotChanges().pipe(
map(actions => actions.map(a => {
const data = a.payload.doc.data() as any;
const id = a.payload.doc.id;
return { id, ...data };
}))
);
}
getPerson(personId: string) {
let itemDoc: AngularFirestoreDocument<Person>;
let path = this.peoplePath + "/" + personId;
itemDoc = this.firestore.doc<Person>(path);
return itemDoc.valueChanges();
}
getTopPeople(amount: number): Person[] {
let topPeople: Person[] = [];
this.getAllPeople().subscribe(aPeople => {
let people = aPeople as Person[];
people.sort((a,b) => (a.total_addresses < b.total_addresses) ? 1 : -1);
let count = 0;
for (let p of people) {
topPeople.push(p);
count++;
if (count == amount) {
break;
}
}
return topPeople;
});
return topPeople;
}
人-search.component.ts:
export class PersonSearchComponent implements OnInit {
term: string;
showSearch: boolean = false;
numOfPeople: number = 20;
numOfPeopleDisplay: number = 20;
constructor(private fbService: FirebaseService) { }
allPeople: Observable<Person[]> = this.fbService.getAllPeople();
topPeople: Person[] = this.fbService.getTopPeople(20);
ngOnInit(): void { }
getPeople() {
this.topPeople = this.fbService.getTopPeople(this.numOfPeople);
this.numOfPeopleDisplay = this.numOfPeople;
}
}
人-search.component.html:
<div>
<input name="search" type="text" (focus)="showSearch = true" placeholder="Search for people" [(ngModel)]="term">
<div class="personSearchResults" *ngIf="showSearch">
<table>
<tbody>
<tr *ngFor="let person of allPeople | async | personfilter:term">
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.first_name}}</a></td>
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.last_name}}</a></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<span>Enter any number: <input type="number" [(ngModel)]="numOfPeople"></span>
<button (click)="getPeople()">Sort</button>
<p>Top {{numOfPeopleDisplay}} people by number of addresses:</p>
</div>
<div>
<table>
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th>Number of addresses</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let person of topPeople; let i=index;">
<td>{{i + 1}}</td>
<td><a routerLink="/person-page/{{person.first_name}}_{{person.last_name}}">{{person.first_name}} {{person.last_name}}</a></td>
<td>{{person.total_addresses}}</td>
</tr>
</tbody>
</table>
</div>
如果需要任何其他信息,请告诉我。
您要查找的是 Firestore 的 data bundles, which (according to this page on serving bundled Firestore content from a CDN):
Cloud Firestore bundles allow you to assemble data bundles from common query results on the backend using the Firebase Admin SDK, and serve these pre-computed blobs cached on a CDN. This gives your users a much faster first load experience and reduces your Cloud Firestore query costs.
所以:
- 创建一个包含您希望在缓存中的文档的包
- 将该包存储在比 Firestore 的 API. 更便宜的地方
- 下载包并将其推送到客户端的本地缓存中。
- 然后就用普通的API读取文档,现在都是本地缓存的