Swift Firestore:如何获取集合中的随机文档
对于我的应用程序来说,能够从firebase中的一个集合中随机选择多个文档是至关重要的 因为Firebase(据我所知)没有内置的本机函数来实现这样的查询,所以我的第一个想法是使用查询游标来选择随机的开始和结束索引,前提是我拥有集合中的文档数 这种方法会起作用,但只能以有限的方式起作用,因为每次都会按顺序将每个文档与其相邻文档一起提供;然而,如果我能够通过父集合中的索引来选择文档,我可以实现随机文档查询,但问题是我找不到任何文档来描述如何实现这一点,或者即使您能够实现这一点Swift Firestore:如何获取集合中的随机文档,swift,database,firebase,data-modeling,google-cloud-firestore,Swift,Database,Firebase,Data Modeling,Google Cloud Firestore,对于我的应用程序来说,能够从firebase中的一个集合中随机选择多个文档是至关重要的 因为Firebase(据我所知)没有内置的本机函数来实现这样的查询,所以我的第一个想法是使用查询游标来选择随机的开始和结束索引,前提是我拥有集合中的文档数 这种方法会起作用,但只能以有限的方式起作用,因为每次都会按顺序将每个文档与其相邻文档一起提供;然而,如果我能够通过父集合中的索引来选择文档,我可以实现随机文档查询,但问题是我找不到任何文档来描述如何实现这一点,或者即使您能够实现这一点 这是我想做的,考虑下
这是我想做的,考虑下面的FixStand模式:
root/
posts/
docA
docB
docC
docD
然后在我的客户机(我在Swift环境中)中,我想编写一个查询来实现这一点:
db.collection("posts")[0, 1, 3] // would return: docA, docB, docD
有没有办法让我做点类似的事情?或者,是否有其他方法可以以类似的方式选择随机文档
请提供帮助。使用随机生成的索引和简单查询,您可以从Cloud Firestore中的集合或集合组中随机选择文档 此答案分为4个部分,每个部分有不同的选项:
\uuuu name\uuuu
字段,后面提到的“低值”是一个空字符串。这是迄今为止生成随机索引最简单的方法,无论使用何种语言和平台都可以使用
默认情况下,文档名(\uuuuuu name\uuuuu
)仅按升序编制索引,并且除了删除和重新创建之外,您也无法重命名现有文档。如果您需要这两种方法中的任何一种,您仍然可以使用此方法,只需将自动id存储为名为random
的实际字段,而不是为此而重载文档名称
随机整数版本
编写文档时,首先在有界范围内生成一个随机整数,并将其设置为名为random
的字段。根据预期的文档数量,可以使用不同的有界范围来节省空间或降低碰撞风险(这会降低此技术的有效性)
你应该考虑你需要哪些语言,因为会有不同的考虑。虽然Swift很简单,但JavaScript显然可以解决以下问题:
- 32位整数:适用于小型(~10K)数据集
- 64位整数:大型数据集(注意:JavaScript本机不支持,)
random
表示。我们将使用此值在索引上查找一个随机点
环绕
现在您有了一个随机值,可以查询单个文档:
let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
.order(by: "random")
.limit(to: 1)
检查是否已返回文档。如果没有,请再次查询,但对随机索引使用“低值”。例如,如果使用随机整数,则低值
为0
:
let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: lowValue)
.order(by: "random")
.limit(to: 1)
只要您有一个文档,就可以保证至少返回一个文档
getDocumentRandomlyParent(): Observable<any> {
return this.getDocumentRandomlyChild()
.pipe(
expand((document: any) => document === null ? this.getDocumentRandomlyChild() : EMPTY),
);
}
getDocumentRandomlyChild(): Observable<any> {
const random = this.afs.createId();
return this.afs
.collection('my_collection', ref =>
ref
.where('random_identifier', '>', random)
.limit(1))
.valueChanges()
.pipe(
map((documentArray: any[]) => {
if (documentArray && documentArray.length) {
return documentArray[0];
} else {
return null;
}
}),
);
}
private val firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
private var usersReference = firestore.collection("users")
val rnds = (0..20001).random()
usersReference.whereGreaterThanOrEqualTo("random",rnds).limit(1).get().addOnSuccessListener {
if (it.size() > 0) {
for (doc in it) {
Log.d("found", doc.toString())
}
} else {
usersReference.whereLessThan("random", rnds).limit(1).get().addOnSuccessListener {
for (doc in it) {
Log.d("found", doc.toString())
}
}
}
}
双向的
环绕方法易于实现,允许您在只启用升序索引的情况下优化存储。一个不利因素是价值观可能受到不公平的保护。例如,如果10K中的前3个文档(A、B、C)的随机索引值为A:409496、B:436496、C:818992,则A和C被选中的几率仅为1/10K,而B被A的接近度有效屏蔽,并且只有大约1/160K的几率
您可以在
=
和之间进行随机选择,而不是在单个方向上进行查询并在未找到值时进行换行,我有一种方法可以在Firebase Firestore中随机获取列表文档,这非常简单。当我在Firestore上传数据时,我创建了一个字段名“position”,其随机值为1到1百万。当我从Fire store获取数据时,我将按字段“位置”设置顺序并更新其值,许多用户加载数据和数据始终会更新,并且将是随机值。对于使用Angular+Firestore的用户,基于@Dan McGrath技术,这里是代码片段
下面的代码段返回1个文档
getDocumentRandomlyParent(): Observable<any> {
return this.getDocumentRandomlyChild()
.pipe(
expand((document: any) => document === null ? this.getDocumentRandomlyChild() : EMPTY),
);
}
getDocumentRandomlyChild(): Observable<any> {
const random = this.afs.createId();
return this.afs
.collection('my_collection', ref =>
ref
.where('random_identifier', '>', random)
.limit(1))
.valueChanges()
.pipe(
map((documentArray: any[]) => {
if (documentArray && documentArray.length) {
return documentArray[0];
} else {
return null;
}
}),
);
}
private val firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
private var usersReference = firestore.collection("users")
val rnds = (0..20001).random()
usersReference.whereGreaterThanOrEqualTo("random",rnds).limit(1).get().addOnSuccessListener {
if (it.size() > 0) {
for (doc in it) {
Log.d("found", doc.toString())
}
} else {
usersReference.whereLessThan("random", rnds).limit(1).get().addOnSuccessListener {
for (doc in it) {
Log.d("found", doc.toString())
}
}
}
}
发布此消息以帮助将来遇到此问题的任何人
如果您使用的是自动ID,您可以生成一个新的自动ID并查询最接近的自动ID,如中所述
我最近创建了一个随机报价api,需要从firestore集合中获取随机报价。
这就是我解决公关问题的方法
var db = admin.firestore();
var quotes = db.collection("quotes");
var key = quotes.doc().id;
quotes.where(admin.firestore.FieldPath.documentId(), '>=', key).limit(1).get()
.then(snapshot => {
if(snapshot.size > 0) {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
}
else {
var quote = quotes.where(admin.firestore.FieldPath.documentId(), '<', key).limit(1).get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
})
.catch(err => {
console.log('Error getting documents', err);
});
.where(admin.firestore.FieldPath.documentId(), '>', key)
import { Component, OnInit } from '@angular/core';
import { Observable, merge, pipe } from 'rxjs';
import { map, switchMap, filter, take } from 'rxjs/operators';
import { AngularFirestore, QuerySnapshot } from '@angular/fire/firestore';
@Component({
selector: 'pp-random',
templateUrl: './random.component.html',
styleUrls: ['./random.component.scss']
})
export class RandomComponent implements OnInit {
constructor(
public afs: AngularFirestore,
) { }
ngOnInit() {
}
public buttonClicked(): void {
this.getRandom().pipe(take(1)).subscribe();
}
public getRandom(): Observable<any[]> {
const randomNumber = this.getRandomNumber();
const request$ = this.afs.collection('your-collection', ref => ref.where('random', '>=', randomNumber).orderBy('random').limit(1)).get();
const retryRequest$ = this.afs.collection('your-collection', ref => ref.where('random', '<=', randomNumber).orderBy('random', 'desc').limit(1)).get();
const docMap = pipe(
map((docs: QuerySnapshot<any>) => {
return docs.docs.map(e => {
return {
id: e.id,
...e.data()
} as any;
});
})
);
const random$ = request$.pipe(docMap).pipe(filter(x => x !== undefined && x[0] !== undefined));
const retry$ = request$.pipe(docMap).pipe(
filter(x => x === undefined || x[0] === undefined),
switchMap(() => retryRequest$),
docMap
);
return merge(random$, retry$);
}
public getRandomNumber(): number {
const min = Math.ceil(Number.MIN_VALUE);
const max = Math.ceil(Number.MAX_VALUE);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
new Promise<Timeline | undefined>(async (resolve, reject) => {
try {
let randomTimeline: Timeline | undefined;
let maxCounter = 5;
do {
const randomId = this.afs.createId(); // AngularFirestore
const direction = getRandomIntInclusive(1, 10) <= 5;
// The firestore id is saved with your model as an "id" property.
let list = await this.list(ref => ref
.where('id', direction ? '>=' : '<=', randomId)
.orderBy('id', direction ? 'asc' : 'desc')
.limit(10)
).pipe(take(1)).toPromise();
// app specific filtering
list = list.filter(x => notThisId !== x.id && x.mediaCounter > 5);
if (list.length) {
randomTimeline = list[getRandomIntInclusive(0, list.length - 1)];
}
} while (!randomTimeline && maxCounter-- >= 0);
resolve(randomTimeline);
} catch (err) {
reject(err);
}
})
"field1" : "value1"
"field2" : "value2"
...
"random" : 13442 //this is the random number i generated upon creating document
private val firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
private var usersReference = firestore.collection("users")
val rnds = (0..20001).random()
usersReference.whereGreaterThanOrEqualTo("random",rnds).limit(1).get().addOnSuccessListener {
if (it.size() > 0) {
for (doc in it) {
Log.d("found", doc.toString())
}
} else {
usersReference.whereLessThan("random", rnds).limit(1).get().addOnSuccessListener {
for (doc in it) {
Log.d("found", doc.toString())
}
}
}
}
let totalDoc = db.collection("stat").get().then(snap=>snap.size)
let randomID = [ ]
while(randomID.length < 20) {
const randNo = Math.floor(Math.random() * totalDoc) + 1;
if(randomID.indexOf(randNo) === -1) randomID.push(randNo);
}
const randomDocs = randomID.map(id => {
db.collection("posts").doc(id).get()
.then(doc => {
if (doc.exists) return doc.data()
})
.catch(error => {
console.log("Error getting document:", error);
});
})
FirebaseFirestore db;
void Start()
{
db = FirebaseFirestore.DefaultInstance;
}
public void GetRandomDocument()
{
Query query1 = db.Collection("Sports").WhereGreaterThanOrEqualTo(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);
Query query2 = db.Collection("Sports").WhereLessThan(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);
query1.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask1) =>
{
if(querySnapshotTask1.Result.Count > 0)
{
foreach (DocumentSnapshot documentSnapshot in querySnapshotTask1.Result.Documents)
{
Debug.Log("Random ID: "+documentSnapshot.Id);
}
} else
{
query2.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask2) =>
{
foreach (DocumentSnapshot documentSnapshot in querySnapshotTask2.Result.Documents)
{
Debug.Log("Random ID: " + documentSnapshot.Id);
}
});
}
});
}
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: lowValue).where("__name__", isNotEqualTo:"PreviiousId")
.order(by: "random")
.limit(to: 1)