क्लाउड फायरस्टार संग्रह गिनती


149

क्या यह गणना करना संभव है कि नए फायरबेस डेटाबेस, क्लाउड फायरस्टार का उपयोग करके किसी संग्रह में कितने आइटम हैं?

अगर ऐसा है, तो मैं वह कैसे करू?


जवाबों:


190

अपडेट (अप्रैल 2019) - FieldValue.increment (बड़े संग्रह समाधान देखें)


कई सवालों के साथ, जवाब है - यह निर्भर करता है

सामने के छोर पर बड़ी मात्रा में डेटा को संभालते समय आपको बहुत सावधान रहना चाहिए। आपके सामने के छोर को सुस्त महसूस करने के लिए, फायरस्टार आपसे $ 0.60 प्रति मिलियन की दर से आपके द्वारा पढ़े जाने का शुल्क भी लेता है


छोटा संग्रह (100 से कम दस्तावेज)

देखभाल के साथ उपयोग करें - फ्रंटेंड उपयोगकर्ता अनुभव हिट हो सकता है

जब तक आप इस लौटे सरणी के साथ बहुत अधिक तर्क नहीं कर रहे हैं तब तक इसे फ्रंट एंड पर संभालना ठीक होना चाहिए।

db.collection('...').get().then(snap => {
   size = snap.size // will return the collection size
});

मध्यम संग्रह (100 से 1000 दस्तावेज)

देखभाल के साथ उपयोग करें - फायरस्टार पढ़े गए इनवोकेशन में बहुत अधिक लागत आ सकती है

इसे फ्रंट एंड पर हैंडल करना संभव नहीं है क्योंकि इसमें यूजर्स सिस्टम को धीमा करने की बहुत अधिक क्षमता है। हमें इस लॉजिक सर्वर साइड को हैंडल करना चाहिए और केवल साइज वापस करना चाहिए।

इस पद्धति का दोष यह है कि आप अभी भी फायरस्टार रीड्स (आपके संग्रह के आकार के बराबर) को आमंत्रित कर रहे हैं, जो लंबे समय में आपको उम्मीद से अधिक लागत खत्म कर सकता है।

बादल समारोह:

...
db.collection('...').get().then(snap => {
    res.status(200).send({length: snap.size});
});

फ़्रंट एंड:

yourHttpClient.post(yourCloudFunctionUrl).toPromise().then(snap => {
     size = snap.length // will return the collection size
})

बड़ा संग्रह (1000+ दस्तावेज)

सबसे स्केलेबल समाधान


FieldValue.increment ()

अप्रैल 2019 के अनुसार फायरस्टार अब काउंटर्स बढ़ाने की अनुमति देता है, पूरी तरह से परमाणु रूप से, और पूर्व डेटा को पढ़े बिनायह सुनिश्चित करता है कि एक साथ कई स्रोतों से अपडेट करते समय (पहले लेन-देन का उपयोग करके हल किया गया) भी हमारे पास सही काउंटर वैल्यू है, जबकि डेटाबेस रीड्स की संख्या को कम करके हम प्रदर्शन करते हैं।


किसी भी दस्तावेज़ को डिलीट करने या बनाने से हम डेटाबेस में बैठे एक काउंट फ़ील्ड से जोड़ या हटा सकते हैं।

फायरस्टार डॉक्स - डिस्ट्रिब्यूटेड काउंटर्स देखें या डेटा एग्रीगेशन पर एक नजर डालें जेफ डेलानी द्वारा । AngularFire का उपयोग करने वाले किसी के लिए भी उसके मार्गदर्शक वास्तव में शानदार हैं, लेकिन उसके पाठों को अन्य रूपरेखाओं पर भी ले जाना चाहिए।

बादल समारोह:

export const documentWriteListener = 
    functions.firestore.document('collection/{documentUid}')
    .onWrite((change, context) => {

    if (!change.before.exists) {
        // New document Created : add one to count

        db.doc(docRef).update({numberOfDocs: FieldValue.increment(1)});

    } else if (change.before.exists && change.after.exists) {
        // Updating existing document : Do nothing

    } else if (!change.after.exists) {
        // Deleting document : subtract one from count

        db.doc(docRef).update({numberOfDocs: FieldValue.increment(-1)});

    }

return;
});

अब दृश्यपटल पर आप संग्रह के आकार को प्राप्त करने के लिए इस नंबरऑफ़डॉक्स फ़ील्ड को क्वेरी कर सकते हैं।


17
बड़े संग्रह के लिए महान समाधान! मैं बस इतना जोड़ना चाहूंगा कि कार्यान्वयनकर्ताओं को रीड को लपेटना चाहिए और firestore.runTransaction { ... }ब्लॉक में लिखना चाहिए । यह पहुँच के साथ समसामयिक समस्याओं को हल करता है numberOfDocs
efemoney

2
ये विधियाँ रिकॉर्ड की संख्या का उपयोग कर रही हैं। यदि आप एक काउंटर का उपयोग करते हैं और लेन-देन का उपयोग करते हुए काउंटर को बढ़ाते हैं, तो क्या क्लाउड फ़ंक्शन के अतिरिक्त लागत और आवश्यकता के बिना एक ही परिणाम प्राप्त नहीं होगा?
user3836415

10
बड़े संग्रहों का समाधान न के बराबर है और न ही किसी भी पैमाने पर काम करता है। फायरस्टार दस्तावेज़ ट्रिगर कम से कम एक बार चलाने की गारंटी है, लेकिन कई बार चल सकता है। जब ऐसा होता है, तब भी लेन-देन के अंदर अद्यतन बनाए रखना एक से अधिक बार चल सकता है, जो आपको एक गलत संख्या देगा। जब मैंने यह कोशिश की, मैं एक समय में एक दर्जन से कम दस्तावेज़ रचनाओं के साथ मुद्दों में भाग गया।
टाईम पोलाक

2
हाय @TymPollack। मैंने क्लाउड ट्रिगर्स का उपयोग करते हुए कुछ असंगत व्यवहार पर ध्यान दिया है। आपके द्वारा अनुभव किए गए व्यवहार की व्याख्या करने के लिए कोई भी मौका जो आप मुझे और लेख या मंच से जोड़ सकते हैं?
मैथ्यू मुलिन

2
जब आप db.collection ('...') का उपयोग करते समय पूरा संग्रह और डेटा पढ़ रहे हैं तो @cmprogram ... इसलिए जब आपको डेटा की आवश्यकता नहीं है तो आप सही हैं - आप आसानी से एक सूची के लिए अनुरोध कर सकते हैं कलेक्शन आईडी (कलेक्शन डॉक्यूमेंट्स डेटा नहीं) और इसे एक रीड के रूप में गिना जाता है।
एटरेशकोव

25

ऐसा करने का सबसे सरल तरीका "क्वेरीस्नापशॉट" के आकार को पढ़ना है।

db.collection("cities").get().then(function(querySnapshot) {      
    console.log(querySnapshot.size); 
});

आप "querySnapshot" के अंदर डॉक्स सरणी की लंबाई भी पढ़ सकते हैं।

querySnapshot.docs.length;

या अगर एक "querySnapshot" खाली मान पढ़ने से खाली है, जो एक बूलियन मान लौटाएगा।

querySnapshot.empty;

73
ध्यान रखें कि प्रत्येक दस्तावेज़ में एक "लागत" होती है। तो अगर आप एक संग्रह में 100 आइटम इस तरह से गिनते हैं, तो आपसे 100 रीड के लिए शुल्क लिया जा रहा है!
जॉर्ज

सही है, लेकिन एक संग्रह में दस्तावेजों की संख्या को योग करने का कोई अन्य तरीका नहीं है। और यदि आपने पहले ही संग्रह प्राप्त कर लिया है, तो "डॉक्स" एरे को पढ़ने के लिए किसी और अधिक लाने की आवश्यकता नहीं होगी, इसलिए अधिक रीडिंग "लागत" नहीं होगी।
ओमपाल

5
यह स्मृति में सभी दस्तावेजों को पढ़ता है! सौभाग्य है कि बड़े डेटासेट के लिए ...
दान Dascalescu

85
यह वास्तव में अविश्वसनीय है कि फायरबेस फायरस्टार के पास इस तरह का नहीं है db.collection.count()। उन्हें केवल इसके लिए छोड़ने की सोच
ब्लू बॉट

8
विशेष रूप से बड़े संग्रह के लिए, हमें चार्ज करना उचित नहीं है जैसे कि हमने वास्तव में डाउनलोड किया और सभी दस्तावेजों का उपयोग किया। तालिका (संग्रह) के लिए गणना एक ऐसी मूल विशेषता है। उनके मूल्य निर्धारण मॉडल और फायरस्टार को 2017 में लॉन्च किया गया था, यह देखते हुए कि यह अविश्वसनीय है कि Google एक संग्रह का आकार प्राप्त करने का कोई वैकल्पिक तरीका प्रदान नहीं करता है। जब तक वे इसे लागू नहीं करते, उन्हें कम से कम इसके लिए चार्ज करने से बचना चाहिए।
निबाना

23

जहां तक ​​मुझे पता है कि इसके लिए कोई बिल्ड-इन समाधान नहीं है और यह केवल नोड एसडीके में ही संभव है। अगर आपके पास एक है

db.collection('someCollection')

आप उपयोग कर सकते हैं

.select([fields])

परिभाषित करना कि आप किस क्षेत्र का चयन करना चाहते हैं। यदि आप एक खाली चयन () करते हैं तो आपको दस्तावेज़ संदर्भों की एक सरणी मिलेगी।

उदाहरण:

db.collection('someCollection').select().get().then( (snapshot) => console.log(snapshot.docs.length) );

यह समाधान केवल सभी दस्तावेजों को डाउनलोड करने के सबसे खराब स्थिति के लिए एक अनुकूलन है और बड़े संग्रह पर स्केल नहीं करता है!

इस पर भी एक नज़र डालें:
क्लाउड फायरस्टोर के साथ संग्रह में दस्तावेजों की संख्या की गणना कैसे करें


मेरे अनुभव में, इससे select(['_id'])तेज हैselect()
JAnton

13

बड़े संग्रह के लिए दस्तावेजों की संख्या की सावधानीपूर्वक गणना करें । यह फायरस्टार डेटाबेस के साथ थोड़ा जटिल है यदि आप हर संग्रह के लिए एक पूर्व-निर्धारित काउंटर चाहते हैं।

इस तरह कोड इस मामले में काम नहीं करता है:

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 नामक संपत्ति है । यदि फ़ंक्शन को एक ही इवेंट के लिए कई बार हैंडल किया गया है, तो इवेंट आईडी सभी मामलों में समान होगी। दुर्भाग्य से, आपके डेटाबेस में "ईवेंट" संग्रह होना चाहिए।


2
वे इसे फिर से परिभाषित कर रहे हैं जैसे कि यह व्यवहार 1.0 रिलीज में तय किया जाएगा। Amazon AWS फ़ंक्शंस उसी समस्या से ग्रस्त हैं। गिनती के क्षेत्रों के रूप में कुछ इतना सरल जटिल और महंगा हो जाता है।
मार्क

यह कोशिश करने जा रहा है क्योंकि यह एक बेहतर समाधान की तरह लगता है। क्या आप वापस जाते हैं और कभी भी अपने ईवेंट संग्रह को शुद्ध करते हैं? मैं सिर्फ एक तारीख क्षेत्र को जोड़ने और एक दिन या कुछ पुराने से शुद्ध करने के बारे में सोच रहा था ताकि डेटा को छोटा रखा जा सके (संभवतः 1mil + घटनाओं / दिन)। जब तक FS में एक आसान तरीका है कि ... केवल FS कुछ महीनों का उपयोग कर रहा है।
टाईम पोल्क

1
क्या हम यह सत्यापित कर सकते हैं कि context.eventIdहमेशा एक ही ट्रिगर के कई इनवोकेशन पर समान होगा? मेरे परीक्षण में यह सुसंगत प्रतीत होता है, लेकिन मैं इसे बताते हुए कोई "आधिकारिक" दस्तावेज नहीं ढूंढ पा रहा हूं।
माइक मैकलिन

2
इसलिए थोड़ी देर इसका उपयोग करने के बाद, मैंने पाया है कि, जबकि यह समाधान ठीक एक लिखने के साथ काम करता है, जो बहुत अच्छा है, अगर बहुत से एक ही बार में कई डॉक्स से आग लगने के बारे में लिखा जा रहा है और एक ही गिनती डॉक्टर को अपडेट करने की कोशिश कर रहा है, तो आप कर सकते हैं फायरस्टार से विवाद त्रुटियों को प्राप्त करें। क्या आपने उन लोगों का सामना किया है, और आप इसके आसपास कैसे पहुंचे? (त्रुटि: 10 संक्षिप्त: इन दस्तावेजों पर बहुत विवाद। कृपया फिर से प्रयास करें।)
तैमूर पोलैक

1
पर @TymPollack नज़र वितरित काउंटरों दस्तावेज़ राईट प्रति सेकंड लगभग एक अद्यतन के लिए सीमित हैं
जेमी

8

2020 में यह अभी भी फायरबेस एसडीके में उपलब्ध नहीं है लेकिन यह फायरबेस एक्सटेंशन (बीटा) में उपलब्ध है हालांकि यह सेटअप और उपयोग के लिए बहुत जटिल है ...

एक उचित दृष्टिकोण

हेल्पर्स ... (क्रिएट / डिलीट बेमानी लगता है लेकिन onUpdate से सस्ता है)

export const onCreateCounter = () => async (
  change,
  context
) => {
  const collectionPath = change.ref.parent.path;
  const statsDoc = db.doc("counters/" + collectionPath);
  const countDoc = {};
  countDoc["count"] = admin.firestore.FieldValue.increment(1);
  await statsDoc.set(countDoc, { merge: true });
};

export const onDeleteCounter = () => async (
  change,
  context
) => {
  const collectionPath = change.ref.parent.path;
  const statsDoc = db.doc("counters/" + collectionPath);
  const countDoc = {};
  countDoc["count"] = admin.firestore.FieldValue.increment(-1);
  await statsDoc.set(countDoc, { merge: true });
};

export interface CounterPath {
  watch: string;
  name: string;
}

निर्यात फायरस्टार हुक


export const Counters: CounterPath[] = [
  {
    name: "count_buildings",
    watch: "buildings/{id2}"
  },
  {
    name: "count_buildings_subcollections",
    watch: "buildings/{id2}/{id3}/{id4}"
  }
];


Counters.forEach(item => {
  exports[item.name + '_create'] = functions.firestore
    .document(item.watch)
    .onCreate(onCreateCounter());

  exports[item.name + '_delete'] = functions.firestore
    .document(item.watch)
    .onDelete(onDeleteCounter());
});

कार्रवाई में

बिल्डिंग रूट संग्रह और सभी उप संग्रह को ट्रैक किया जाएगा।

यहां छवि विवरण दर्ज करें

यहाँ /counters/रूट पथ के तहत

यहां छवि विवरण दर्ज करें

अब संग्रह मायने रखता है स्वचालित रूप से और अंततः अद्यतन करेगा! यदि आपको एक गिनती की आवश्यकता है, तो बस संग्रह पथ का उपयोग करें और इसके साथ उपसर्ग करें counters

const collectionPath = 'buildings/138faicnjasjoa89/buildingContacts';
const collectionCount = await db
  .doc('counters/' + collectionPath)
  .get()
  .then(snap => snap.get('count'));

क्या यह एक ही सीमा के अधीन नहीं है "प्रति सेकंड 1 दस्तावेज़ अद्यतन"?
अयप्पा

हां, लेकिन यह अंततः सुसंगत है, जिसका अर्थ है कि संग्रह की गिनती अंततः वास्तविक संग्रह की संख्या के साथ संरेखित होगी, यह लागू करने का सबसे आसान समाधान है और कई मामलों में गिनती में एक संक्षिप्त अंतराल स्वीकार्य है।
बेन वाइंडिंग

7

मैं @ मैथ्यू के साथ सहमत हूं, अगर आप इस तरह की क्वेरी करते हैं तो बहुत अधिक खर्च होगा ।

[विकासकर्ताओं के लिए सलाहें आगे बढ़ती हैं]

चूंकि हमारे पास इस स्थिति की शुरुआत है, इसलिए हम वास्तव में प्रकार के साथ एक क्षेत्र में सभी काउंटरों को संग्रहीत करने के लिए एक दस्तावेज़ के साथ एक संग्रह अर्थात् काउंटर बना सकते हैं number

उदाहरण के लिए:

संग्रह पर प्रत्येक CRUD ऑपरेशन के लिए, काउंटर दस्तावेज़ अपडेट करें:

  1. जब आप एक नया संग्रह / सबकोलिक्शन बनाते हैं : (काउंटर में +1) [1 राइट ऑपरेशन]
  2. जब आप कोई संग्रह / सब-कलेक्शन हटाते हैं : (काउंटर में -1) [1 राइट ऑपरेशन]
  3. जब आप किसी मौजूदा संग्रह / सबकोलेक्शन को अपडेट करते हैं, तो काउंटर दस्तावेज़ पर कुछ भी न करें: (0)
  4. जब आप एक मौजूदा संग्रह / सब-कलेक्शन पढ़ते हैं , तो काउंटर दस्तावेज़ पर कुछ भी न करें: (0)

अगली बार, जब आप संग्रह की संख्या प्राप्त करना चाहते हैं, तो आपको बस दस्तावेज़ फ़ील्ड को क्वेरी / इंगित करना होगा। [1 रीड ऑपरेशन]

इसके अलावा, आप संग्रह नाम को किसी सरणी में संग्रहीत कर सकते हैं, लेकिन यह मुश्किल होगा, फायरबेस में सरणी की स्थिति नीचे दी गई है:

// we send this
['a', 'b', 'c', 'd', 'e']
// Firebase stores this
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}

// since the keys are numeric and sequential,
// if we query the data, we get this
['a', 'b', 'c', 'd', 'e']

// however, if we then delete a, b, and d,
// they are no longer mostly sequential, so
// we do not get back an array
{2: 'c', 4: 'e'}

इसलिए, यदि आप संग्रह को हटाने नहीं जा रहे हैं, तो आप वास्तव में हर बार सभी संग्रह को क्वेरी करने के बजाय संग्रह नाम की सूची संग्रहीत करने के लिए सरणी का उपयोग कर सकते हैं।

आशा करता हूँ की ये काम करेगा!


एक छोटे संग्रह के लिए, हो सकता है। लेकिन ध्यान रखें कि फायरस्टार दस्तावेज़ आकार की सीमा ~ 1MB है, जो, अगर किसी संग्रह में दस्तावेज़ आईडी ऑटो-जनरेट (20 बाइट्स) हैं, तो आप केवल सरणी रखने वाले डॉक्टर से पहले ~ 52,425 स्टोर कर पाएंगे। बहुत बडा है। मैं अनुमान लगाता हूं कि आप हर 50,000 तत्वों में एक नया डॉक्टर बना सकते हैं, लेकिन फिर उन सरणियों को बनाए रखना पूरी तरह से असहनीय होगा। इसके अलावा, जैसा कि डॉक्टर का आकार बढ़ता है, इसे पढ़ने और अपडेट करने में अधिक समय लगेगा, जो अंततः उस समय किसी अन्य संचालन को विवाद में डाल देगा।
टाइम पोलक

5

नहीं, अभी एकत्रीकरण प्रश्नों के लिए कोई अंतर्निहित समर्थन नहीं है। हालाँकि कुछ चीजें हैं जो आप कर सकते हैं।

पहले यहाँ पर प्रलेखित है । समग्र जानकारी बनाए रखने के लिए आप लेनदेन या क्लाउड फ़ंक्शंस का उपयोग कर सकते हैं:

यह उदाहरण दिखाता है कि एक सबकोलेक्शन में रेटिंग की संख्या के साथ-साथ औसत रेटिंग का ट्रैक रखने के लिए फ़ंक्शन का उपयोग कैसे करें।

exports.aggregateRatings = firestore
  .document('restaurants/{restId}/ratings/{ratingId}')
  .onWrite(event => {
    // Get value of the newly added rating
    var ratingVal = event.data.get('rating');

    // Get a reference to the restaurant
    var restRef = db.collection('restaurants').document(event.params.restId);

    // Update aggregations in a transaction
    return db.transaction(transaction => {
      return transaction.get(restRef).then(restDoc => {
        // Compute new number of ratings
        var newNumRatings = restDoc.data('numRatings') + 1;

        // Compute new average rating
        var oldRatingTotal = restDoc.data('avgRating') * restDoc.data('numRatings');
        var newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;

        // Update restaurant info
        return transaction.update(restRef, {
          avgRating: newAvgRating,
          numRatings: newNumRatings
        });
      });
    });
});

जेबीबी द्वारा उल्लिखित समाधान भी उपयोगी है यदि आप केवल दस्तावेजों को बार-बार गिनना चाहते हैं। select()प्रत्येक दस्तावेज़ को डाउनलोड करने से बचने के लिए कथन का उपयोग करना सुनिश्चित करें (यह बहुत अधिक बैंडविड्थ है जब आपको केवल एक गणना की आवश्यकता होती है)। select()अभी के लिए सर्वर SDK में उपलब्ध है ताकि समाधान एक मोबाइल ऐप में काम न करे।


1
यह समाधान बेकार नहीं है, इसलिए कोई भी ट्रिगर जो एक से अधिक बार आग लगाता है वह आपकी रेटिंग और औसत को फेंक देगा।
टाइम पोलक

4

कोई प्रत्यक्ष विकल्प उपलब्ध नहीं है। आप ऐसा नहीं कर सकते db.collection("CollectionName").count()। नीचे दो तरीके दिए गए हैं जिनके द्वारा आप एक संग्रह के भीतर दस्तावेजों की संख्या की गिनती पा सकते हैं।

1: - संग्रह में सभी दस्तावेज प्राप्त करें और फिर उसका आकार प्राप्त करें। (सबसे अच्छा समाधान नहीं)

db.collection("CollectionName").get().subscribe(doc=>{
console.log(doc.size)
})

उपरोक्त कोड का उपयोग करके आपका दस्तावेज़ पढ़ता है एक संग्रह के भीतर दस्तावेजों के आकार के बराबर होगा और यही कारण है कि किसी को उपरोक्त समाधान का उपयोग करने से बचना चाहिए।

2: - अपने संग्रह के साथ एक अलग दस्तावेज़ बनाएँ जो संग्रह में दस्तावेज़ों की संख्या की गणना करेगा। (सर्वश्रेष्ठ समाधान)

db.collection("CollectionName").doc("counts")get().subscribe(doc=>{
console.log(doc.count)
})

ऊपर हमने सभी गणना की जानकारी संग्रहीत करने के लिए नाम के साथ एक दस्तावेज बनाया है। आप निम्नलिखित तरीके से गणना दस्तावेज़ को अपडेट कर सकते हैं: -

  • दस्तावेज़ काउंट पर एक फायरस्टार ट्रिगर बनाएँ
  • जब कोई नया दस्तावेज़ बनाया जाता है तो काउंट डॉक्यूमेंट की गिनती संपत्ति में वृद्धि।
  • दस्तावेज़ हटाए जाने पर काउंट डॉक्यूमेंट की गिनती संपत्ति में कमी।

wrt मूल्य (दस्तावेज़ पढ़ें = 1) और तेजी से डेटा पुनर्प्राप्ति उपरोक्त समाधान अच्छा है।


3

व्यवस्थापन का उपयोग कर एक काउंटर बढ़ाएँ। Firestore.FieldValue.increment :

exports.onInstanceCreate = functions.firestore.document('projects/{projectId}/instances/{instanceId}')
  .onCreate((snap, context) =>
    db.collection('projects').doc(context.params.projectId).update({
      instanceCount: admin.firestore.FieldValue.increment(1),
    })
  );

exports.onInstanceDelete = functions.firestore.document('projects/{projectId}/instances/{instanceId}')
  .onDelete((snap, context) =>
    db.collection('projects').doc(context.params.projectId).update({
      instanceCount: admin.firestore.FieldValue.increment(-1),
    })
  );

इस उदाहरण में हम instanceCountप्रोजेक्ट में हर बार डॉक्यूमेंट को instancesउप संग्रह में शामिल करने के लिए एक क्षेत्र में वृद्धि करते हैं । यदि फ़ील्ड अभी तक मौजूद नहीं है, तो इसे बनाया जाएगा और 1 के लिए बढ़ाया जाएगा।

वृद्धिशील आंतरिक रूप से लेन-देन होता है, लेकिन यदि आपको प्रत्येक 1 सेकंड से अधिक बार वेतन वृद्धि की आवश्यकता है, तो आपको एक वितरित काउंटर का उपयोग करना चाहिए ।

इसे लागू करना बेहतर है onCreateऔर onDeleteइसके बजाय onWriteआप onWriteअपडेट के लिए कॉल करेंगे जिसका मतलब है कि आप अनावश्यक फ़ंक्शन इनवॉइस पर अधिक पैसा खर्च कर रहे हैं (यदि आप अपने संग्रह में डॉक्स अपडेट करते हैं)।


2

एक समाधान यह है:

फायरबेस डॉक में एक काउंटर लिखें, जिसे आप एक लेन-देन के भीतर बढ़ाते हैं जब आप एक नई प्रविष्टि बनाते हैं

आप अपनी नई प्रविष्टि (यानी: स्थिति: 4) के क्षेत्र में गिनती को संग्रहीत करते हैं।

फिर आप उस फ़ील्ड (स्थिति DESC) पर एक इंडेक्स बनाते हैं।

आप एक क्वेरी के साथ स्किप + लिमिट कर सकते हैं। कहीं भी ("स्थिति", "<" x) ।ऑडरबाय ("स्थिति", DESC)

उम्मीद है की यह मदद करेगा!


1

मैंने सभी काउंटर स्थितियों (प्रश्नों को छोड़कर) को संभालने के लिए इन सभी विचारों का उपयोग करके एक सार्वभौमिक फ़ंक्शन बनाया।

एकमात्र अपवाद तब होगा जब ऐसा करने वाले एक सेकंड लिखते हैं, यह आपको धीमा कर देता है। एक उदाहरण एक ट्रेंडिंग पोस्ट पर पसंद किया जाएगा । उदाहरण के लिए, यह एक ब्लॉग पोस्ट पर ओवरकिल है, और आपको अधिक खर्च करेगा। मेरा सुझाव है कि शार्द का उपयोग करते हुए उस मामले में एक अलग फ़ंक्शन बनाएं: https://firebase.google.com/docs/firestore/solutions/colutionsers

// trigger collections
exports.myFunction = functions.firestore
    .document('{colId}/{docId}')
    .onWrite(async (change: any, context: any) => {
        return runCounter(change, context);
    });

// trigger sub-collections
exports.mySubFunction = functions.firestore
    .document('{colId}/{docId}/{subColId}/{subDocId}')
    .onWrite(async (change: any, context: any) => {
        return runCounter(change, context);
    });

// add change the count
const runCounter = async function (change: any, context: any) {

    const col = context.params.colId;

    const eventsDoc = '_events';
    const countersDoc = '_counters';

    // ignore helper collections
    if (col.startsWith('_')) {
        return null;
    }
    // simplify event types
    const createDoc = change.after.exists && !change.before.exists;
    const updateDoc = change.before.exists && change.after.exists;

    if (updateDoc) {
        return null;
    }
    // check for sub collection
    const isSubCol = context.params.subDocId;

    const parentDoc = `${countersDoc}/${context.params.colId}`;
    const countDoc = isSubCol
        ? `${parentDoc}/${context.params.docId}/${context.params.subColId}`
        : `${parentDoc}`;

    // collection references
    const countRef = db.doc(countDoc);
    const countSnap = await countRef.get();

    // increment size if doc exists
    if (countSnap.exists) {
        // createDoc or deleteDoc
        const n = createDoc ? 1 : -1;
        const i = admin.firestore.FieldValue.increment(n);

        // create event for accurate increment
        const eventRef = db.doc(`${eventsDoc}/${context.eventId}`);

        return db.runTransaction(async (t: any): Promise<any> => {
            const eventSnap = await t.get(eventRef);
            // do nothing if event exists
            if (eventSnap.exists) {
                return null;
            }
            // add event and update size
            await t.update(countRef, { count: i });
            return t.set(eventRef, {
                completed: admin.firestore.FieldValue.serverTimestamp()
            });
        }).catch((e: any) => {
            console.log(e);
        });
        // otherwise count all docs in the collection and add size
    } else {
        const colRef = db.collection(change.after.ref.parent.path);
        return db.runTransaction(async (t: any): Promise<any> => {
            // update size
            const colSnap = await t.get(colRef);
            return t.set(countRef, { count: colSnap.size });
        }).catch((e: any) => {
            console.log(e);
        });;
    }
}

यह ईवेंट, वेतन वृद्धि और लेनदेन को संभालता है। इसमें सुंदरता यह है कि यदि आप किसी दस्तावेज़ की सटीकता के बारे में सुनिश्चित नहीं हैं (शायद अभी भी बीटा में हैं), तो आप काउंटर को हटा सकते हैं ताकि यह स्वचालित रूप से उन्हें अगले ट्रिगर पर जोड़ सके। हां, यह लागत, इसलिए इसे अन्यथा नष्ट न करें।

गिनती पाने के लिए इसी तरह की बात:

const collectionPath = 'buildings/138faicnjasjoa89/buildingContacts';
const colSnap = await db.doc('_counters/' + collectionPath).get();
const count = colSnap.get('count');

इसके अलावा, आप डेटाबेस स्टोरेज पर पैसे बचाने के लिए पुरानी घटनाओं को हटाने के लिए क्रोन जॉब (शेड्यूल फंक्शन) बनाना चाहते हैं। आपको कम से कम एक ब्लेज़ प्लान की आवश्यकता है, और कुछ और कॉन्फ़िगरेशन हो सकते हैं। आप इसे प्रत्येक रविवार को रात 11 बजे तक चला सकते हैं, उदाहरण के लिए। https://firebase.google.com/docs/functions/schedule-functions

यह अप्रयुक्त है , लेकिन कुछ ट्वीक के साथ काम करना चाहिए:

exports.scheduledFunctionCrontab = functions.pubsub.schedule('5 11 * * *')
    .timeZone('America/New_York')
    .onRun(async (context) => {

        // get yesterday
        const yesterday = new Date();
        yesterday.setDate(yesterday.getDate() - 1);

        const eventFilter = db.collection('_events').where('completed', '<=', yesterday);
        const eventFilterSnap = await eventFilter.get();
        eventFilterSnap.forEach(async (doc: any) => {
            await doc.ref.delete();
        });
        return null;
    });

और आखिरी, firestore.rules में संग्रह की रक्षा करना न भूलें :

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

अपडेट: प्रश्न

मेरे अन्य उत्तर को जोड़ना यदि आप क्वेरी काउंट को स्वचालित करना चाहते हैं, तो आप अपने क्लाउड फ़ंक्शन में इस संशोधित कोड का उपयोग कर सकते हैं:

    if (col === 'posts') {

        // counter reference - user doc ref
        const userRef = after ? after.userDoc : before.userDoc;
        // query reference
        const postsQuery = db.collection('posts').where('userDoc', "==", userRef);
        // add the count - postsCount on userDoc
        await addCount(change, context, postsQuery, userRef, 'postsCount');

    }
    return delEvents();

जो अपने आप पोस्ट को अपडेट कर देगा यूजरडीन्यूमेंट में। आप आसानी से इस तरह से कई मामलों में अन्य को जोड़ सकते हैं। यह आपको केवल यह बताता है कि आप चीजों को कैसे स्वचालित कर सकते हैं। मैंने आपको ईवेंट हटाने का एक और तरीका भी दिया। आपको इसे हटाने के लिए प्रत्येक तिथि को पढ़ना होगा, इसलिए यह आपको बाद में हटाने के लिए वास्तव में नहीं बचाएगा, बस फ़ंक्शन को धीमा कर देता है।

/**
 * Adds a counter to a doc
 * @param change - change ref
 * @param context - context ref
 * @param queryRef - the query ref to count
 * @param countRef - the counter document ref
 * @param countName - the name of the counter on the counter document
 */
const addCount = async function (change: any, context: any, 
  queryRef: any, countRef: any, countName: string) {

    // events collection
    const eventsDoc = '_events';

    // simplify event type
    const createDoc = change.after.exists && !change.before.exists;

    // doc references
    const countSnap = await countRef.get();

    // increment size if field exists
    if (countSnap.get(countName)) {
        // createDoc or deleteDoc
        const n = createDoc ? 1 : -1;
        const i = admin.firestore.FieldValue.increment(n);

        // create event for accurate increment
        const eventRef = db.doc(`${eventsDoc}/${context.eventId}`);

        return db.runTransaction(async (t: any): Promise<any> => {
            const eventSnap = await t.get(eventRef);
            // do nothing if event exists
            if (eventSnap.exists) {
                return null;
            }
            // add event and update size
            await t.set(countRef, { [countName]: i }, { merge: true });
            return t.set(eventRef, {
                completed: admin.firestore.FieldValue.serverTimestamp()
            });
        }).catch((e: any) => {
            console.log(e);
        });
        // otherwise count all docs in the collection and add size
    } else {
        return db.runTransaction(async (t: any): Promise<any> => {
            // update size
            const colSnap = await t.get(queryRef);
            return t.set(countRef, { [countName]: colSnap.size }, { merge: true });
        }).catch((e: any) => {
            console.log(e);
        });;
    }
}
/**
 * Deletes events over a day old
 */
const delEvents = async function () {

    // get yesterday
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);

    const eventFilter = db.collection('_events').where('completed', '<=', yesterday);
    const eventFilterSnap = await eventFilter.get();
    eventFilterSnap.forEach(async (doc: any) => {
        await doc.ref.delete();
    });
    return null;
}

मुझे आपको यह भी चेतावनी देनी चाहिए कि सार्वभौमिक कार्य प्रत्येक ऑनराइट कॉल काल पर चलेंगे। यह केवल onCreate पर और अपने विशिष्ट संग्रह के onDelete इंस्टेंस पर फ़ंक्शन चलाने के लिए सस्ता हो सकता है। NoSQL डेटाबेस की तरह हम उपयोग कर रहे हैं, बार-बार कोड और डेटा आपको पैसे बचा सकते हैं।


आसान पहुंच के लिए माध्यम पर इसके बारे में एक लेख लिखें।
अहमदलिबालोच


0

ऊपर दिए गए कुछ उत्तरों के आधार पर मुझे यह काम करने में थोड़ा समय लगा, इसलिए मैंने सोचा कि मैं इसे दूसरों के उपयोग के लिए साझा करूँगा। मुझे आशा है कि यह उपयोगी है।

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();

exports.countDocumentsChange = functions.firestore.document('library/{categoryId}/documents/{documentId}').onWrite((change, context) => {

    const categoryId = context.params.categoryId;
    const categoryRef = db.collection('library').doc(categoryId)
    let FieldValue = require('firebase-admin').firestore.FieldValue;

    if (!change.before.exists) {

        // new document created : add one to count
        categoryRef.update({numberOfDocs: FieldValue.increment(1)});
        console.log("%s numberOfDocs incremented by 1", categoryId);

    } else if (change.before.exists && change.after.exists) {

        // updating existing document : Do nothing

    } else if (!change.after.exists) {

        // deleting document : subtract one from count
        categoryRef.update({numberOfDocs: FieldValue.increment(-1)});
        console.log("%s numberOfDocs decremented by 1", categoryId);

    }

    return 0;
});

0

मैंने अलग-अलग तरीकों से बहुत कोशिश की है। और अंत में, मैं तरीकों में से एक में सुधार करता हूं। पहले आपको एक अलग संग्रह बनाने और सभी घटनाओं को बचाने की आवश्यकता है। दूसरा आपको समय के अनुसार ट्रिगर होने के लिए एक नया लैम्बडा बनाने की आवश्यकता है। यह मेमना ईवेंट संग्रह और स्पष्ट ईवेंट दस्तावेजों में घटनाओं की गणना करेगा। लेख में कोड विवरण। https://medium.com/@ihor.malaniuk/how-to-count-documents-in-google-cloud-firestore-b0e65863aeca


कृपया प्रासंगिक विवरण और कोड को उत्तर में शामिल करें, अपने ब्लॉग पोस्ट पर लोगों को इंगित करना वास्तव में StackOverflow की बात नहीं है।
डीबीएस

0

इस क्वेरी के परिणामस्वरूप दस्तावेज़ की गणना होगी।

this.db.collection(doc).get().subscribe((data) => {
      count = data.docs.length;
    });

console.log(count)

1
एक अच्छा समाधान नहीं है क्योंकि आप हर बार संग्रह से सभी दस्तावेज़ ला रहे हैं। इसमें काफी खर्च आएगा। एक बेहतर तरीका यह है कि इस संग्रह में हर बार एक नया दस्तावेज़ जोड़ने के लिए एक काउंटर स्थापित किया जाए ताकि आप बस कुछ दस्तावेज़ों को हजारों की जगह ले सकें।
कोरेंटिन हाउडेयेर

0

यह संख्यात्मक यूनिक आईडी बनाने के लिए गिनती का उपयोग करता है। मेरे उपयोग में, मैं कभी भी नहीं घटाऊंगा , तब भी जब documentउस आईडी को हटाने की आवश्यकता हो।

एक ऐसी collectionरचना पर जिसे अद्वितीय संख्यात्मक मान की आवश्यकता होती है

  1. एक संग्रह नामित appDataएक दस्तावेज़ के साथ, setसाथ .docआईडीonly
  2. uniqueNumericIDAmountमें 0 पर सेट करेंfirebase firestore console
  3. doc.data().uniqueNumericIDAmount + 1अद्वितीय संख्यात्मक आईडी के रूप में उपयोग करें
  4. के साथ अद्यतन appDataसंग्रहuniqueNumericIDAmountfirebase.firestore.FieldValue.increment(1)
firebase
    .firestore()
    .collection("appData")
    .doc("only")
    .get()
    .then(doc => {
        var foo = doc.data();
        foo.id = doc.id;

        // your collection that needs a unique ID
        firebase
            .firestore()
            .collection("uniqueNumericIDs")
            .doc(user.uid)// user id in my case
            .set({// I use this in login, so this document doesn't
                  // exist yet, otherwise use update instead of set
                phone: this.state.phone,// whatever else you need
                uniqueNumericID: foo.uniqueNumericIDAmount + 1
            })
            .then(() => {

                // upon success of new ID, increment uniqueNumericIDAmount
                firebase
                    .firestore()
                    .collection("appData")
                    .doc("only")
                    .update({
                        uniqueNumericIDAmount: firebase.firestore.FieldValue.increment(
                            1
                        )
                    })
                    .catch(err => {
                        console.log(err);
                    });
            })
            .catch(err => {
                console.log(err);
            });
    });

-1
firebaseFirestore.collection("...").addSnapshotListener(new EventListener<QuerySnapshot>() {
        @Override
        public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {

            int Counter = documentSnapshots.size();

        }
    });

1
यह उत्तर कोड के उदाहरण के रूप में अधिक संदर्भ का उपयोग कर सकता है।
शैलनिजा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.