source

Cloud Firestore를 사용하여 컬렉션의 문서 수를 가져오는 방법

ittop 2023. 7. 16. 17:57
반응형

Cloud Firestore를 사용하여 컬렉션의 문서 수를 가져오는 방법

Firestore에서 컬렉션의 총 문서 수를 얻으려면 어떻게 해야 합니까?

예를 들어, 만약 내가.

/people
    /123456
        /name - 'John'
    /456789
        /name - 'Jane'

저는 제가 몇 명인지 물어보고 2명을 받고 싶습니다.

/people에 대한 쿼리를 수행한 다음 반환된 결과의 길이를 얻을 수 있지만, 특히 대규모 데이터셋에서 이 작업을 수행할 예정이기 때문에 이 작업이 낭비되는 것 같습니다.

현재 세 가지 옵션이 있습니다.

옵션 1: 클라이언트 측

이것은 기본적으로 당신이 언급한 접근법입니다.컬렉션에서 모두를 선택하고 클라이언트 측에서 카운트합니다.이것은 소규모 데이터 세트에 충분히 효과적이지만 데이터 세트가 더 크면 분명히 작동하지 않습니다.

옵션 2: 쓰기 시간 최적화

이 방법을 사용하면 클라우드 기능을 사용하여 컬렉션에서 추가 및 삭제할 때마다 카운터를 업데이트할 수 있습니다.

추가/삭제가 초당 1개 이하의 속도로만 발생하는 한 모든 데이터셋 크기에 적합합니다.이렇게 하면 거의 최신 카운트를 즉시 제공하기 위해 읽을 단일 문서가 제공됩니다.

초당 1개를 초과해야 하는 경우 설명서에 따라 분산 카운터를 구현해야 합니다.

옵션 3: 정확한 쓰기 시간

클라우드 기능을 사용하는 대신 클라이언트에서 문서를 추가하거나 삭제하는 동시에 카운터를 업데이트할 수 있습니다.즉, 카운터도 최신 상태가 되지만 문서를 추가하거나 삭제할 때 이 논리를 포함해야 합니다.

옵션 2와 마찬가지로 초당 초과하려면 분산 카운터를 구현해야 합니다.

집계가 방법입니다(클라이언트 측에서 노출을 원하지 않을 수 있는 정보를 사용자에게 노출하기 때문에 파이어베이스 기능은 이러한 집계를 업데이트하는 권장 방법으로 보입니다). https://firebase.google.com/docs/firestore/solutions/aggregation

큰 목록에 적합하지 않고 전체 목록을 다운로드하는 다른 방법(권장하지 않음): res.size(권장하지 않음)

   db.collection("logs")
      .get()
      .then((res) => console.log(res.size));

하면 (AngulareFire2를 사용한다고 하면) 할 수 .private afs: AngularFirestore생성자에 주입됨):

this.afs.collection(myCollection).valueChanges().subscribe( values => console.log(values.length));

여기서,values는 의모항배다니열입에 있는 입니다.myCollection사용할 수 있도록 메타데이터가 필요하지 않습니다.valueChanges()직접적인 방법.

클라우드 기능이 있는 대규모 컬렉션의 경우 문서 수를 세는 데 주의해야 합니다.모든 수집에 대해 미리 계산된 카운터를 사용하려면 Firestore 데이터베이스가 약간 복잡합니다.

이 경우에는 다음과 같은 코드가 작동하지 않습니다.

export const customerCounterListener = 
    functions.firestore.document('customers/{customerId}')
    .onWrite((change, context) => {

    // on create
    if (!change.before.exists && change.after.exists) {
        return firestore
                 .collection('metadatas')
                 .doc('customers')
                 .get()
                 .then(docSnap =>
                     docSnap.ref.set({
                         count: docSnap.data().count + 1
                     }))
    // on delete
    } else if (change.before.exists && !change.after.exists) {
        return firestore
                 .collection('metadatas')
                 .doc('customers')
                 .get()
                 .then(docSnap =>
                     docSnap.ref.set({
                         count: docSnap.data().count - 1
                     }))
    }

    return null;
});

그 이유는 파이어스토어 설명서에 나와 있듯이 모든 클라우드 파이어스토어 트리거는 동일해야 하기 때문입니다. https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees

해결책

따라서 코드가 여러 번 실행되지 않도록 하려면 이벤트 및 트랜잭션을 관리해야 합니다.다음은 대규모 수집 카운터를 처리하는 특별한 방법입니다.

const executeOnce = (change, context, task) => {
    const eventRef = firestore.collection('events').doc(context.eventId);

    return firestore.runTransaction(t =>
        t
         .get(eventRef)
         .then(docSnap => (docSnap.exists ? null : task(t)))
         .then(() => t.set(eventRef, { processed: true }))
    );
};

const documentCounter = collectionName => (change, context) =>
    executeOnce(change, context, t => {
        // on create
        if (!change.before.exists && change.after.exists) {
            return t
                    .get(firestore.collection('metadatas')
                    .doc(collectionName))
                    .then(docSnap =>
                        t.set(docSnap.ref, {
                            count: ((docSnap.data() && docSnap.data().count) || 0) + 1
                        }));
        // on delete
        } else if (change.before.exists && !change.after.exists) {
            return t
                     .get(firestore.collection('metadatas')
                     .doc(collectionName))
                     .then(docSnap =>
                        t.set(docSnap.ref, {
                            count: docSnap.data().count - 1
                        }));
        }

        return null;
    });

사용 사례:

/**
 * Count documents in articles collection.
 */
exports.articlesCounter = functions.firestore
    .document('articles/{id}')
    .onWrite(documentCounter('articles'));

/**
 * Count documents in customers collection.
 */
exports.customersCounter = functions.firestore
    .document('customers/{id}')
    .onWrite(documentCounter('customers'));

보시다시피 다중 실행을 방지하는 핵심은 컨텍스트 개체의 eventId라는 속성입니다.동일한 이벤트에 대해 여러 번 기능을 처리한 경우 이벤트 ID는 모든 경우에서 동일합니다.데이터베이스에 "이벤트" 컬렉션이 있어야 합니다.

다른 스레드에서 찾은 아래 답변 확인 부탁드립니다.숫자는 원자여야 합니다.이러한 경우에는 FieldValue.increment() 함수를 사용해야 합니다.

https://stackoverflow.com/a/49407570/3337028

소방 기지 관리자 제공select(fields)컬렉션 내의 문서에 대한 특정 필드만 가져올 수 있습니다.용사를 합니다.select모든 필드를 가져오는 것보다 성능이 뛰어납니다.그러나 다음 기간에만 사용할 수 있습니다.firebase-admin그리고.firebase-admin일반적으로 서버 측에서만 사용됩니다.

select다음과 같이 사용할 수 있습니다.

select('age', 'name') // fetch the age and name fields
select() // select no fields, which is perfect if you just want a count

selectNode.js 서버에서 사용할 수 있지만 다른 언어에 대해서는 잘 모르겠습니다.

https://googleapis.dev/nodejs/firestore/latest/Query.html#선택 https://googleapis.dev/nodejs/firestore/latest/CollectionReference.html# 선택

Node.측 함수가 이 함수는 Node..js를 사용합니다.select필터링된 컬렉션을 카운트하고 모든 결과 문서의 ID를 가져옵니다.TS로 작성되었지만 쉽게 JS로 변환됩니다.

import admin from 'firebase-admin'

// https://stackoverflow.com/questions/46554091/cloud-firestore-collection-count

// we need to use admin SDK here as select() is only available for admin
export const videoIds = async (req: any): Promise<any> => {

  const id: string = req.query.id || null
  const group: string = req.query.group || null
  let processed: boolean = null
  if (req.query.processed === 'true') processed = true
  if (req.query.processed === 'false') processed = false

  let q: admin.firestore.Query<admin.firestore.DocumentData> = admin.firestore().collection('videos')
  if (group != null) q = q.where('group', '==', group)
  if (processed != null) q = q.where('flowPlayerProcessed', '==', processed)
  // select restricts returned fields such as ... select('id', 'name')
  const query: admin.firestore.QuerySnapshot<admin.firestore.DocumentData> = await q.orderBy('timeCreated').select().get()

  const ids: string[] = query.docs.map((doc: admin.firestore.QueryDocumentSnapshot<admin.firestore.DocumentData>) => doc.id) // ({ id: doc.id, ...doc.data() })

  return {
    id,
    group,
    processed,
    idx: id == null ? null : ids.indexOf(id),
    count: ids.length,
    ids
  }
}

클라우드 기능 HTTP 요청은 각 문서에 많은 데이터가 포함된 500개의 문서 모음에 대해 1초 이내에 완료됩니다. 정도로 하지 않는 것보다 훨씬 .select클라이언트 측 캐싱(또는 서버 측 캐싱)을 도입하여 성능을 개선할 수 있습니다.

클라우드 기능 시작 지점은 다음과 같습니다.

exports.videoIds = functions.https.onRequest(async (req, res) => {
  const response: any = await videoIds(req)
  res.json(response)
})

HTTP 요청 URL은 다음과 같습니다.

https://SERVER/videoIds?group=my-group&processed=true

Firebase 기능은 서버가 배치된 위치를 자세히 설명합니다.

Dan Answer:데이터베이스에 별도의 카운터를 두고 Cloud Functions를 사용하여 유지 관리할 수 있습니다. (쓰기 시간 최선의 노력)

// Example of performing an increment when item is added
module.exports.incrementIncomesCounter = collectionRef.onCreate(event => {
  const counterRef = event.data.ref.firestore.doc('counters/incomes')

  counterRef.get()
  .then(documentSnapshot => {
    const currentCount = documentSnapshot.exists ? documentSnapshot.data().count : 0

    counterRef.set({
      count: Number(currentCount) + 1
    })
    .then(() => {
      console.log('counter has increased!')
    })
  })
})

이 코드는 방법의 완전한 예를 보여줍니다. https://gist.github.com/saintplay/3f965e0aea933a1129cc2c9a823e74d7

새 쓰기 배치 가져오기

WriteBatch batch = db.batch();

컬렉션 "NYC"에 새 값 추가

DocumentReference nycRef = db.collection("cities").document();
batch.set(nycRef, new City());

ID가 카운트이고 초기 값이 합계=0인 문서 유지 관리

추가 작업 중에는 다음과 같은 작업을 수행합니다.

DocumentReference countRef= db.collection("cities").document("count");
batch.update(countRef, "total", FieldValue.increment(1));

삭제 작업 중에는 다음과 같이 수행합니다.

DocumentReference countRef= db.collection("cities").document("count");
batch.update(countRef, "total", FieldValue.increment(-1));

항상 문서 개수 가져오기

DocumentReference nycRef = db.collection("cities").document("count");

모든 카운터를 처리할 NPM 패키지를 만들었습니다.

먼저 functions 디렉토리에 모듈을 설치합니다.

npm i adv-firestore-functions

그런 다음 다음과 같이 사용합니다.

import { eventExists, colCounter } from 'adv-firestore-functions';

functions.firestore
    .document('posts/{docId}')
    .onWrite(async (change: any, context: any) => {

    // don't run if repeated function
    if (await eventExists(context)) {
      return null;
    }

    await colCounter(change, context);
}

이벤트 및 기타 모든 것을 처리합니다.

모든 기능에 대해 범용 카운터로 설정하려는 경우:

import { eventExists, colCounter } from 'adv-firestore-functions';

functions.firestore
    .document('{colId}/{docId}')
    .onWrite(async (change: any, context: any) => {

    const colId = context.params.colId;

    // don't run if repeated function
    if (await eventExists(context) || colId.startsWith('_')) {
      return null;
    }

    await colCounter(change, context);
}

그리고 당신의 규칙을 잊지 마세요.

match /_counters/{document} {
  allow read;
  allow write: if false;
}

물론 다음과 같은 방법으로 액세스할 수 있습니다.

const collectionPath = 'path/to/collection';
const colSnap = await db.doc('_counters/' + collectionPath).get();
const count = colSnap.get('count');

자세히 보기: https://code.build/p/9DicAmrnRoK4uk62Hw1bEV/firestore-counters GitHub: https://github.com/jdgamble555/adv-firestore-functions

트랜잭션을 사용하여 데이터베이스 쓰기의 성공 수신기 내 개수를 업데이트합니다.

FirebaseFirestore.getInstance().runTransaction(new Transaction.Function<Long>() {
                @Nullable
                @Override
                public Long apply(@NonNull Transaction transaction) throws FirebaseFirestoreException {
                    DocumentSnapshot snapshot = transaction
                            .get(pRefs.postRef(forumHelper.getPost_id()));
                    long newCount;
                    if (b) {
                        newCount = snapshot.getLong(kMap.like_count) + 1;
                    } else {
                        newCount = snapshot.getLong(kMap.like_count) - 1;
                    }

                    transaction.update(pRefs.postRef(forumHelper.getPost_id()),
                            kMap.like_count, newCount);

                    return newCount;
                }
            });

언급URL : https://stackoverflow.com/questions/46553314/how-to-get-a-count-of-number-of-documents-in-a-collection-with-cloud-firestore

반응형