Firestore 쿼리 하위 컬렉션


125

새로운 Firebase Firestore로 하위 컬렉션을 쿼리 할 수 ​​있다고 읽었지만 예제가 보이지 않습니다. 예를 들어 다음과 같은 방식으로 Firestore를 설정했습니다.

  • 춤 [컬렉션]
    • danceName
    • 노래 [컬렉션]
      • 노래 제목

"SongName == 'X'인 모든 춤 찾기"를 쿼리하려면 어떻게해야합니까?


1
이 지원 아직 firestore, 2020 년입니까?
sajanyamaha

답변:


148

업데이트 2019-05-07

오늘 우리는 컬렉션 그룹 쿼리를 출시 했으며이를 통해 하위 컬렉션 을 쿼리 할 수 ​​있습니다.

예를 들어 웹 SDK에서 :

db.collectionGroup('Songs')
  .where('songName', '==', 'X')
  .get()

컬렉션 경로의 마지막 부분이 'Songs'인 모든 컬렉션의 문서와 일치합니다.

원래 질문은 songName == 'X'인 댄스 를 찾는 것이 었지만 여전히 직접 불가능하지만 일치하는 각 노래에 대해 부모를로드 할 수 있습니다.

원래 답변

이것은 아직 존재하지 않는 기능입니다. 이를 "컬렉션 그룹 쿼리"라고하며 어떤 댄스에 포함되었는지에 관계없이 모든 노래를 쿼리 할 수 ​​있습니다. 이것은 우리가 지원하려는 것이지만 그것이 언제 올 것인지에 대한 구체적인 일정이 없습니다.

이 시점에서 대체 구조는 노래를 최상위 컬렉션으로 만들고 노래가 노래 속성의 일부인 댄스를 만드는 것입니다.


147
Firestore 개발팀이 하위 컬렉션 쿼리를 최대한 빨리 구현하면 훨씬 더 좋을 것입니다. 결국 '더 강력한 쿼리'는 Firestore 매뉴얼에 따르면 주요 판매 포인트 중 하나입니다. 지금 Firestore는 바퀴가없는 포르쉐와 같습니다.
Arne Wolframm

21
우리는 동의한다! 하루에 제한된 시간이 있습니다 :-).
Gil Gilbert

20
이해가 안 돼요. 파이어베이스가 제한되어 있다면 사람들은 무엇을 지불합니까? 백엔드리스조차도 Firebase보다 더 많은 기능을 가지고있는 것 같습니다. 그리고 Firebase가 왜 그렇게 인기가 있을까요? 사람들이 미친 것 같습니다
nzackoya

15
이 기능은 매우 필요합니다. 그렇지 않으면 사람들이 대안을 찾기 시작할 것입니다. 심지어 우리는 마감일이 있습니다. : P
JD-V

13
이 기능이 필요합니다. 최소한 이것을 공개하는 일정은 우리가 준비하는 데 도움이 될 것입니다.
sanjaya panigrahy

22

지금 업데이트 Firestore는 어레이 포함을 지원합니다.

이 문서를 가지고

    {danceName: 'Danca name 1', songName: ['Title1','Title2']}
    {danceName: 'Danca name 2', songName: ['Title3']}

이렇게 해

collection("Dances")
    .where("songName", "array-contains", "Title1")
    .get()...

@ Nelson.b.austin firestore에는 아직 해당 기능이 없으므로 평평한 구조를 갖는 것이 좋습니다.

Dances = {
    danceName: 'Dance name 1',
    songName_Title1: true,
    songName_Title2: true,
    songName_Title3: false
}

그렇게하면 다음과 같이 할 수 있습니다.

var songTitle = 'Title1';
var dances = db.collection("Dances");
var query = dances.where("songName_"+songTitle, "==", true);

이게 도움이 되길 바란다.


2
무엇에 songName_Title3: false유용합니까? 내가 잘못 아니에요 경우, 단지 우리가 필요한 가정 특정 노래 이름이없는 춤을 검색하는 데 사용할 수 songName_Title3: false있도록 dances.where("songName_"+songTitle, "==", false); 이 가능한 모든 노래 부울 플래그를 가지고 모든 춤에 대한 sence을하지 않을 리턴 같은 결과를 이름 ...
epeleg

이것은 훌륭하지만 문서는 1MB로 제한되어 있으므로 긴 문자열 목록 또는 기타 항목을 특정 문서와 연결해야하는 경우이 방법을 사용할 수 없습니다.
Supertecnoboff

@Supertecnoboff 엄청나게 크고 긴 문자열 목록이어야 할 것 같습니다. 이 "array_contains"쿼리는 얼마나 성능이 좋으며 더 성능이 좋은 대안은 무엇입니까?
Jay Ordway 2019 년

14

노래를 컬렉션이 아닌 개체로 저장하면 어떨까요? 노래를 필드로 사용하여 각 춤 : 유형 Object (컬렉션 아님)

{
  danceName: "My Dance",
  songs: {
    "aNameOfASong": true,
    "aNameOfAnotherSong": true,
  }
}

그런 다음 aNameOfASong으로 모든 댄스를 쿼리 할 수 ​​있습니다.

db.collection('Dances')
  .where('songs.aNameOfASong', '==', true)
  .get()
  .then(function(querySnapshot) {
    querySnapshot.forEach(function(doc) {
      console.log(doc.id, " => ", doc.data());
    });
   })
   .catch(function(error) {
     console.log("Error getting documents: ", error);
    });

3
이 솔루션은 작동하지만 노래 수가 많거나 동적으로 증가 할 수있는 경우 확장 할 수 없습니다. 이것은 문서 크기를 증가시키고 읽기 / 쓰기 성능에 영향을줍니다. 보기 약은 (페이지의 마지막 부분 '제한'참조) 아래에 링크 된 중포 기지 문서에서 찾을 수 있습니다 firebase.google.com/docs/firestore/solutions/arrays
Nouman HANIF

14

2019 업데이트

Firestore는 컬렉션 그룹 쿼리를 출시했습니다. 위의 Gil의 답변 또는 공식 컬렉션 그룹 쿼리 문서를 참조하십시오.


이전 답변

Gil Gilbert가 말했듯이 컬렉션 그룹 쿼리 가 현재 작업중인 것처럼 보입니다 . 그동안 루트 레벨 컬렉션을 사용하고 문서 UID를 사용하여 이러한 컬렉션 사이를 연결하는 것이 더 좋습니다.

아직 모르는 사람들을 위해 Jeff Delaney는 AngularFirebase 에서 Firebase (및 Angular)로 작업하는 모든 사람을위한 놀라운 가이드와 리소스를 제공 합니다.

Firestore NoSQL 관계형 데이터 모델링 -여기서 그는 NoSQL 및 Firestore DB 구조화의 기본 사항을 분석합니다.

Firestore를 사용한 고급 데이터 모델링 (예제 별) -염두에 두어야 할 고급 기술입니다. Firestore 기술을 한 단계 더 높이고 자하는 사람들을위한 훌륭한 책


7

2019 년 7 월 8 일 새로운 업데이트 :

db.collectionGroup('Songs')
  .where('songName', isEqualTo:'X')
  .get()

3

항상 다음과 같이 검색 할 수 있습니다.

this.key$ = new BehaviorSubject(null);

return this.key$.switchMap(key =>
  this.angFirestore
    .collection("dances").doc("danceName").collections("songs", ref =>
      ref
        .where("songName", "==", X)
    )
    .snapshotChanges()
    .map(actions => {
      if (actions.toString()) {
        return actions.map(a => {
          const data = a.payload.doc.data() as Dance;
          const id = a.payload.doc.id;
          return { id, ...data };
        });
      } else {
        return false;
      }
    })
);

3

쿼리 제한

Cloud Firestore는 다음 유형의 쿼리를 지원하지 않습니다.

  1. 다른 필드에 범위 필터가있는 쿼리.

  2. 여러 컬렉션 또는 하위 컬렉션에 대한 단일 쿼리. 각 쿼리는 단일 문서 모음에 대해 실행됩니다. 데이터 구조가 쿼리에 미치는 영향에 대한 자세한 내용은 데이터 구조 선택을 참조 하십시오 .

  3. 논리적 OR 쿼리. 이 경우 각 OR 조건에 대해 별도의 쿼리를 만들고 앱에서 쿼리 결과를 병합해야합니다.

  4. ! = 절이있는 쿼리. 이 경우 쿼리를보다 큼 쿼리와보다 작음 쿼리로 분할해야합니다. 예를 들어 where ( "age", "! =", "30") 쿼리 절이 지원되지 않더라도 두 쿼리를 where ( "age", "<) 절과 결합하여 동일한 결과 집합을 얻을 수 있습니다. ","30 ") 및 where ("age ","> ", 30) 절이있는 하나.


2
var songs = []    
db.collection('Dances')
      .where('songs.aNameOfASong', '==', true)
      .get()
      .then(function(querySnapshot) {
        var songLength = querySnapshot.size
        var i=0;
        querySnapshot.forEach(function(doc) {
           songs.push(doc.data())
           i ++;
           if(songLength===i){
                console.log(songs
           }
          console.log(doc.id, " => ", doc.data());
        });
       })
       .catch(function(error) {
         console.log("Error getting documents: ", error);
        });

1

플랫 데이터 구조를 사용하는 것이 더 나을 수 있습니다.
문서 는이 페이지에 있는 다른 데이터 구조의 장단점을 지정합니다 .

특히 하위 컬렉션이있는 구조의 제한 사항에 대해 설명합니다.

하위 컬렉션을 쉽게 삭제하거나 하위 컬렉션에서 복합 쿼리를 수행 할 수 없습니다.

플랫 데이터 구조의 장점과는 대조됩니다.

루트 수준 컬렉션은 각 컬렉션 내에서 강력한 쿼리와 함께 최고의 유연성과 확장 성을 제공합니다.


1

해결책을 찾았습니다. 이것을 확인하십시오.

var museums = Firestore.instance.collectionGroup('Songs').where('songName', isEqualTo: "X");
        museums.getDocuments().then((querySnapshot) {
            setState(() {
              songCounts= querySnapshot.documents.length.toString();
            });
        });

그런 다음 console.firebase.google.com에서 클라우드 Firestore의 데이터, 규칙, 색인, 사용 탭을 볼 수 있습니다. 마지막으로 색인 탭에서 색인을 설정해야합니다.여기에 이미지 설명 입력

여기에 컬렉션 ID와 일부 필드 값을 입력합니다. 그런 다음 컬렉션 그룹 옵션을 선택합니다. 그것을 즐기십시오. 감사


이것은 질문에 대한 답이 아닙니다. 위에서 언급 한 쿼리는 songName = 'X'인 모든 노래를 가져옵니다. 이것은 songName = 'X'인 댄스를 제공하지 않습니다.
sachin rathod

0

나는 여기 Observables와 AngularFire 래퍼로 작업하고 있지만 여기에 내가 어떻게 할 수 있었는지 보여줍니다.

그것은 일종의 미친 짓입니다. 나는 여전히 관찰 가능 항목에 대해 배우고 있으며 아마도 그것을 과장했습니다. 그러나 그것은 좋은 운동이었습니다.

일부 설명 (RxJS 전문가 아님) :

  • songId $는 ID를 방출하는 Observable입니다.
  • dance $는 해당 ID를 읽고 첫 번째 값만 가져 오는 Observable입니다.
  • 그런 다음 모든 노래의 collectionGroup을 쿼리하여 모든 인스턴스를 찾습니다.
  • 인스턴스를 기반으로 부모 Dances로 이동하여 ID를 얻습니다.
  • 이제 모든 Dance ID가 있으므로 데이터를 가져 오기 위해 쿼리해야합니다. 그러나 나는 그것이 잘 수행되기를 원했기 때문에 하나씩 쿼리하는 대신 10 개의 버킷으로 일괄 처리합니다 (최대 각도는 in쿼리에 걸립니다 .
  • 결국 N 개의 버킷으로 끝나고 해당 값을 얻으려면 firestore에서 N 개의 쿼리를 수행해야합니다.
  • firestore에서 쿼리를 수행 한 후에도 실제로 데이터를 파싱해야합니다.
  • 마지막으로 모든 쿼리 결과를 병합하여 모든 Dance가 포함 된 단일 배열을 얻을 수 있습니다.
type Song = {id: string, name: string};
type Dance = {id: string, name: string, songs: Song[]};

const songId$: Observable<Song> = new Observable();
const dance$ = songId$.pipe(
  take(1), // Only take 1 song name
  switchMap( v =>
    // Query across collectionGroup to get all instances.
    this.db.collectionGroup('songs', ref =>
      ref.where('id', '==', v.id)).get()
  ),
  switchMap( v => {
    // map the Song to the parent Dance, return the Dance ids
    const obs: string[] = [];
    v.docs.forEach(docRef => {
      // We invoke parent twice to go from doc->collection->doc
      obs.push(docRef.ref.parent.parent.id);
    });
    // Because we return an array here this one emit becomes N
    return obs;
  }),
  // Firebase IN support up to 10 values so we partition the data to query the Dances
  bufferCount(10),
  mergeMap( v => { // query every partition in parallel
    return this.db.collection('dances', ref => {
      return ref.where( firebase.firestore.FieldPath.documentId(), 'in', v);
    }).get();
  }),
  switchMap( v => {
    // Almost there now just need to extract the data from the QuerySnapshots
    const obs: Dance[] = [];
    v.docs.forEach(docRef => {
      obs.push({
        ...docRef.data(),
        id: docRef.id
      } as Dance);
    });
    return of(obs);
  }),
  // And finally we reduce the docs fetched into a single array.
  reduce((acc, value) => acc.concat(value), []),
);
const parentDances = await dance$.toPromise();

내 코드를 복사하고 변수 이름을 귀하의 것으로 변경했지만 오류가 있는지 확실하지 않지만 잘 작동했습니다. 오류를 발견하거나 모의 Firestore로 테스트하는 더 나은 방법을 제안 할 수 있으면 알려주세요.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.