【问题标题】:Cloud Firestore deep get with subcollectionCloud Firestore 深度获取子集合
【发布时间】:2018-03-18 14:05:31
【问题描述】:

假设我们有一个名为“todos”的根集合。

此集合中的每个文档都有:

  1. title:字符串
  2. 名为todo_items 的子集合

子集合todo_items 中的每个文档都有

  1. title:字符串
  2. completed: 布尔值

我知道默认情况下 Cloud Firestore 中的查询很浅,这很好,但是有没有办法查询 todos 并自动获取包含子集合 todo_items 的结果?

换句话说,如何使以下查询包含todo_items 子集合?

db.collection('todos').onSnapshot((snapshot) => {
  snapshot.docChanges.forEach((change) => {
    // ...
  });
});

【问题讨论】:

  • 我在“宠物所有权”方面遇到了同样的问题。在我的搜索结果中,我需要显示用户拥有的每只宠物,但我还需要能够自行搜索宠物。我最终复制了数据。我将在每个用户以及 pets 子集合上都有一个 pets 数组属性。我认为这是我们在这种情况下能做的最好的事情。

标签: database firebase google-cloud-platform google-cloud-firestore data-modeling


【解决方案1】:

不支持这种类型的查询,但我们将来可能会考虑这样做。

【讨论】:

  • 请添加对此的支持!这是一个非常重要的功能,实时数据库支持这一点,所以我认为 Firestore 也应该这样做
  • 也许我疯了,但我完全希望这可以作为参考类型的基本功能开箱即用。否则,该类型如何比字符串更有用?这正是我认为 Firestore 旨在解决的问题。请执行此操作!
  • 今天是 2018 年 5 月 27 日。这个功能现在可用吗?这是否已经在 Firebase 的路线图中?
  • @danmcgrath 你有关于路线图的答案吗?
  • 不知道为什么文档中没有指出这一点 - 开发人员不得不通过 StackOverflow 找到这一点有点令人沮丧,可能是因为他们在编码数天后已经达到了这个限制。这确实是基线功能,所以我很惊讶谷歌的回应是“可能会在未来考虑它”,没有 a) 解决方法或 b) 它只是一个考虑因素而不是产品路线图的原因
【解决方案2】:

如果有人仍然想知道如何在 firestore 中进行深度查询,这里是我提出的云函数 getAllTodos 的一个版本,它返回所有具有 'todo_items' 子集合的 'todos'。

exports.getAllTodos = function (req, res) {
    getTodos().
        then((todos) => {
            console.log("All Todos " + todos) // All Todos with its todo_items sub collection.
            return res.json(todos);
        })
        .catch((err) => {
            console.log('Error getting documents', err);
            return res.status(500).json({ message: "Error getting the all Todos" + err });
        });
}

function getTodos(){
    var todosRef = db.collection('todos');

    return todosRef.get()
        .then((snapshot) => {
            let todos = [];
            return Promise.all(
                snapshot.docs.map(doc => {  
                        let todo = {};                
                        todo.id = doc.id;
                        todo.todo = doc.data(); // will have 'todo.title'
                        var todoItemsPromise = getTodoItemsById(todo.id);
                        return todoItemsPromise.then((todoItems) => {                    
                                todo.todo_items = todoItems;
                                todos.push(todo);         
                                return todos;                  
                            }) 
                })
            )
            .then(todos => {
                return todos.length > 0 ? todos[todos.length - 1] : [];
            })

        })
}


function getTodoItemsById(id){
    var todoItemsRef = db.collection('todos').doc(id).collection('todo_items');
    let todo_items = [];
    return todoItemsRef.get()
        .then(snapshot => {
            snapshot.forEach(item => {
                let todo_item = {};
                todo_item.id = item.id;
                todo_item.todo_item = item.data(); // will have 'todo_item.title' and 'todo_item.completed'             
                todo_items.push(todo_item);
            })
            return todo_items;
        })
}

【讨论】:

  • 如果我尝试打印结果待办事项,我的子集合采用以下格式:[Object][Object]...
【解决方案3】:

我遇到了同样的问题,但是对于 IOS,无论如何,如果我收到您的问题,并且如果您将自动 ID 用于待办事项收集文档,那么如果您将文档 ID 存储为带有标题字段的字段,这将很容易 就我而言:

let ref = self.db.collection("collectionName").document()

let data  = ["docID": ref.documentID,"title" :"some title"]

所以当你检索到一个待办事项数组时,当你点击任何项目时,你可以很容易地通过路径导航

ref = db.collection("docID/\(todo_items)")

我希望我能给你确切的代码,但我不熟悉 Javascript

【讨论】:

    【解决方案4】:

    我使用了 AngularFirestore (afs) 和 Typescript:

    import { map, flatMap } from 'rxjs/operators';
    import { combineLatest } from 'rxjs';
    
    interface DocWithId {
      id: string;
    }
    
    convertSnapshots<T>(snaps) {
      return <T[]>snaps.map(snap => {
        return {
          id: snap.payload.doc.id,
          ...snap.payload.doc.data()
        };
      });
    }
    
    getDocumentsWithSubcollection<T extends DocWithId>(
        collection: string,
        subCollection: string
      ) {
        return this.afs
          .collection(collection)
          .snapshotChanges()
          .pipe(
            map(this.convertSnapshots),
            map((documents: T[]) =>
              documents.map(document => {
                return this.afs
                 .collection(`${collection}/${document.id}/${subCollection}`)
                  .snapshotChanges()
                  .pipe(
                    map(this.convertSnapshots),
                    map(subdocuments =>
                      Object.assign(document, { [subCollection]: subdocuments })
                    )
                  );
              })
            ),
            flatMap(combined => combineLatest(combined))
          );
      }
      

    【讨论】:

      【解决方案5】:

      正如其他答案中所指出的,您不能请求深度查询。

      我的建议:复制您的数据尽可能少。

      我在“宠物所有权”方面遇到了同样的问题。在我的搜索结果中,我需要显示用户拥有的每只宠物,但我还需要能够自行搜索宠物。我最终复制了数据。我将在每个用户以及 pets 子集合上都有一个 pets 数组属性。我认为这是我们在这种情况下能做的最好的事情。

      【讨论】:

      • 重复数据不一致怎么办?
      • 对相关更改使用 Firebase Cloud Functions 以更新重复数据。
      【解决方案6】:

      根据文档,您需要对 firestore 进行 2 次调用。第一次获取 doc,第二次获取 subcollection。 减少总时间的最佳方法是使用promise.Allpromise.allSettled 并行进行这两个调用,而不是顺序调用。

      【讨论】:

        【解决方案7】:

        你可以试试这样的:

        db.collection('coll').doc('doc').collection('subcoll').doc('subdoc')
        

        【讨论】:

        猜你喜欢
        • 2019-02-17
        • 2019-12-24
        • 1970-01-01
        • 1970-01-01
        • 2021-05-09
        • 1970-01-01
        • 1970-01-01
        • 2018-11-19
        • 2018-04-23
        相关资源
        最近更新 更多