Database 为我们的内容应用程序优化Firestore查询

Database 为我们的内容应用程序优化Firestore查询,database,firebase,google-cloud-firestore,database-design,denormalization,Database,Firebase,Google Cloud Firestore,Database Design,Denormalization,我们正在使用Firestore构建内容应用程序。 基本要求是有一个主集合,比如说“内容”。文档的数量可能达到1000个 content1, content2, content3 ... content9999 我们希望为我们的用户提供此集合中的内容,确保他们不会看到相同的内容两次,并且每次他们在应用程序中都会看到新内容。 同时,我们不希望向每个用户提供相同的内容序列。一些随机化会很好 user1: content9, content123, content17, content33, cont

我们正在使用Firestore构建内容应用程序。 基本要求是有一个主集合,比如说“内容”。文档的数量可能达到1000个

content1, content2, content3 ... content9999
我们希望为我们的用户提供此集合中的内容,确保他们不会看到相同的内容两次,并且每次他们在应用程序中都会看到新内容。 同时,我们不希望向每个用户提供相同的内容序列。一些随机化会很好

user1: content9, content123, content17, content33, content902 .. and so on
user2: content854, content79, content190, content567 ... and so on
我一直在绞尽脑汁想,在不复制主收藏的情况下,我们如何才能实现这个解决方案。复制主收藏会非常昂贵,但可以完成这项工作


此外,我们如何才能编写经济高效且性能优化的查询,尤其是当我们希望在这些内容片段的序列中保持随机性时?

以下是我的建议。请将其视为伪代码,因为我没有运行它

如果内容文档ID不可预见 您必须存储和维护哪些用户看到了哪些内容,例如在集合中:
/seed/uid\u contentId

查看从集合中获取随机文档的巧妙方法。您需要存储集合的大小,可能是另一个集合中的文档。因此,以下是您如何做到这一点:

const snapshot = await firestore.doc(`/userSeen/${uid}`).get(); // do it only once
const alreadySeen = snapshot.exists ? snapshot.data.contents : [];

async function getContent(uid) {
  for (let trials = 0; trials < 10; trials++) { // limit the cost
    const startAt = Math.random() * contentCollectionSize;
    const snapshot = await firestore.collection("/contents").startAt(startAt).limit(1).get();
    const document = snapshot.empty ? null : snapshot.docs[0]; // a random content

    if(document.exists && !alreadySeen.includes(document.id)) {
      alreadySeen.push(document.id);
      await firestore.doc(`/userSeen/${uid}`).set({contents: arrayUnion(document.id)}); // mark it as seen
      return document;
    }
  }

  return null;
}
const snapshot=wait firestore.doc(`/userSeen/${uid}`)。get();//只做一次
const alreadySeen=snapshot.exists?snapshot.data.contents:[];
异步函数getContent(uid){
对于(让试验=0;试验<10;试验++){//限制成本
const startAt=Math.random()*contentCollectionSize;
const snapshot=wait firestore.collection(“/contents”).startAt(startAt.limit(1.get();
const document=snapshot.empty?null:snapshot.docs[0];//随机内容
if(document.exists&!alreadySeen.includes(document.id)){
alreadySeen.push(document.id);
wait firestore.doc(`/userSeen/${uid}`).set({contents:arrayUnion(document.id)});//将其标记为seen
归还文件;
}
}
返回null;
}
在这里,您可能需要对Firestore进行多次查询(上限为10以限制成本),因为您无法在客户端计算内容文档ID

如果内容文档ID遵循简单的模式:1、2、3。。。 为了节省成本和性能,您应该将每个用户看到的所有内容存储在单个文档中(限制为1MB,即超过250000个整数!)。然后每个用户下载此文档一次,并在客户端检查是否已经看到随机内容

const snapshot = await firestore.doc(`/userSeen/${uid}`).get(); // do it only once
const alreadySeen = snapshot.exists ? snapshot.data.contents : [];


async function getContent(uid) {
  let idx = Math.random() * contentCollectionSize;

  for (let trials = 0; trials < contentCollectionSize; trials++) { 
    idx = idx + 1 < contentCollectionSize ? idx + 1 : 0;

    if(alreadySeen.includes(idx)) continue; // this shortcut reduces the number of Firestore queries

    const document = await firestore.doc(`/contents/${idx}`).get();

    if(document.exists){
      alreadySeen.push(idx);
      await firestore.doc(`/userSeen/${uid}`).set({contents: arrayUnion(idx)}); // mark it as seen
      return document;
    }
  }

  return null;
}
const snapshot=wait firestore.doc(`/userSeen/${uid}`)。get();//只做一次
const alreadySeen=snapshot.exists?snapshot.data.contents:[];
异步函数getContent(uid){
让idx=Math.random()*contentCollectionSize;
for(让trials=0;trials

如您所见,如果您对内容使用可预见的文档ID,这将便宜得多。但也许有人会有更好的主意。

这是我的建议。请将其视为伪代码,因为我没有运行它

如果内容文档ID不可预见 您必须存储和维护哪些用户看到了哪些内容,例如在集合中:
/seed/uid\u contentId

查看从集合中获取随机文档的巧妙方法。您需要存储集合的大小,可能是另一个集合中的文档。因此,以下是您如何做到这一点:

const snapshot = await firestore.doc(`/userSeen/${uid}`).get(); // do it only once
const alreadySeen = snapshot.exists ? snapshot.data.contents : [];

async function getContent(uid) {
  for (let trials = 0; trials < 10; trials++) { // limit the cost
    const startAt = Math.random() * contentCollectionSize;
    const snapshot = await firestore.collection("/contents").startAt(startAt).limit(1).get();
    const document = snapshot.empty ? null : snapshot.docs[0]; // a random content

    if(document.exists && !alreadySeen.includes(document.id)) {
      alreadySeen.push(document.id);
      await firestore.doc(`/userSeen/${uid}`).set({contents: arrayUnion(document.id)}); // mark it as seen
      return document;
    }
  }

  return null;
}
const snapshot=wait firestore.doc(`/userSeen/${uid}`)。get();//只做一次
const alreadySeen=snapshot.exists?snapshot.data.contents:[];
异步函数getContent(uid){
对于(让试验=0;试验<10;试验++){//限制成本
const startAt=Math.random()*contentCollectionSize;
const snapshot=wait firestore.collection(“/contents”).startAt(startAt.limit(1.get();
const document=snapshot.empty?null:snapshot.docs[0];//随机内容
if(document.exists&!alreadySeen.includes(document.id)){
alreadySeen.push(document.id);
wait firestore.doc(`/userSeen/${uid}`).set({contents:arrayUnion(document.id)});//将其标记为seen
归还文件;
}
}
返回null;
}
在这里,您可能需要对Firestore进行多次查询(上限为10以限制成本),因为您无法在客户端计算内容文档ID

如果内容文档ID遵循简单的模式:1、2、3。。。 为了节省成本和性能,您应该将每个用户看到的所有内容存储在单个文档中(限制为1MB,即超过250000个整数!)。然后每个用户下载此文档一次,并在客户端检查是否已经看到随机内容

const snapshot = await firestore.doc(`/userSeen/${uid}`).get(); // do it only once
const alreadySeen = snapshot.exists ? snapshot.data.contents : [];


async function getContent(uid) {
  let idx = Math.random() * contentCollectionSize;

  for (let trials = 0; trials < contentCollectionSize; trials++) { 
    idx = idx + 1 < contentCollectionSize ? idx + 1 : 0;

    if(alreadySeen.includes(idx)) continue; // this shortcut reduces the number of Firestore queries

    const document = await firestore.doc(`/contents/${idx}`).get();

    if(document.exists){
      alreadySeen.push(idx);
      await firestore.doc(`/userSeen/${uid}`).set({contents: arrayUnion(idx)}); // mark it as seen
      return document;
    }
  }

  return null;
}
const snapshot=wait firestore.doc(`/userSeen/${uid}`)。get();//只做一次
const alreadySeen=snapshot.exists?snapshot.data.contents:[];
异步函数getContent(uid){
让idx=Math.random()*contentCollectionSize;
for(让trials=0;trials