0

So I would like to load firestore documents using httpsCallable cloud functions, on my client application. Before I loaded my documents with a typical getDocuments function and it returned an array of simple JSON format snapshots, like so:

Firestore.firestore().collection("users").getDocuments { (snapshot, err) in
if let err = err{
                print("Failed to load data from firebase:", err)
                return
            }
snapshot?.documents.forEach({ (documentSnapshot) in
// documentSnapshot.data() is in JSON format
...
    }
}

And now I would like to load my documents with the return of a httpsCallable cloud function. So I have my cloud function working well and returning my documents' data. But the issue that I'm having is in the return format of the function. The data I receive from the cloud function is in the following format:

(
        {
        index = 34;
        field1 =         (
            "yes",
            "no",
            "maybe"
        );
        field2 = 26;
        brand = Puma;
        color = "marshmallow-natural";
        EcoFriendly = 1;
     },
      {
        index = 12;
        field1 =         (
            "oui",
            "non",
            "peut-être"
        );
        field2 = 21;
        brand = Nike;
        color = "red";
        EcoFriendly = 0;
     }
...
)

I observe this by calling the function as follows:

functions.httpsCallable("smartShoeFinder").call([
        "vector": fireV[0]]) { (result, error) in
          
            if let error = error as NSError? {
            // Handle error
          }
            print(result?.data)
            //The following is my attempt towards extracting the data and
            // converting it to an array of JSON:
            let requestResult = result!.data
            if let swiftArray  = result!.data as! NSArray as? [Any] {
                print(swiftArray[0])
            }
            
  }

I would like my data as an array of JSON as so:

[["index": 34, "field1": ["yes", "no", "maybe"], "field2": 26, "brand": "Puma", "color" = "marshmallow-natural", "shoeEcoFriendly": 1], 
["index": 12, "field1": ["oui", "non", "peut-être"], "field2": 21, "brand": "Nike", "color" = "red", "shoeEcoFriendly": 1]]

Before the format was an array of simple JSON dictionaries (as can be seen above) easily convertable to my different client objects. Now it is a __NSArrayM (which is apparently a NSMutableArray). So do you know if I can get the return of my httpsCallable firestore function in an array of JSON? Just like the following request:

Firestore.firestore().collection("users").getDocuments{ (snapshot, err) in
    snapshot?.documents.forEach({ (documentSnapshot) in
    documentSnapshot.data() // I'd like it in this format
    }
}

My firestore function returns an array of the following data (TypeScript):

const db = admin.firestore();
export const smartShoeFinder = functions.https.onCall(async (data, context) => {
let outputDocuments = new Array()
const collection = await db.collection('collection')
const notVisited = [1,32,13,44,15,26]
for (const index of notVisited){
    console.log(index)
    const snap = await collection.where("index", "==", index).get()
    if (snap.size == 0){
       continue
       }
    const onlyDoc = snap.docs[0].data
    outputDocuments.push(onlyDoc) // Populating the array with the wanted documents
  }
// Now returning promise to the client
.then(() => {
    return new Promise((resolve, reject) => {
        resolve(outputDocuments)
    })
})
.catch(err => console.log(err))
})

where outputDocuments is the array of document data.

Is there a better way to do this?

1 Answers1

0

I am a JavaScript developer and have limited knowledge in Swift but you can simplify the code like so:

const db = admin.firestore();

exports.smartShoeFinder = functions.https.onCall(async (data, context) => {
    // FROM CLIENT AS AN ARRAY OF INTEGERS
    const notVisited = data.notVisited;     

    function getDoc(index){
        // RETURNS THE QUERY WHICH IS ALREADY A PROMISE SO NO NEED TO WRAP IT
        return db.collection('collection').where("index", "==", index).get().then(colSS=>{
            if (r.empty){
                return {
                    isEmpty: true
                };
            }
            else{
                return {
                    isEmpty: false,
                    doc: r.docs[0].data()
                };
            }
        }).catch(e=>{
            console.log("QUERY ERROR: " + e.message);
        })
    }
    // STORES ALL QUERIES IN AN ARRAY OF PROMISES
    let processes = notVisited.map(index=>{
        return getDoc(index);
    })
    // USE Promise.all() TO RESOLVE ALL THE PROMISES IN PARALLEL
    return Promise.all(processes).then(result=>{
        // FILTER OUT THE NON EMPTY ONES AND EXTRACT THE DATA.
        return result.filter(query=>{
            return !query.isEmpty;
        }).map(query=>{
            return query.doc;
        })
    })
})

Alternatively, it can be further simplified ONLY IF your array of notVisited indices is within 10 elements long using the array-contains-any query and converting the "index" field to an array of single element (which you need to prepare beforehand for each document) like so:

const db = admin.firestore();

exports.smartShoeFinder = functions.https.onCall(async (req, context) => {
    return await db.collection("collection").where("index", "array-contains-any", data.notVisited).then(colSS=>{
        return colSS.docs.map(docSS=>{
            return docSS.data();
        })
    })
})
CRUD DS
  • 456
  • 2
  • 5