简体   繁体   English

如何获取 Firebase Cloud Firestore 中存在/不存在特定字段的文档?

[英]How do I get documents where a specific field exists/does not exists in Firebase Cloud Firestore?

In Firebase Cloud Firestore, I have "user_goals" in collections and goals may be a predefined goal (master_id: "XXXX") or a custom goal (no "master_id" key)在 Firebase Cloud Firestore 中,我在集合中有“user_goals”,目标可能是预定义的目标(master_id:“XXXX”)或自定义目标(没有“master_id”键)

In JavaScript, I need to write two functions, one to get all predefined goals and other to get all custom goals.在 JavaScript 中,我需要编写两个函数,一个用于获取所有预定义目标,另一个用于获取所有自定义目标。

I have got some workaround to get custom goals by setting "master_id" as "" empty string and able to get as below:我有一些解决方法可以通过将“master_id”设置为“”空字符串来获得自定义目标,并且能够获得如下:

db.collection('user_goals')
    .where('challenge_id', '==', '')  // workaround works
    .get()

Still this is not the correct way, I continued to use this for predefined goals where it has a "master_id" as below仍然这不是正确的方法,我继续将其用于具有“master_id”的预定义目标,如下所示

db.collection('user_goals')
    .where('challenge_id', '<', '')  // this workaround
    .where('challenge_id', '>', '')  // is not working
    .get()

Since Firestore has no "!=" operator, I need to use "<" and ">" operator but still no success.由于 Firestore 没有“!=”运算符,我需要使用“<”和“>”运算符但仍然没有成功。

Question: Ignoring these workarounds, what is the preferred way to get docs by checking whether a specific field exists or does not exists?问题:忽略这些变通方法,通过检查特定字段是否存在来获取文档的首选方法是什么?

As @Emile Moureau solution.作为@Emile Moureau 解决方案。 I prefer我更喜欢

.orderBy(`field`)

To query documents with the field exists.用字段来查询文档是否存在。 Since it will work with any type of data with any value even for null .因为它可以处理任何类型的数据,即使是null也是如此。

But as @Doug Stevenson said:但正如@Doug Stevenson 所说:

You can't query for something that doesn't exist in Firestore.您无法查询 Firestore 中不存在的内容。 A field needs to exist in order for a Firestore index to be aware of it.字段需要存在才能让 Firestore 索引知道它。

You can't query for documents without the field.您无法查询没有该字段的文档。 At least for now.至少现在。

The preferred way to get docs where a specified field exists is to use the:获取存在指定字段的文档的首选方法是使用:

.orderBy(fieldPath)

As specified in the Firebase documentation :Firebase 文档中所述:

在此处输入图像描述

Thus the answer provided by @hisoft is valid.因此@hisoft 提供的答案是有效的。 I just decided to provide the official source, as the question was for the preferred way.我只是决定提供官方来源,因为问题是针对首选方式。

The solution I use is:我使用的解决方案是:

Use: .where('field', '>', ''),使用: .where('field', '>', ''),

Where "field" is the field we are looking for!其中“字段”是我们正在寻找的字段!

Firestore is an indexed database . Firestore 是一个索引数据库 For each field in a document, that document is inserted into that field's index as appropriate based on your configuration .对于文档中的每个字段,该文档会根据您的配置适当地插入到该字段的索引中 If a document doesn't contain a particular field (like challenge_id ) it will not appear in that field's index and will be omitted from queries on that field.如果文档不包含特定字段(如challenge_id ),它将不会出现在该字段的索引中,并且将从对该字段的查询中省略。 Normally, because of the way Firestore is designed, queries should read an index in one continuous sweep.通常,由于 Firestore 的设计方式,查询应该在一次连续扫描中读取索引。 Prior to the introduction of the != and not-in operators, this meant you couldn't exclude particular values as this would require jumping over sections of an index.在引入!=not-in运算符之前,这意味着您不能排除特定值,因为这需要跳过索引的各个部分。 This limitation is still encountered when trying to use exclusive ranges ( v<2 || v>4 ) in a single query.尝试在单个查询中使用独占范围 ( v<2 || v>4 ) 时,仍然会遇到此限制。

Field values are sorted according to the Realtime Database sort order except that the results can be sorted by multiple fields when duplicates are encountered instead of just the document's ID.字段值根据实时数据库排序顺序进行排序,但在遇到重复时,结果可以按多个字段排序,而不仅仅是文档的 ID。

Firestore Value Sort Order Firestore 值排序顺序

Priority优先 Sorted Values排序值 Priority优先 Sorted Values排序值
1 1 null 6 6 strings字符串
2 2 false 7 7 DocumentReference文件参考
3 3 true 8 8 GeoPoint地理点
4 4 numbers数字 9 9 arrays数组
5 5 Timestamp时间戳 10 10 maps地图

Inequality != / <>不等式!= / <>

This section documents how inequalities worked prior to the release of the != and not-in operators in Sep 2020 .本节记录了在2020 年 9 月发布!=not-in运算符之前不等式是如何工作的。 See the documentation on how to use these operators. 请参阅有关如何使用这些运算符的文档 The following section will be left for historical purposes.以下部分将留作历史用途。

To perform an inequality query on Firestore, you must rework your query so that it can be read by reading from Firestore's indexes.要在 Firestore 上执行不等式查询,您必须重新编写查询,以便可以通过读取 Firestore 的索引来读取它。 For an inequality, this is done by using two queries - one for values less than the equality and another for values greater than the equality.对于不等式,这是通过使用两个查询来完成的——一个用于小于equality的值,另一个用于大于等于的值。

As a trivial example, let's say I wanted the numbers that aren't equal to 3.作为一个简单的例子,假设我想要不等于 3 的数字。

const someNumbersThatAreNotThree = someNumbers.filter(n => n !== 3)

can be written as可以写成

const someNumbersThatAreNotThree = [
   ...someNumbers.filter(n => n < 3),
   ...someNumbers.filter(n => n > 3)
];

Applying this to Firestore, you can convert this ( formerly ) incorrect query:将此应用于 Firestore,您可以转换此( 以前)不正确的查询:

const docsWithChallengeID = await colRef
  .where('challenge_id', '!=', '')
  .get()
  .then(querySnapshot => querySnapshot.docs);

into these two queries and merge their results:进入这两个查询并合并它们的结果:

const docsWithChallengeID = await Promise.all([
  colRef
    .orderBy('challenge_id')
    .endBefore('')
    .get()
    .then(querySnapshot => querySnapshot.docs),
  colRef
    .orderBy('challenge_id')
    .startAfter('')
    .get()
    .then(querySnapshot => querySnapshot.docs),
]).then(results => results.flat());

Important Note: The requesting user must be able to read all the documents that would match the queries to not get a permissions error.重要提示:请求用户必须能够读取与查询匹配的所有文档,以免出现权限错误。

Missing/Undefined Fields缺失/未定义的字段

Simply put, in Firestore, if a field doesn't appear in a document, that document won't appear in that field's index.简而言之,在 Firestore 中,如果某个字段未出现在文档中,则该文档将不会出现在该字段的索引中。 This is in contrast to the Realtime Database where omitted fields had a value of null .这与实时数据库形成对比,其中省略字段的值为null

Because of the nature of NoSQL databases where the schema you are working with might change leaving your older documents with missing fields, you might need a solution to "patch your database".由于 NoSQL 数据库的性质,您正在使用的架构可能会发生变化,从而使您的旧文档缺少字段,您可能需要一个解决方案来“修补您的数据库”。 To do this, you would iterate over your collection and add the new field to the documents where it is missing.为此,您将遍历您的集合并将新字段添加到缺少它的文档中。

To avoid permissions errors, it is best to make these adjustments using the Admin SDK with a service account, but you can do this using a regular SDK using a user with the appropriate read/write access to your database.为避免权限错误,最好使用带有服务帐户的 Admin SDK 进行这些调整,但您可以使用对数据库具有适当读/写访问权限的用户使用常规 SDK 来执行此操作。

This function is recursive, and is intended to be executed once .此函数是递归的,旨在执行一次

async function addDefaultValueForField(queryRef, fieldName, defaultFieldValue, pageSize = 100) {
  let checkedCount = 0, pageCount = 1;
  const initFieldPromises = [], newData = { [fieldName]: defaultFieldValue };

  // get first page of results
  console.log(`Fetching page ${pageCount}...`);
  let querySnapshot = await queryRef
    .limit(pageSize)
    .get();

  // while page has data, parse documents
  while (!querySnapshot.empty) {
    // for fetching the next page
    let lastSnapshot = undefined;

    // for each document in this page, add the field as needed
    querySnapshot.forEach(doc => {
      if (doc.get(fieldName) === undefined) {
        const addFieldPromise = doc.ref.update(newData)
          .then(
            () => ({ success: true, ref: doc.ref }),
            (error) => ({ success: false, ref: doc.ref, error }) // trap errors for later analysis
          );

        initFieldPromises.push(addFieldPromise);
      }

      lastSnapshot = doc;
    });

    checkedCount += querySnapshot.size;
    pageCount++;

    // fetch next page of results
    console.log(`Fetching page ${pageCount}... (${checkedCount} documents checked so far, ${initFieldPromises.length} need initialization)`);
    querySnapshot = await queryRef
      .limit(pageSize)
      .startAfter(lastSnapshot)
      .get();
  }

  console.log(`Finished searching documents. Waiting for writes to complete...`);

  // wait for all writes to resolve
  const initFieldResults = await Promise.all(initFieldPromises);

  console.log(`Finished`);

  // count & sort results
  let initializedCount = 0, errored = [];
  initFieldResults.forEach((res) => {
    if (res.success) {
      initializedCount++;
    } else {
      errored.push(res);
    }
  });

  const results = {
    attemptedCount: initFieldResults.length,
    checkedCount,
    errored,
    erroredCount: errored.length,
    initializedCount
  };

  console.log([
    `From ${results.checkedCount} documents, ${results.attemptedCount} needed the "${fieldName}" field added.`,
    results.attemptedCount == 0
      ? ""
      : ` ${results.initializedCount} were successfully updated and ${results.erroredCount} failed.`
  ].join(""));

  const errorCountByCode = errored.reduce((counters, result) => {
    const code = result.error.code || "unknown";
    counters[code] = (counters[code] || 0) + 1;
    return counters;
  }, {});
  console.log("Errors by reported code:", errorCountByCode);

  return results;
}

You would then apply changes using:然后,您将使用以下方法应用更改:

const goalsQuery = firebase.firestore()
  .collection("user_goals");

addDefaultValueForField(goalsQuery, "challenge_id", "")
  .catch((err) => console.error("failed to patch collection with new default value", err));

The above function could also be tweaked to allow the default value to be calculated based on the document's other fields:也可以调整上述函数以允许根据文档的其他字段计算默认值:

let getUpdateData;
if (typeof defaultFieldValue === "function") {
  getUpdateData = (doc) => ({ [fieldName]: defaultFieldValue(doc) });
} else {
  const updateData = { [fieldName]: defaultFieldValue };
  getUpdateData = () => updateData;
}

/* ... later ... */
const addFieldPromise = doc.ref.update(getUpdateData(doc))

As you correctly state, it is not possible to filter based on != .正如您正确指出的那样,无法根据!=进行过滤。 If possible, I would add an extra field to define the goal type.如果可能,我会添加一个额外的字段来定义目标类型。 It is possible to use != in security rules, along with various string comparison methods, so you can enforce the correct goal type, based on your challenge_id format.可以在安全规则中使用!=以及各种字符串比较方法,因此您可以根据您的challenge_id格式强制执行正确的目标类型。

Specify the goal type指定目标类型

Create a type field and filter based on this field.创建一个type字段并基于此字段进行过滤。

type: master or type: custom and search .where('type', '==', 'master') or search for custom. type: mastertype: custom并搜索.where('type', '==', 'master')或搜索 custom。

Flag custom goals标记自定义目标

Create a customGoal field which can be true or false .创建一个可以为truefalsecustomGoal字段。

customGoal: true and search .where('customGoal', '==', true) or false (as required). customGoal: true并搜索.where('customGoal', '==', true)或 false(根据需要)。

Update更新

It is now possible to perform a != query in Cloud Firestore现在可以在 Cloud Firestore 中执行 != 查询

Firestore does pick up on boolean, which is a thing! Firestore 确实接受了布尔值,这是一回事! and can be orderBy 'd.并且可以orderBy 'd。

So often, like now, for this, I add this into the array-pushing from onSnapshot or get , use .get().then( for dev...所以经常,就像现在一样,为此,我将它添加到来自onSnapshotget的数组推送中,使用.get().then(用于开发...

if (this.props.auth !== undefined) {
  if (community && community.place_name) {
    const sc =
      community.place_name && community.place_name.split(",")[1];
      const splitComma = sc ? sc : false
    if (community.splitComma !== splitComma) {
      firebase
        .firestore()
        .collection("communities")
        .doc(community.id)
        .update({ splitComma });
    }
    const sc2 =
      community.place_name && community.place_name.split(",")[2];
      const splitComma2 =sc2 ? sc2 : false
    console.log(splitComma2);
    if (community.splitComma2 !== splitComma2) {
      firebase
        .firestore()
        .collection("communities")
        .doc(community.id)
        .update({
          splitComma2
        });
    }
  }

This way, I can query with orderBy instead of where这样,我可以使用orderBy而不是where

browseCommunities = (paginate, cities) => {
  const collection = firebase.firestore().collection("communities");
    const query =
      cities === 1 //countries
        ? collection.where("splitComma2", "==", false) //without a second comma
        : cities //cities
        ? collection
            .where("splitComma2", ">", "")
            .orderBy("splitComma2", "desc") //has at least two
        : collection.orderBy("members", "desc");
  var shot = null;
  if (!paginate) {
    shot = query.limit(10);
  } else if (paginate === "undo") {
    shot = query.startAfter(this.state.undoCommunity).limit(10);
  } else if (paginate === "last") {
    shot = query.endBefore(this.state.lastCommunity).limitToLast(10);
  }
  shot &&
    shot.onSnapshot(
      (querySnapshot) => {
        let p = 0;
        let browsedCommunities = [];
        if (querySnapshot.empty) {
          this.setState({
            [nuller]: null
          });
        }
        querySnapshot.docs.forEach((doc) => {
          p++;
          if (doc.exists) {
            var community = doc.data();
            community.id = doc.id;

It is not an ideal solution, but here is my workaround when a field does not exist:这不是一个理想的解决方案,但是当字段不存在时,这是我的解决方法:

let user_goals = await db.collection('user_goals').get()
user_goals.forEach(goal => {
  let data = goal.data()
  if(!Object.keys(data).includes(challenge_id)){
    //Perform your task here
  }
})

Note that it would impact your read counts a lot so only use this if you have small collection or can afford the reads.请注意,它会极大地影响您的读取计数,因此只有在您的收藏量较小或负担得起读取时才使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Firestore expressjs:如何获取特定字段存在的文档并更新文档 - Firestore expressjs: how to get document where specific field exists and update the document 如何检索集合中的所有文档并查看 firebase Firestore 中是否存在文档? - How do to retrieve all documents in a collection and see if a document exists in firebase Firestore? Firestore 如何使用 javascript 检查云 Firestore 中的数组中是否存在元素 - Firestore how do I check if an element exists in an array in cloud firestore using javascript 如何使用 Firestore 在 Cloud Functions for Firebase 中获取服务器时间戳? - How do I get the server timestamp in Cloud Functions for Firebase with Firestore? 如何检索作为文档放置在 Firebase Cloud Firestore 中的 UID - How do I retrieve the UID's that I have placed as documents in my Firebase Cloud Firestore 如何批量删除 Firebase 云 function 中的 Firestore 文档 - How to do a Batch delete Firestore Documents in a Firebase cloud function 我可以在firestore中使用`exists` where子句吗? - Is there an `exists` where clause I can use with firestore? 如果字段存在则更新 Firestore - Updating Firestore if field exists 如何从 Cloud Firestore 检索多个文档,但没有重复? - How do i retrieve multiple documents from cloud firestore, but no duplicates? 如何按属性值从Firebase / Firestore获取文档 - How do I get documents from Firebase / Firestore ordered by property value
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM