เป็นไปได้หรือไม่ที่จะนับว่ามีกี่รายการที่คอลเลกชันใช้ฐานข้อมูล Firebase ใหม่คือ Cloud Firestore
ถ้าเป็นเช่นนั้นฉันจะทำอย่างไร
เป็นไปได้หรือไม่ที่จะนับว่ามีกี่รายการที่คอลเลกชันใช้ฐานข้อมูล Firebase ใหม่คือ Cloud Firestore
ถ้าเป็นเช่นนั้นฉันจะทำอย่างไร
คำตอบ:
เช่นเดียวกับหลาย ๆ คำถามคำตอบคือ - มันขึ้นอยู่กับ
คุณควรระมัดระวังในการจัดการข้อมูลจำนวนมากที่ส่วนหน้า ด้านบนของการทำให้ส่วนหน้าของคุณรู้สึกซบเซาFirestore ยังคิดค่าใช้จ่าย $ 0.60 ต่อการอ่านล้านครั้งที่คุณทำ
ใช้ด้วยความระมัดระวัง - ประสบการณ์ผู้ใช้ส่วนหน้าอาจได้รับความนิยม
การจัดการสิ่งนี้ในส่วนหน้าควรจะดีถ้าคุณไม่ได้ใช้ตรรกะมากเกินไปกับอาเรย์ที่ส่งคืน
db.collection('...').get().then(snap => {
size = snap.size // will return the collection size
});
ใช้ด้วยความระมัดระวัง - Firestore การร้องขอการอ่านอาจมีค่าใช้จ่ายสูง
การจัดการสิ่งนี้ในส่วนหน้านั้นไม่สามารถทำได้เนื่องจากมีศักยภาพมากเกินไปที่จะทำให้ระบบของผู้ใช้ช้าลง เราควรจัดการด้านเซิร์ฟเวอร์เซิร์ฟเวอร์นี้และคืนขนาดเท่านั้น
ข้อเสียของวิธีนี้คือคุณยังคงเรียกใช้การอ่าน firestore (เท่ากับขนาดของคอลเลกชันของคุณ) ซึ่งในระยะยาวอาจทำให้คุณต้องเสียค่าใช้จ่ายมากกว่าที่คาดไว้
ฟังก์ชั่นคลาวด์:
...
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
})
โซลูชันที่ปรับขนาดได้มากที่สุด
FieldValue.increment ()
ณ เมษายน 2019 FireStore ขณะนี้ช่วยให้การเพิ่มเคาน์เตอร์สมบูรณ์อะตอมและโดยไม่ต้องอ่านข้อมูลก่อน สิ่งนี้ช่วยให้มั่นใจว่าเรามีค่าตัวนับที่ถูกต้องแม้ว่าจะอัปเดตจากหลาย ๆ แหล่งพร้อมกัน (แก้ไขก่อนหน้านี้โดยใช้ธุรกรรม) ในขณะที่ยังลดจำนวนของฐานข้อมูลที่เราอ่าน
โดยการฟังเอกสารใด ๆ ที่ลบหรือสร้างเราสามารถเพิ่มหรือลบออกจากเขตข้อมูลนับที่อยู่ในฐานข้อมูล
ดูเอกสาร firestore - เคาน์เตอร์แบบกระจาย หรือดูที่Data Aggregationโดย Jeff Delaney ไกด์ของเขานั้นยอดเยี่ยมอย่างแท้จริงสำหรับทุกคนที่ใช้ 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;
});
ในส่วนหน้าคุณสามารถสอบถามฟิลด์ numberOfDocs นี้เพื่อรับขนาดของคอลเลกชัน
firestore.runTransaction { ... }
บล็อก numberOfDocs
นี้ประเด็นการแก้ไขเห็นพ้องด้วยการเข้าถึง
วิธีที่ง่ายที่สุดในการทำเช่นนั้นคือการอ่านขนาดของ "querySnapshot"
db.collection("cities").get().then(function(querySnapshot) {
console.log(querySnapshot.size);
});
นอกจากนี้คุณยังสามารถอ่านความยาวของอาร์เรย์เอกสารภายใน "querySnapshot"
querySnapshot.docs.length;
หรือถ้า "querySnapshot" ว่างเปล่าโดยการอ่านค่าว่างซึ่งจะส่งกลับค่าบูลีน
querySnapshot.empty;
db.collection.count()
ไม่ได้มีชนิดของ กำลังคิดที่จะทิ้งพวกเขาไว้เพื่อสิ่งนี้
เท่าที่ฉันรู้ว่าไม่มีวิธีแก้ปัญหาแบบบิวด์อินและเป็นไปได้เฉพาะในโหนด sdk เท่านั้น หากคุณมี
db.collection('someCollection')
คุณสามารถใช้ได้
.select([fields])
เพื่อกำหนดฟิลด์ที่คุณต้องการเลือก หากคุณเลือกว่างเปล่า () คุณจะได้รับเอกสารอ้างอิงหลายชุด
ตัวอย่าง:
db.collection('someCollection').select().get().then(
(snapshot) => console.log(snapshot.docs.length)
);
โซลูชันนี้เป็นเพียงการปรับให้เหมาะสมที่สุดสำหรับกรณีที่แย่ที่สุดในการดาวน์โหลดเอกสารทั้งหมดและไม่ได้เพิ่มจำนวนมากขึ้น!
select(['_id'])
เร็วกว่าselect()
นับเป็นตัวเลขที่ระมัดระวังของเอกสารสำหรับคอลเลกชันขนาดใหญ่ มันซับซ้อนเล็กน้อยกับฐานข้อมูล 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;
});
เหตุผลก็เพราะว่าทริกเกอร์ไฟร์เมฆทุกตัวต้องเป็น idempotent ตามที่เอกสารไฟเตอร์บอกว่า: 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 เหตุการณ์จะเหมือนกันในทุกกรณี น่าเสียดายที่คุณต้องมีการรวบรวม "กิจกรรม" ในฐานข้อมูลของคุณ
context.eventId
จะเหมือนกันในการเรียกหลายครั้งของทริกเกอร์เดียวกันหรือไม่ ในการทดสอบของฉันดูเหมือนว่าจะสอดคล้องกัน แต่ฉันไม่สามารถหาเอกสาร "ทางการ" ใด ๆ ที่ระบุสิ่งนี้
ในปี 2020 นี้ยังไม่พร้อมใช้งานใน Firebase SDK แต่มีให้ในFirebase Extensions (Beta)แต่มันค่อนข้างซับซ้อนในการติดตั้งและใช้ ...
แนวทางที่เหมาะสม
ผู้ช่วยเหลือ ... (สร้าง / ลบดูเหมือนซ้ำซ้อน แต่ราคาถูกกว่า 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;
}
ส่งออก Firestore hooks
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'));
ฉันเห็นด้วยกับ @Matthew มันจะมีค่าใช้จ่ายมากถ้าคุณทำแบบสอบถามดังกล่าว
[คำแนะนำสำหรับนักพัฒนาก่อนเริ่มโครงการของพวกเขา]
number
เนื่องจากเราได้เล็งเห็นสถานการณ์เช่นนี้ที่จุดเริ่มต้นที่เราจริงสามารถทำให้คอลเลกชันคือเคาน์เตอร์ที่มีเอกสารในการจัดเก็บเคาน์เตอร์ทั้งหมดในเขตข้อมูลที่มีชนิด
ตัวอย่างเช่น:
สำหรับการดำเนินการ CRUD แต่ละครั้งในการรวบรวมให้อัพเดตเอกสารตัวนับ:
ครั้งต่อไปเมื่อคุณต้องการรับจำนวนคอลเลกชันคุณเพียงแค่ต้องค้นหา / ชี้ไปที่ช่องเอกสาร [การดำเนินการอ่าน 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'}
ดังนั้นถ้าคุณจะไม่ลบคอลเลกชันคุณสามารถใช้อาร์เรย์เพื่อจัดเก็บรายชื่อคอลเลกชันแทนการสืบค้นคอลเลกชันทั้งหมดได้ทุกครั้ง
หวังว่ามันจะช่วย!
ไม่ขณะนี้ไม่มีการรองรับการรวบรวมข้อความค้นหาในตัว อย่างไรก็ตามมีบางสิ่งที่คุณสามารถทำได้
ที่แรกก็คือการบันทึกไว้ที่นี่ คุณสามารถใช้ธุรกรรมหรือฟังก์ชั่นคลาวด์เพื่อรักษาข้อมูลรวม:
ตัวอย่างนี้แสดงวิธีใช้ฟังก์ชันเพื่อติดตามจำนวนการจัดเรตในการจัดเก็บย่อยรวมถึงเรตติ้งเฉลี่ย
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
});
});
});
});
โซลูชันที่ jbb ที่กล่าวถึงนั้นมีประโยชน์ถ้าคุณต้องการนับเอกสารไม่บ่อยนัก ตรวจสอบให้แน่ใจว่าใช้select()
คำสั่งเพื่อหลีกเลี่ยงการดาวน์โหลดเอกสารทั้งหมด (ซึ่งมีแบนด์วิดท์จำนวนมากเมื่อคุณต้องการนับ) select()
มีให้ใช้งานใน SDK เซิร์ฟเวอร์เท่านั้นในตอนนี้ดังนั้นโซลูชันจะไม่ทำงานในแอปมือถือ
ไม่มีตัวเลือกโดยตรง คุณทำไม่db.collection("CollectionName").count()
ได้ ด้านล่างนี้เป็นสองวิธีที่คุณสามารถค้นหาจำนวนเอกสารภายในคอลเล็กชัน
db.collection("CollectionName").get().subscribe(doc=>{
console.log(doc.size)
})
ด้วยการใช้รหัสด้านบนเอกสารของคุณจะอ่านเท่ากับขนาดของเอกสารภายในคอลเลกชันและนั่นคือเหตุผลว่าทำไมต้องหลีกเลี่ยงการใช้วิธีการแก้ปัญหาข้างต้น
db.collection("CollectionName").doc("counts")get().subscribe(doc=>{
console.log(doc.count)
})
ด้านบนเราสร้างเอกสารที่มีการนับชื่อเพื่อเก็บข้อมูลการนับทั้งหมดคุณสามารถอัปเดตเอกสารการนับด้วยวิธีต่อไปนี้: -
ราคา wrt (Document Read = 1) และการดึงข้อมูลที่รวดเร็วทางออกข้างต้นนั้นดี
เพิ่มตัวนับโดยใช้admin.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
ให้มีการปรับปรุงซึ่งหมายความว่าคุณใช้เงินมากขึ้นในการเรียกใช้ฟังก์ชันที่ไม่จำเป็น (ถ้าคุณอัปเดตเอกสารในชุดสะสมของคุณ)
วิธีแก้ปัญหาคือ:
เขียนตัวนับใน doc ของ firebase ซึ่งคุณเพิ่มภายในธุรกรรมทุกครั้งที่คุณสร้างรายการใหม่
คุณเก็บจำนวนไว้ในฟิลด์ของรายการใหม่ของคุณ (เช่น: ตำแหน่ง: 4)
จากนั้นคุณสร้างดัชนีในฟิลด์นั้น (ตำแหน่ง DESC)
คุณสามารถข้ามขีด จำกัด + ด้วยแบบสอบถามได้ที่นี่ ("ตำแหน่ง", "<" x) .OrderBy ("ตำแหน่ง", DESC)
หวังว่านี่จะช่วยได้!
ฉันสร้างฟังก์ชั่นสากลโดยใช้แนวคิดทั้งหมดเหล่านี้เพื่อจัดการกับสถานการณ์ตอบโต้ทั้งหมด (ยกเว้นข้อความค้นหา)
ข้อยกเว้นเพียงอย่างเดียวคือเมื่อคุณเขียนหลายวินาทีมันทำให้คุณช้าลง ตัวอย่างจะชอบในโพสต์ที่มีแนวโน้ม มันเกินความจริงในโพสต์บล็อกและจะทำให้คุณเสียค่าใช้จ่ายมากขึ้น ฉันขอแนะนำให้สร้างฟังก์ชั่นแยกต่างหากในกรณีนี้โดยใช้เศษ: https://firebase.google.com/docs/firestore/solutions/counters
// 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');
นอกจากนี้คุณอาจต้องการสร้างงาน cron (ฟังก์ชั่นตามกำหนดการ) เพื่อลบกิจกรรมเก่าเพื่อประหยัดเงินในการจัดเก็บฐานข้อมูล อย่างน้อยคุณต้องมีแผนการเผาไหม้และอาจมีการกำหนดค่าเพิ่มเติมบางอย่าง คุณสามารถเรียกใช้ได้ทุกวันอาทิตย์เวลา 23.00 น. https://firebase.google.com/docs/functions/schedule-functions
นี่คือยังไม่ทดลองแต่ควรทำงานกับ tweaks ไม่กี่:
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กฎ:
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();
ซึ่งจะอัปเดตpostsCountโดยอัตโนมัติใน userDocument คุณสามารถเพิ่มอีกหนึ่งรายการเข้ากับการนับด้วยวิธีนี้ได้อย่างง่ายดาย นี่เป็นเพียงการให้คุณทราบว่าคุณจะทำให้สิ่งต่าง ๆ เป็นอัตโนมัติ ฉันยังให้คุณอีกวิธีหนึ่งในการลบกิจกรรม คุณต้องอ่านแต่ละวันเพื่อลบมันดังนั้นมันจะไม่ช่วยให้คุณลบในภายหลังจริงๆเพียงแค่ทำให้ฟังก์ชั่นช้าลง
/**
* 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 ที่เราใช้รหัสซ้ำและข้อมูลสามารถประหยัดเงินได้
ใช้เวลาสักครู่ในการทำให้งานนี้ขึ้นอยู่กับคำตอบบางข้อด้านบนดังนั้นฉันคิดว่าฉันจะแบ่งปันให้ผู้อื่นใช้ ฉันหวังว่ามันจะมีประโยชน์
'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;
});
ฉันลองใช้วิธีการต่าง ๆ มากมาย และสุดท้ายฉันก็ปรับปรุงวิธีการอย่างใดอย่างหนึ่ง ก่อนอื่นคุณต้องสร้างการรวบรวมแยกต่างหากและบันทึกเหตุการณ์ทั้งหมดที่นั่น ประการที่สองคุณต้องสร้างแลมบ์ดาใหม่เพื่อให้ถูกเรียกตามเวลา แลมบ์ดานี้จะนับเหตุการณ์ในการรวบรวมกิจกรรมและเอกสารกิจกรรมที่ชัดเจน รายละเอียดรหัสในบทความ https://medium.com/@ihor.malaniuk/how-to-count-documents-in-google-cloud-firestore-b0e65863aeca
แบบสอบถามนี้จะส่งผลให้จำนวนเอกสาร
this.db.collection(doc).get().subscribe((data) => {
count = data.docs.length;
});
console.log(count)
สิ่งนี้ใช้การนับเพื่อสร้าง ID เฉพาะที่เป็นตัวเลข ในการใช้งานของฉันฉันจะไม่ลดลงเรื่อยๆ แม้ว่าdocument
จะต้องลบ ID นั้นก็ตาม
เมื่อมีcollection
การสร้างที่ต้องการค่าตัวเลขที่ไม่ซ้ำกัน
appData
ด้วยหนึ่งเอกสารที่set
มี.doc
idonly
uniqueNumericIDAmount
เป็น 0 ในfirebase firestore console
doc.data().uniqueNumericIDAmount + 1
เป็นรหัสตัวเลขที่เป็นเอกลักษณ์appData
คอลเล็กชันuniqueNumericIDAmount
ด้วยfirebase.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);
});
});
firebaseFirestore.collection("...").addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
int Counter = documentSnapshots.size();
}
});