B4J Library [BANanoFireStoreDB] - A CRUD Cloud FireStoreDB Wrap for BANano

Ola

Download

Yesterday I started playing around Google FireStore. This is things I learned. So, you need to log to the console and do the initial setup.

There is a Realtime Database (FireBase) and there is a Cloud FireStore. This is about the latter.

Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform. Like Firebase Realtime Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity. Cloud Firestore also offers seamless integration with other Firebase and Google Cloud Platform products, including Cloud Functions.

Here is a very simplified method of setting up firebase storage

  • Go to firebase console
  • click add project
  • specify project name > Continue
  • enable / disable google analytics > Continue / create project
  • Your project Is ready > Continue
  • Side Menu > Develop > Authentication
  • Set up sign-in method
  • Sign in method > Email/Password (click edit icon) > Enable > Save

  • Side Menu > Develop > Cloud Firestore
  • Create database
  • Start in test mode > Next
  • Select location > enable
  • Side Menu > Project Overview > Project Settings
  • Your apps > web
  • Enter App NickName > Register App
  • Add Firebase SDK = Connection settings (copy)
  • Continue To console


Step 1.


Step 2.
Create your database. Ensure that you update your permissions as they are set to False (cannot read, cannot write). https://firebase.google.com/docs/firestore/security/get-started#auth-required

More details: https://firebase.google.com/docs/firestore/quickstart

Step 3
Get your connection configuration data. This you will use on the attached library / class

Step 4
Create a collection. I have created a collection called users for my tests. This will be used for CRUDing around.

What has been done

1. CRUD
1.1 Adding records to a collection.
1.2. Updating an existing record in a collection
1.3. Reading an existing record from a collection
1.4. Reading all records from an existing collection
1.5. Deleting a record from a collection.
1.6. Work Offline with enablePerist...
1.7. Real-time change detection (changes made by other users to the collection)

How was the Learning Curve?

Once you get around BANano.RunMethod, BANano.GetField and how these can be used with the JavaScript FireBase, all is well.

  • BANano.RunMethod is used to execute a function.
  • BANano.GetField is like map.get(?)

Thing is FireBase uses a lot of dots (.) so its challenging to determine what is a field / function at times. It has been a trial and error exercise. For example...

B4X:
'
'get/read a record
Sub CollectionGet(collection As String, colID As String) As BANanoPromise
    'get the collection to add to
    Dim promGet As BANanoPromise = firestore.RunMethod("collection", Array(collection)).RunMethod("doc", Array(colID)).RunMethod("get", Null)
    Return promGet
End Sub

#WatchThisSpace

PS: I have attached the first tests here, this is WIP (work in progress) and more is being done to wrap an easy db functionality for CRUD.
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
The C of CRUD - Creating Records i.e. this is like a SQL INSERT statement.

1. We have created a collection and gave it a named of "users", we set it to auto-increment ids.

Assumptions: You can updated your credentials and also called .connect

You can call this code on button click to save a "map" i.e record to a database.

B4X:
'add a new user (update permissions )
    Dim user As Map = CreateMap()
    user.put("name", "Anele")
    user.Put("email", "[email protected]")
    user.put("phone", "082")
    'add the record and get its id.
    Dim recID As String
    Dim Response As Map
    Dim Error As Map
    Dim adduser As BANanoPromise = fb.CollectionAdd("users", user)
    adduser.then(Response)
    recID = fb.GetID(Response)
    Log($"Record ID: ${recID}"$)
    Log(Response)
    adduser.Else(Error)
    Dim message As String = Error.Get("message")
    Log(message)
    adduser.End

The way this works is via promises. I have not tested the Wait functionality of the BANanoPromise as yet. Will cross that bridge when I get to it.

Ta!
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
The R of CRUD - Read Records i.e. this is like a SQL SELECT ... WHERE statement.

We will break this into parts.

1. Reading a single record

To read a single record, you need to have the id of the record. From the list of records and from the added record, you can get an id of each record. These ids are auto-incremented as we decided to use that approach in adding records to the users collection.

B4X:
recID = "ytQirWSQOt3exeDOMbMP"
    Dim gRes As Map
    Dim gError As Map
    Dim getUser As BANanoPromise = fb.CollectionGet("users", recID)
    getUser.then(gRes)
    Dim data As Map = fb.GetRecord(gRes)
    Log("Read User:")
    Log(data)
    getUser.Else(gError)
    Dim message As String = gError.Get("message")
    Log(message)
    adduser.End

Return values based on our example

B4X:
email: "[email protected]"
id: "ytQirWSQOt3exeDOMbMP"
name: "Anele"
phone: "082"

2. Reading all records i.e selecting all records

In the example below we select all records in the users collection and we sort them by name asc. To sort them by name desc, we can just use 'name desc'. Asc is the default sort order.

B4X:
'get all records, order by name asc for dec, use "name desc"
    Dim usrResponse As Map
    Dim usrError As Map
    Dim getUsers As BANanoPromise = fb.CollectionGetAll("users", "name")
    getUsers.Then(usrResponse)
    Dim docs As List = usrResponse.Get("docs")
    Dim recs As List
    recs.Initialize
    For Each userx As BANanoObject In docs
        Dim uid As String = userx.Getfield("id").Result
        Dim udata As Map = userx.RunMethod("data", Null).Result
        udata.Put("id", uid)
        recs.Add(udata)
    Next
    Log(recs)
    getUsers.Else(usrError)
    Dim message As String = usrError.Get("message")
    Log(message)
    getUsers.End

This results is an json array, for example

B4X:
0: {name: "Anele", email: "[email protected]", phone: "082", id: "303xx7kNICVxBpladDii"}
[LIST=1]
[*]1: {name: "Anele", phone: "082", email: "[email protected]", id: "53rADfZezyrolty8MVmG"}
[*]2: {phone: "082", email: "[email protected]", name: "Anele", id: "GcL3ZEENx3yhCnUX5a15"}
[*]3: {name: "Anele", email: "[email protected]", phone: "082", id: "JcmYjY22fEL1qGrdBww6"}
[*]4: {email: "[email protected]", name: "Anele", phone: "082", id: "hvSFXf68dZ7JFMKPQmDt"}
[*]5: {name: "Anele", email: "[email protected]", phone: "082", id: "l9INBPt4cYWiXP4ieIZe"}
[*]6: {phone: "082", name: "Anele", email: "[email protected]", id: "oxZlxFLqQjB41W8Tz7AO"}
[*]7: {phone: "0817366739", email: "[email protected]", name: "Anele", id: "rvI8ZAlu4zoULmBs2xxF"}
[*]8: {name: "Usi", phone: "081", email: "[email protected]", id: "8dHuKjiEm0PRETlxXiRF"}
[*]9: {email: "[email protected]", name: "Usi", phone: "081", id: "8mVByZBEIKitiOVR8ySM"}
[*]10: {phone: "081", email: "[email protected]", name: "Usi", id: "BlSOnYAoBawEH9kU3I97"}
[*]11: {name: "Usi", email: "[email protected]", phone: "081", id: "Cc6wBPJioJ2vZ2x5j4Zn"}[code]
[/LIST]
Remember, the user id is automatically generated by firebase.

Ta!
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
The U of CRUD - Update Records i.e. this is like a SQL UPDATE ... WHERE statement.

Here we are updating an existing record using its generated auto-increment field

B4X:
'update an existing record
    'create the fields you need to update
    Dim recu As Map = CreateMap()
    recu.Put("name", "Anele Mbanga (Mashy)")
    recu.Put("lastupdate", DateTime.Now)
    recu.Put("city", "East London, Eastern Cape Province, South Africa")
    '
    Dim uID As String = "ytQirWSQOt3exeDOMbMP"
    Dim uResponse As Map
    Dim uError As Map
    Dim upUser As BANanoPromise = fb.CollectionUpdate("users", uID, recu)
    upUser.then(uResponse)
    Log("User updated successfully!")
    upUser.Else(uError)
    Dim message As String = uError.Get("message")
    Log(message)
    upUser.End

The next time indeed one reads the record, it is updated.

Ta!
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
The D of CRUD - Delete Records i.e. this is like a SQL DELETE ... WHERE statement.

B4X:
'delete an existing record using the primary auto-incremented key
    Dim dID As String = "ytQirWSQOt3exeDOMbMP"
    Dim dResponse As Map
    Dim dError As Map
    Dim delUser As BANanoPromise = fb.CollectionDelete("users", dID)
    delUser.then(dResponse)
    Log("User deleted successfully!")
    Log(dResponse)
    delUser.Else(dError)
    Dim message As String = dError.Get("message")
    Log(message)
    delUser.End

Ta!
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
I have not tested the Wait functionality of the BANanoPromise as yet. Will cross that bridge when I get to it.
The bridge arrived. I have noted that the list of records display after adding the records to the collection. So I have just tried .ThenWait and .ElseWait. This works perfectly.

B4X:
'execute the promise to add the record to the collection
    Dim adduser As BANanoPromise = fb.CollectionAdd("users", user)
    adduser.ThenWait(Response)
    'get the id of the recently added record
    recID = fb.GetID(Response)
    Log($"Record ID: ${recID}"$)
    Log(Response)
    adduser.ElseWait(Error)
    'display the error
    Dim message As String = Error.Get("message")
    Log(message)
    adduser.End

**** I WOULD RECOMMEND WE FOLLOW THIS APPROACH.
 

Mashiane

Expert
Licensed User
Longtime User
To avoid ambiguity I have wrapped a small sub to convert the "docs" to list.

B4X:
'convert docs to list
Sub FromJSON(response As Map) As List
    'get the returned docs
    Dim docs As List = response.Get("docs")
    Dim recs As List
    recs.Initialize
    For Each userx As BANanoObject In docs
        Dim uid As String = userx.Getfield("id").Result
        Dim udata As Map = userx.RunMethod("data", Null).Result
        udata.Put("id", uid)
        recs.Add(udata)
    Next
    Return recs
End Sub

So the new get all records has now been updated to be:

B4X:
'get all records, order by name asc for dec, use "name desc"
    Dim usrResponse As Map
    Dim usrError As Map
    'get records order by name asc
    Dim getUsers As BANanoPromise = fb.CollectionGetAll("users", "name")
    getUsers.ThenWait(usrResponse)
    'convert these to a list
    Dim recs As List = fb.FromJSON(usrResponse)
    Log(recs)
    getUsers.ElseWait(usrError)
    Dim message As String = usrError.Get("message")
    Log(message)
    getUsers.End

Ta!
 

Mashiane

Expert
Licensed User
Longtime User
SELECT WHERE Clauses

NB: If you are using a compound queries, you need to create indexes in your database via the FireBase console.

1596127045688.png


Just wow. This is amazing...

I have created helper functions to do this.

B4X:
'[WHERE CLAUSE]
    Dim wResponse As Map
    Dim wError As Map
    Log("select * from users where name = 'Anele' and age > 0")
    fb.SelectFrom("users")
    fb.WhereCondition("name", fb.FB_EQ, "Anele")
    'age > 0
    fb.WhereCondition("age", fb.FB_GT, 0)
    Dim wUser As BANanoPromise = fb.Execute
    wUser.then(wResponse)
    Dim recs As List = fb.FromJSON(wResponse)
    Log(recs)
    wUser.Else(wError)
    Dim message As String = fb.GetMessage(wError)
    Log(message)
    wUser.End

This returns all records where the name is anele with age > 0

Cool stuff hey!

I had added the age attributes for my records later on, so the query will only process those it finds and anything without that attribute is left out.

B4X:
[INDENT][LIST=1]
[*]0: {name: "Anele", email: "[email protected]", phone: "082", age: 46, children: 5, …}
[*]1: {email: "[email protected]", children: 5, age: 46, phone: "082", name: "Anele", …}
[*]2: {name: "Anele", age: 46, children: 5, phone: "082", email: "[email protected]", …}
[*]3: {age: 46, name: "Anele", children: 5, email: "[email protected]", phone: "082", …}
[*]4: {phone: "082", email: "[email protected]", age: 46, name: "Anele", children: 5, …}
[*]5: {age: 46, children: 5, name: "Anele", phone: "082", email: "[email protected]", …}
[*]6: {children: 5, age: 46, phone: "082", email: "[email protected]", name: "Anele", …}
[*]7: {name: "Anele", phone: "082", age: 46, children: 5, email: "[email protected]", …}
[*]8: {name: "Anele", email: "[email protected]", children: 5, phone: "082", age: 46, …}
[*]9: {email: "[email protected]", age: 46, name: "Anele", phone: "082", children: 5, …}
[*]10: {children: 5, name: "Anele", email: "[email protected]", phone: "082", age: 46, …}
[*]11: {phone: "082", name: "Anele", age: 46, email: "[email protected]", children: 5, …}
[*]12: {phone: "082", email: "[email protected]", age: 46, children: 5, name: "Anele", …}
[*]13: {email: "[email protected]", children: 5, phone: "082", age: 46, name: "Anele", …}
[*]14: {name: "Anele", age: 46, email: "[email protected]", children: 5, phone: "082", …}
[*]15: {age: 46, phone: "082", name: "Anele", email: "[email protected]", children: 5, …}
[*]16: {phone: "082", email: "[email protected]", age: 46, children: 5, name: "Anele", …}
[*]length: 17
[*]__proto__: Array(0)
[/LIST][/INDENT]
 

Mashiane

Expert
Licensed User
Longtime User
Going Offline and Syncing Changes when Online

Apparently one can, but limited to these browsers:

NB: For the web, offline persistence is supported only by the Chrome, Safari, and Firefox web browsers.

B4X:
Log("Enable offline storage...")
    Dim ofResponse As Map
    Dim ofError As Map
    Dim offline As BANanoPromise = fb.enablePersistence
    offline.then(ofResponse)
    Log(ofResponse)
    offline.Else(ofError)
    Dim message As String = fb.GetMessage(ofError)
    Log(message)
    offline.End
 

Mashiane

Expert
Licensed User
Longtime User
Picking up changes made on the database in Realtime

When someone makes changes to the data, other users can be notified. This is done by detecting and onSnapshot Method.

To activate this, we need to set a callback to it. We want to know each time a change happens to the users collection, lets assign an onSnapShot event to it.

B4X:
'detect changes being made
    fb.onSnapshot("users", Me, "onSnapshot")

When this method fires, we get all documents in the collection.

B4X:
Sub onSnapshot(doc As Map)
    Log("onSnapshot firing...")
    'get all documents
    Dim recs As List = fb.FromJSON(doc)
    Log(recs)
End Sub

BANanoFireStoreDB.gif
 

Mashiane

Expert
Licensed User
Longtime User
Previously we spoke about snapShots.

These enable one to perform an action when ever a record is added, modified or deleted.

With this method on the library, we can detect snapshot changes anytime a record meets these change types. This will return the id of the record, its data and the change type.
If activated, a snapshot fires everytime anyone makes a change to the collection.

B4X:
'get the changes that have been made for added, modified, removed
Sub DocChanges(snapShot As Map) As List
    Dim xDocChanges As BANanoObject = snapShot
    Dim changes As List = xDocChanges.RunMethod("docChanges",Null).Result
    Dim recs As List
    recs.Initialize
    For Each recx As BANanoObject In changes
        Dim stype As String = recx.GetField("type").Result
        Dim doc As BANanoObject = recx.GetField("doc")
        Dim rdata As Map = doc.RunMethod("data", Null).Result
        Dim uid As String = doc.Getfield("id").Result
        rdata.Put("changetype", stype)
        rdata.Put("id", uid)
        recs.Add(rdata)
    Next
    Return recs
End Sub

Then our example source code will be like this.

B4X:
Sub onSnapshot(snapShot As Map)
    'get changes when they happen
    Dim changes As List = fb.DocChanges(snapShot)
    For Each item As Map In changes
        'what is the change type
        Dim changetype As String = item.Get("changetype")
        Select Case changetype
        Case fb.FB_ADDED
        Case fb.FB_REMOVED
        Case fb.FB_MODIFIED
        End Select
    Next
End Sub

We will see how we can use it to our advantage soon enough.
 

Mashiane

Expert
Licensed User
Longtime User
User Authentication: Allowing users to sign in with their Google Email & Password via Popup

Download

The code for this post is on Examples\23 Firebase Auth folder


NB: Ensure that google authentication is enabled in the Sign-In-Methods

1. When the webapp starts, it connects to firebase using your own specified details.

2. We have added an event trapper,

B4X:
'detect onAuthStateChanged
    fb.onAuthStateChanged(Me, "onAuthStateChanged")

So that each time a user logs in / logs off, this is detected and specific events are fired and the UX updated.

3. The above events receives the user profile. If the user is logged, the user profile will have content i.e. isLogged(Y) else it will be null/undefined i.e. isLogged(N)

4. If the user is logged, the photoURL from google for the user should be read and displayed, including the display name
5. On the profile for the user, the user should now be able to log off and also be able to see his/her profile. The Login button should be hidden and we also save the acess token.
6. If the user us not logged, we show the generic profile, hide, My Profile and Logoff and show Login.
7. When a user clicks Logoff, a google signout is fired and the authState changes.
8. If user clicks Login, a popup to enter the email and then password i.e. Googles own popup is shown. When done, it fires authStateChanged.

1597589047321.png
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
please kindly resolve this issue in order to understand the bananofirestoredb

Please note: This thread is meant to publish stuff related to this library. PLEASE DO NOT POST QUESTIONS ON THIS THREAD, start a new thread when you want to ask a question. PLEASE.

In the github there are 2 examples, you have not indicated which project you are working with.

1600623481998.png


I am not able to reproduce your error. The possibility is that you are using old versions of the demo files, kindly download the latest versions from here, https://github.com/Mashiane/BANanoFireStoreDB.

Open and run the BANanoFireStoreDB project from the Library folder so that it compiles the latest version of the project, then you can try and run the demo projects again.

Ta!
 

swamisantosh

Member
Licensed User
Longtime User
Please note: This thread is meant to publish stuff related to this library. PLEASE DO NOT POST QUESTIONS ON THIS THREAD, start a new thread when you want to ask a question. PLEASE.

In the github there are 2 examples, you have not indicated which project you are working with.

View attachment 100303

I am not able to reproduce your error. The possibility is that you are using old versions of the demo files, kindly download the latest versions from here, https://github.com/Mashiane/c.

Open and run the BANanoFireStoreDB project from the Library folder so that it compiles the latest version of the project, then you can try and run the demo projects again.

Ta!
BANanoFireStoreDB latest version 1.30, i'm using the same version..................error shows the library used.
 
Top