Sandeep Choudhary
Sandeep Choudhary

Reputation: 260

Counter in Firestore database in Firebase

I'm creating an Android application wherein I have to keep count of YTD, MTD, and Daily record punched for an organization as well as individual users. I tried the approach where for every save of record, I have a counter collection where I save data like

ORG_ORGID_2020 (for YTD)
ORG_ORGID_202005 (for MTD)
ORG_ORGID_20200513 (for Daily data)

ORG_USER1_2020 (for YTD)
ORG_USER2_202005 (for MTD)
ORG_USER3_20200513 (for Daily data)

so that I don't have to read many documents while fetching reports. Now to minimize the reads I save properties in the above documents (org_ID, year (i.e. 2020), yearMonth (i.e.202005), and so on. I save above documents in the form of the counter object

public class Counter {

@DocumentId
private String id;
private long count;
private String dealerId;
private String userId;
private String year;
private String yearMonth;
private String yearMonthDate;

}

Not the issue arises when I have to update the counter. I tried using

private FieldValue count;

and was able to update the count properly using

Counter counter = new Counter();
    counter.setCount(FieldValue.increment(1));
    counter.setDealerId(intentDealer.getId());
    counter.setYear(strFullYear);
    batch.set(dealerYtdColRef, counter, SetOptions.merge());

but when I try to fetch the record, I get

java.lang.RuntimeException: No properties to serialize found on class com.google.firebase.firestore.FieldValue

if I change the field to

private long count;

I'm not getting as, how to update the counter. I have to set all fields too along with the counter. I tried using .update method too, but it gives an error when the document is not present and has to be created for the first time.

Hw can I properly manage the counters? I'm doing counter part from app-only instead of functions because I'm trying to get the app work in free firebase tier only.

Upvotes: 1

Views: 222

Answers (2)

Sandeep Choudhary
Sandeep Choudhary

Reputation: 260

Finally, as per suggestion from Alex, I use the map values, but at the same time, I moved my code to google. Please let me know if I have done the implementation wrong. It seems to work though

const functions = require('firebase-functions');

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();

// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });

const db = admin.firestore();
// [START_EXCLUDE]
const settings = { timestampsInSnapshots: true };
db.settings(settings);
// [END_EXCLUDE]

// [START aggregate_function]
exports.aggregateEnquiries = functions.firestore
    .document('enquiries/{id}')
    .onWrite(async (change, context) => {

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

            var dealerId = change.after.data().dealerId;
            var userId = change.after.data().assignedTo;
            var date = change.after.data().createdDt.toDate();
            var day = date.getDate();
            var month = date.getMonth() + 1;
            var year = date.getFullYear();
            var yearMonth = String(year) + (month < 10 ? "0" + month : month);
            var yearMonthDate = yearMonth + (day < 10 ? "0" + day : day);

            try {
                return await db.collection("dealers").doc(dealerId)
                    .get()
                    .then((doc) => {
                        if (doc !== null && doc.exists) {
                            const increment = admin.firestore.FieldValue.increment(1);

                            db.collection("enquiries_agg")
                                .doc("D_" + dealerId + "_" + year)
                                .set({ "count": increment }, { merge: true });

                            db.collection("enquiries_agg")
                                .doc("D_" + dealerId + "_" + monthYear)
                                .set({ "count": increment }, { merge: true });

                            db.collection("enquiries_agg")
                                .doc("U_" + userId + "_" + year)
                                .set({
                                    "count": increment,
                                    "dealerId": dealerId,
                                    "userId": userId,
                                    "reference": String(year)
                                }, { merge: true });

                            db.collection("enquiries_agg")
                                .doc("U_" + userId + "_" + yearMonth)
                                .set({
                                    "count": increment,
                                    "dealerId": dealerId,
                                    "userId": userId,
                                    "reference": String(yearMonth)
                                }, { merge: true });

                            db.collection("enquiries_agg")
                                .doc("U_" + userId + "_" + yearMonthDate)
                                .set({
                                    "count": increment,
                                    "dealerId": dealerId,
                                    "userId": userId,
                                    "reference": String(yearMonthDate)
                                }, { merge: true });
                        } else {
                            console.log("error in aggregare entries.");
                        }

                        return null;
                    });
            }
            catch (error) {
                console.log("Error getting documents: ", error);
                throw new Error("Profile doesn't exist : " + error);
            }
        } else if (change.before.exists && change.after.exists) {
            // Updating existing document : Do nothing
        } else if (!change.after.exists) {
            // Deleting document : subtract one from count
        }

        return null;
    });

Upvotes: 0

Alex Mamo
Alex Mamo

Reputation: 138814

The problem in your code is the following line of code:

counter.setCount(FieldValue.increment(1));

Your count property is defined in your Counter class to be of type long. When you are using the setCount() method to set its value, you should pass a long value as an argument but you actually don't. The following statement:

FieldValue.increment(1)

Return an object of type FieldValue and not a long, hence that error. To increment the value of your count property atomically by one, please use the following lines of code:

Map<String, Object> updateCount = new HashMap<>();
updateCount.put("count", FieldValue.increment(1));
yourDocRef.set(updateCount, SetOptions.merge());

Upvotes: 1

Related Questions