69 votes

Cloud Firestore deep get avec sous-collection

Disons que nous avons une racine collection nommée "todos'.

Tous les documents de cette collection a:

  1. title: String
  2. sous-groupe nommé todo_items

Chaque document dans le sous-groupe todo_items a

  1. title: String
  2. completed: Boolean

Je sais que l'interrogation dans le Cloud Firestore est peu profonde par défaut, ce qui est excellent, mais est-il une façon d'interroger l' todos et obtenir des résultats qui incluent le sous-groupe todo_items automatiquement?

En d'autres termes, comment puis-je faire la requête suivante inclure l' todo_items sous-groupe?

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

49voto

Dan McGrath Points 9839

Ce type de requête n'est pas pris en charge, bien que nous puissions en tenir compte ultérieurement.

4voto

Malick Points 337

Si vous souhaitez toujours savoir comment effectuer une requête détaillée dans Firestore, voici une version de la fonction cloud getAllTodos que j'ai proposée, qui renvoie tous les «todos» contenant la sous-collection «todo_items».

 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;
        })
}
 

0voto

Ali Adil Points 324

Je suis confronté au même problème, mais avec IOS, de toute façon, si je reçois votre question et si vous utilisez un identifiant automatique pour un document de collecte de tâches à effectuer, il sera facile si vous stockez l'identifiant du document aussi loin avec le champ de titre dans mon cas:

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

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

Ainsi, lorsque vous récupérez, disons un tableau de tâches à effectuer et lorsque vous cliquez sur un élément, vous pouvez naviguer aussi facilement par le chemin

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

J'aimerais pouvoir vous donner le code exact, mais je ne connais pas Javascript

0voto

user2734839 Points 36

J'ai utilisé AngularFirestore (afs) et 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))
      );
  }
   

0voto

lustig Points 1570

Comme l'a souligné dans d'autres réponses, vous ne pouvez pas demander de profondes interrogations.

Ma recommandation: Dupliquer vos données aussi peu que possible.

Je suis en cours d'exécution dans ce même problème avec les "propriétaires d'animaux de compagnie". Dans mes recherches, j'ai besoin d'afficher chaque animal possède un utilisateur, mais j'ai aussi besoin d'être en mesure de rechercher pour les animaux de compagnie sur leur propre. J'ai dupliqué les données. Je vais avoir des animaux propriété de tableau sur chaque utilisateur AINSI QUE des animaux en sous-groupe. Je pense que c'est le meilleur que nous pouvons faire avec ces types de scénarios.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X