swift - 오기 - firestore 사용법




Firestore:컬렉션에서 임의의 문서를 가져 오는 방법 (4)

@Dan McGrath 기술을 기반으로하는 Angular + Firestore를 사용하는 사람들을 위해 여기에 코드 스 니펫이 있습니다.

아래 코드 스 니펫은 1 개의 문서를 반환합니다.

  getDocumentRandomlyParent(): Observable<any> {
    return this.getDocumentRandomlyChild()
      .pipe(
        expand((document: any) => document === null ? this.getDocumentRandomlyChild() : EMPTY),
      );
  }

  getDocumentRandomlyChild(): Observable<any> {
      const random = this.afs.createId();
      return this.afs
        .collection('my_collection', ref =>
          ref
            .where('random_identifier', '>', random)
            .limit(1))
        .valueChanges()
        .pipe(
          map((documentArray: any[]) => {
            if (documentArray && documentArray.length) {
              return documentArray[0];
            } else {
              return null;
            }
          }),
        );
  }

1) .expand ()는 무작위 선택에서 문서를 확실히 얻을 수 있도록 재귀를위한 rxjs 작업입니다.

2) 재귀가 예상대로 작동하려면 두 가지 기능이 필요합니다.

3) .expand () 연산자를 종료하기 위해 EMPTY를 사용합니다.

import { Observable, EMPTY } from 'rxjs';

내 응용 프로그램이 firebase의 컬렉션에서 무작위로 여러 문서를 선택할 수 있어야합니다.

필자는 파이어베이스에 내장 된 네이티브 함수가 없기 때문에, 필자가 생각한 첫 번째 쿼리는 쿼리 커서를 사용하여 임의의 시작 및 끝 인덱스를 선택하는 것이 었습니다. 컬렉션.

이 접근 방식은 모든 문서가 매번 인접 문서로 순차적으로 제공되기 때문에 제한된 방식으로 만 작동합니다. 그러나 부모 컬렉션에서 해당 인덱스로 문서를 선택할 수 있었다면 임의의 문서 쿼리를 얻을 수는 있지만 문제는이 작업을 수행하는 방법이나 수행 할 수있는 방법을 설명하는 문서를 찾을 수 없다는 것입니다.

다음과 같은 파이어 스토어 스키마를 고려해 볼 수 있기를 바랍니다.

root/
  posts/
     docA
     docB
     docC
     docD

그럼 내 클라이언트 (나는 스위프트 환경에있어) 이렇게 할 수있는 쿼리를 작성하고 싶습니다 :

db.collection("posts")[0, 1, 3] // would return: docA, docB, docD

어쨌든 내가이 일을 따라 뭔가를 할 수 있을까? 아니면 비슷한 방식으로 임의의 문서를 선택할 수있는 다른 방법이 있습니까?

도와주세요.


Angular 7 + RxJS에서이 작업을 수행하여 예제를 원하는 사람들과 여기에서 공유 할 수 있습니다.

@Dan McGrath의 답변을 사용했고 여러 옵션에 대해 Random Integer version + Rinse & Repeat 옵션을 선택했습니다. 나는 또한이 기사에서 설명한 것들을 사용했다 : RxJS, If-Else Operator는 어디에 있는가? 스트림 레벨에서 if / else 문을 만들 수 있습니다 (여러분이 그것에 프라이머가 필요한 경우).

또한 Angular에서 Firebase 통합을 쉽게하기 위해 angularfire2 를 사용 angularfire2 .

다음은 코드입니다.

import { Component, OnInit } from '@angular/core';
import { Observable, merge, pipe } from 'rxjs';
import { map, switchMap, filter, take } from 'rxjs/operators';
import { AngularFirestore, QuerySnapshot } from '@angular/fire/firestore';

@Component({
  selector: 'pp-random',
  templateUrl: './random.component.html',
  styleUrls: ['./random.component.scss']
})
export class RandomComponent implements OnInit {

  constructor(
    public afs: AngularFirestore,
  ) { }

  ngOnInit() {
  }

  public buttonClicked(): void {
    this.getRandom().pipe(take(1)).subscribe();
  }

  public getRandom(): Observable<any[]> {
    const randomNumber = this.getRandomNumber();
    const request$ = this.afs.collection('your-collection', ref => ref.where('random', '>=', randomNumber).orderBy('random').limit(1)).get();
    const retryRequest$ = this.afs.collection('your-collection', ref => ref.where('random', '<=', randomNumber).orderBy('random', 'desc').limit(1)).get();

    const docMap = pipe(
      map((docs: QuerySnapshot<any>) => {
        return docs.docs.map(e => {
          return {
            id: e.id,
            ...e.data()
          } as any;
        });
      })
    );

    const random$ = request$.pipe(docMap).pipe(filter(x => x !== undefined && x[0] !== undefined));

    const retry$ = request$.pipe(docMap).pipe(
      filter(x => x === undefined || x[0] === undefined),
      switchMap(() => retryRequest$),
      docMap
    );

    return merge(random$, retry$);
  }

  public getRandomNumber(): number {
    const min = Math.ceil(Number.MIN_VALUE);
    const max = Math.ceil(Number.MAX_VALUE);
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
}

임의로 생성 된 색인 및 간단한 쿼리를 사용하여 Cloud Firestore의 컬렉션 또는 컬렉션 그룹에서 문서를 임의로 선택할 수 있습니다.

이 답변은 각 섹션마다 다른 옵션이있는 4 개의 섹션으로 나뉩니다.

  1. 임의의 인덱스를 생성하는 방법
  2. 임의의 인덱스를 쿼리하는 방법
  3. 여러 임의의 문서 선택
  4. 진행중인 임의성을 위해 다시 시드

임의의 인덱스를 생성하는 방법

이 대답의 기초는 오름차순 또는 내림차순으로 정렬되면 모든 문서가 무작위로 정렬되는 색인 된 필드를 만드는 것입니다. 이것을 생성하는 방법은 여러 가지가 있으므로 가장 쉽게 사용할 수있는 것으로 시작하여 2를 살펴 보겠습니다.

자동 ID 버전

클라이언트 라이브러리에서 제공되는 무작위로 생성 된 자동 ID를 사용하는 경우이 동일한 시스템을 사용하여 임의로 문서를 선택할 수 있습니다. 이 경우 임의로 정렬 된 인덱스 문서 ID입니다.

나중에 쿼리 섹션에서 생성하는 무작위 값은 새로운 자동 ID ( iOS , Android , Web )이며 쿼리하는 필드는 __name__ 필드이고 나중에 언급 할 '낮은 값'은 빈 문자열입니다. 이것은 언어와 플랫폼에 관계없이 무작위 색인을 생성하는 가장 쉬운 방법입니다.

기본적으로 문서 이름 ( __name__ )은 오름차순으로 만 인덱싱되며 기존 문서의 이름을 삭제 및 다시 만들기의 이름으로 바꿀 수는 없습니다. 이 중 하나가 필요하면이 방법을 사용할 수 있으며이 목적으로 문서 이름을 오버로드하는 대신 auto-id를 random 이라는 실제 필드로 저장하면됩니다.

무작위 정수 버전

문서를 작성할 때는 먼저 한정 범위에서 임의의 정수를 생성하고 random이라는 필드로 설정합니다. 예상되는 문서의 수에 따라 다른 경계 범위를 사용하여 공간을 절약하거나 충돌 위험을 줄일 수 있습니다 (이 기술의 효율성이 떨어짐).

다른 고려 사항이 있기 때문에 필요한 언어를 고려해야합니다. Swift는 쉽지만, JavaScript는 주목할만한 점이 있습니다.

  • 32 비트 정수 : 작은 경우 ( 충돌이 발생하지 않는 ~ 10K) 데이터 집합
  • 64 비트 정수 : 대형 데이터 세트 (참고 : JavaScript는 기본적으로 지원하지 않습니다.)

이렇게하면 문서를 무작위로 정렬하여 색인을 만들 수 있습니다. 나중에 쿼리 섹션에서 생성하는 무작위 값은이 값 중 하나가되며 나중에 설명하는 '낮은 값'은 -1이됩니다.

임의의 인덱스를 쿼리하는 방법

무작위 색인이 생겼으므로 색인을 생성해야합니다. 아래에서는 1 개의 임의 문서를 선택하는 간단한 변형과 ​​1을 초과하는 옵션을 선택하는 옵션을 살펴 보겠습니다.

이 모든 옵션의 경우, 문서를 작성할 때 작성한 색인 값과 동일한 양식의 새 임의 값을 생성해야합니다 (아래 변수 random 참조). 이 값을 사용하여 색인에서 임의의 지점을 찾습니다.

줄 바꿈

이제 무작위 값을 얻었으므로 단일 문서를 쿼리 할 수 ​​있습니다.

let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)

이것이 문서를 반환했는지 확인하십시오. 그렇지 않으면 다시 쿼리하지만 임의의 인덱스에 '낮은 값'을 사용하십시오. 예를 들어, Random Integers를 수행 한 경우 lowValue0 .

let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: lowValue)
                   .order(by: "random")
                   .limit(to: 1)

하나의 문서 만 있으면 적어도 하나의 문서를 반환 할 수 있습니다.

양방향

랩 어라운드 (wrap-around) 방법은 구현이 간단하며 오름차순 인덱스 만 사용하여 스토리지를 최적화 할 수 있습니다. 한 가지 단점은 가치가 불공정하게 차폐 될 가능성이 있다는 것입니다. 예를 들어, 10K 중 처음 3 개의 문서 (A, B, C)가 A : 409496, B : 436496, C : 818992의 임의 색인 값을 갖는 경우 A와 C는 선택 될 확률이 1 / B는 A 근접에 의해 효과적으로 차폐되며 1 / 160K 정도의 확률로 차폐됩니다.

단일 방향으로 쿼리하고 값을 찾을 수없는 경우 줄 바꿈하지 않고 >=<= 중에서 임의로 선택할 수 있습니다. 이렇게하면 값이 두 배가되는 비용으로 불필요하게 차폐 된 값의 반을 줄일 수 있습니다.

한 방향에서 결과가 반환되지 않으면 다른 방향으로 전환하십시오.

queryRef = postsRef.whereField("random", isLessThanOrEqualTo: random)
                   .order(by: "random", descending: true)
                   .limit(to: 1)

queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)

여러 임의의 문서 선택

종종 한 번에 두 개 이상의 임의 문서를 선택하려고합니다. 당신이 원하는 트레이드 오프에 따라 위의 기술을 조정하는 2 가지 다른 방법이 있습니다.

린스 & 반복

이 방법은 간단합니다. 매번 새로운 무작위 정수를 선택하는 과정을 반복하면됩니다.

이 방법을 사용하면 같은 패턴을 반복적으로 보지 않아도 무작위 순서로 문서를 볼 수 있습니다.

절충은 각 문서에 대해 서비스로 별도의 왕복이 필요하기 때문에 다음 방법보다 느려질 것입니다.

계속 오라.

이 방법에서는 원하는 문서 수의 한도를 단순히 늘립니다. 0을 반환 할 수 있기 때문에 좀 더 복잡합니다. 통화 0..limit 문서를 제한하십시오. 그런 다음 누락 된 문서를 같은 방식으로 가져와야하지만 한도는 차이만으로 줄여야합니다. 요청하는 숫자보다 더 많은 문서가 있다는 것을 알고 있다면 두 번째 호출에서는 충분하지 않은 문서를 다시 얻지 못하는 경우를 무시하여 최적화 할 수 있습니다 (첫 번째 호출은 제외).

이 솔루션과의 절충은 반복되는 순서입니다. 문서가 무작위로 정렬되는 동안 범위가 겹치게되면 이전에 본 것과 같은 패턴을 볼 수 있습니다. 재 시드에 관한 다음 섹션에서 논의되는이 문제를 완화 할 수있는 방법이 있습니다.

이 방법은 최상의 경우에 한 번 또는 최악의 경우 2 건의 모든 문서를 요청할 때 '헹굼 및 반복'보다 빠릅니다.

진행중인 임의성을 위해 다시 시드

이 방법은 문서 세트가 정적 인 경우 무작위로 문서를 제공하지만 반환되는 각 문서의 확률도 고정됩니다. 이것은 일부 값이 초기 무작위 값을 기반으로 부당하게 낮거나 높은 확률을 가질 수 있으므로 문제입니다. 많은 경우, 괜찮습니다. 그러나 어떤 경우에는 하나의 문서를 반환 할 확률이 더 높아질 수 있도록 장기 임의성을 늘려야 할 수 있습니다.

삽입 된 문서는 중간에 위치하며, 문서를 삭제하는 것과 마찬가지로 확률이 점진적으로 변경됩니다. 삽입 / 삭제 비율이 문서 수를 감안할 때 너무 적 으면이를 처리하는 몇 가지 전략이 있습니다.

다중 임의

재 시드를 걱정하지 않고 문서 당 여러 개의 임의 색인을 생성 한 다음 매번 색인 중 하나를 무작위로 선택할 수 있습니다. 예를 들어, random 필드를 1 ~ 3의 서브 필드가있는 맵으로 지정하십시오.

{'random': {'1': 32456, '2':3904515723, '3': 766958445}}

이제 random.1, random.2, random.3에 대해 무작위로 쿼리하여 무작위의 확산을 넓힐 수 있습니다. 이것은 본질적으로 스토리지를 증가시켜 재 시드해야하는 계산 (문서 쓰기)을 줄입니다.

쓰기시 다시 시도

문서를 업데이트 할 때마다 random 필드의 임의 값을 다시 생성하십시오. 이렇게하면 무작위 색인에서 문서가 이동합니다.

읽기에 다시 시도

생성 된 임의의 값이 균등하게 분산되지 않으면 (무작위이므로 예상 됨) 동일한 문서가 적절한 시간만큼 선택 될 수 있습니다. 이는 임의로 선택된 문서를 읽은 후에 새로운 임의의 값으로 업데이트함으로써 쉽게 상쇄됩니다.

쓰기가 더 비싸고 핫스팟이 될 수 있기 때문에 시간의 하위 집합 (예 : if random(0,100) === 0) update; 할 때만 업데이트하도록 선택할 수 있습니다 if random(0,100) === 0) update; ).


파이어 폭스 파이어 스토어 (Firestore Firestore)에서 목록 문서를 무작위로 얻는 한 가지 방법이 있습니다. Firestore에서 데이터를 업로드 할 때 1에서 1 백만 사이의 임의 값으로 필드 이름 "위치"를 만듭니다. 내가 스토어에서 데이터를 가져올 때 필드 "위치"로 값을 설정하고 값을 업데이트하면 많은 사용자로드 데이터와 데이터가 항상 업데이트되며 임의의 값이됩니다.





google-cloud-firestore