Tutorial: Read and write data

Step by step Read and write data

Writing data

To write data into a [[service]] application database, Three methods are used.

Method Description
set() This method is used to (over)write data into an application database.
update() This method is used to update some data or child node in an application database without erasing all data.
push() This method is used to add a list of data into an application database. By this way, we can also add one or several child nodes into the database.

Writing data by using set()

The set() method is used to write new data into the application database. If it already contains data, all data at the specified location including any child nodes will be overwritten. So to understand the set() method we will take the following example: an address book.

Let's start by saving some data in the application. We'll store each contact by a unique userName, and we'll store also their firstName, lastName, phoneNumber, email and birthday.

First, we must create a [[service]] application to save our contacts's data. All data from our contacs will be stored at this location:

var adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook");
Webcom adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook");
var adbk : WCWebcom = WCWebcom(url:"[[baseUrl]]/base/webcom-addressbook")!

Then with set() method, we'll create an object "contact" to the database with the username, firstName, lastName, phoneNumber, email and birthday. With set() method, we can pass string, number, boolean, null, array or a JSON object. Passing null to set() will remove the data at the specified node. In our example we'll pass the object "contact" :

var contact = adbk.child("contacts");
contact.set({
  macca: {
    birthday: "June 18, 1942",
    firstName: "Paul",
    lastName: "McCartney",
    phoneNumber: "020 1234 6541",
    email: "paulo@apple.com"
  },
  john: {
    birthday: "October 9, 1940",
    firstName: "John",
    lastName: "Lennon",
    email: "johnandyoko@apple.com"
  }
});
Webcom contact = adbk.child("contacts");
contact.set(new HashMap(){
  {put("macca", new HashMap(){
    {put("birthday", "June 18, 1942");}
    {put("firstName", "Paul");}
    {put("lastName", "McCartney");}
    {put("phoneNumber", "020 1234 6541");}
    {put("email", "paulo@apple.com");}
  });}
  {put("john", new HashMap(){
    {put("birthday", "October 9, 1940");}
    {put("firstName", "John");}
    {put("lastName", "Lennon");}
    {put("email", "johnandyoko@apple.com");}
  });}
});
let contact : WCWebcom = adbk.child("contacts")!
        contact.set([  "macca" : [
                        "birthday" : "June 18, 1942",
                        "firstName" : "Paul",
                        "lastName" : "McCartney",
                        "phoneNumber" : "020 1234 6541",
                        "email" : "paulo@apple.com"
            ]  ,
                    "john" : [
                        "birthday" : "October 9, 1940",
                        "firstName" : "John",
                        "lastName" : "Lennon",
                        "email" : "johnandyoko@apple.com"
            ] ] as NSObject)

You could also save contact's data directly in a child location, like this:

var contact = adbk.child("contacts");
contact.child("macca").set({
  birthday: "June 18, 1942",
  firstName: "Paul",
  lastName: "McCartney",
  phoneNumber: "020 1234 6541",
  email: "paulo@apple.com"
});
contact.child("lennon").set({
  birthday: "October 9, 1940",
  firstName: "John",
  lastName: "Lennon",
  email: "johnandyoko@apple.com"
});
Webcom contact = adbk.child("contacts");
contact.child("macca").set(new HashMap(){
  {put("birthday", "June 18, 1942");}
  {put("firstName", "Paul");}
  {put("lastName", "McCartney");}
  {put("phoneNumber", "020 1234 6541");}
  {put("email", "paulo@apple.com");}
});
adbk.child("contacts/lennon").set(new HashMap(){
  {put("birthday", "October 9, 1940");}
  {put("firstName", "John");}
  {put("lastName", "Lennon");}
  {put("email", "johnandyoko@apple.com");}
});
let contact : WCWebcom = adbk.child("contacts")!
contact.child("macca")?.set([
            "birthday" : "June 18, 1942",
            "firstName" : "Paul",
            "lastName" : "McCartney",
            "phoneNumber" : "020 1234 6541",
            "email" : "paulo@apple.com"
            ] as NSObject)
contact.child("lennon")?.set([
            "birthday" : "October 9, 1940",
            "firstName" : "John",
            "lastName" : "Lennon",
            "email" : "johnandyoko@apple.com"
            ] as NSObject)

For this two examples, the result is the same, data will be saved in your database, like this:

{
  "contacts": {
    "macca": {
      "birthday": "June 18, 1942",
      "firstName": "Paul",
      "lastName": "McCartney",
      "phoneNumber": "020 1234 6541",
      "email": "paulo@apple.com"
     },
    "lennon": {
      "birthday": "October 9, 1940",
      "firstName": "John",
      "lastName": "Lennon",
      "email": "johnandyoko@apple.com"
     }
  }
}

If data already existed in the "contact" node,

  • with the first method, the data will be overwritten (including all child nodes).
  • with the second method, only the value of each separate child node will be overwritten, the other children of the "contact" node will be unchanged.

Writing data by using update()

The update() method allow to write to multiple children of a database at the same time without overwriting other child nodes.

var mccContact = contact.child("macca");
mccContact.update({
  "address": "London"
});
Webcom mccContact = contact.child("macca");
mccContact.update(new HashMap(){
  {put("address", "London");}
});
let mccContact : WCWebcom = contact.child("macca")!
mccContact.update(
        [
          "address":"London"
        ] as NSObject)

This example will update macca's data to include his address. If we had used set() instead of update(), this would have deleted all data (birthday, firstName, lastname, phoneNumber and email) from the node "macca".

Adding a completion callback with set() or update() methods

The methods set() and update() offer an optional completion callback that is called when the write has been committed on the [[service]] servers. For example in the previous case, if you would like know when your contact's data has been committed, you can add a completion callback. If the call was unsuccessful for some reason, the callback will be passed an error object indicating why the failure occurred.

contact.set("writing contact", function(error) {
  if (error) {
    alert("Contact's data could not be saved." + error);
  } else {
    alert("Contact's data saved successfully.");
  }
});
contact.set("writing contact", new OnComplete(){
  @Override
  public void onComplete() {
    // Contact's data saved successfully.
  }
  @Override
  public void onError(WebcomError error) {
    // Contact's data could not be saved.
  }
});
contact.set("Writing contact" as NSObject) { (error) in
            if ((error) != nil){
                print("Contact's data could not be saved. \(error.debugDescription)")
            } else {
                print("Contact's data saved successfully")
            }
        }

Saving lists of Data with push() method

In our example of address book, we could imagine that the address book is shared between several users. If a user adds a new contact it will be stored in the database. But in a shared address book many users may add contacts at the same time. If two users write simultaneously contacts in the "contacts" node, then one of the contact would be deleted by the other.

To solve this problem, Webcom use the method push(). This method generates an unique ID every time that a new child is added in the database. The ID generated is based on a timestamp, so the list of datas will automatically be saved and ordered chronologically. By using this method, several users can add children to the same node of a database at the same time without write conflicts.

In the following example we can add contacts to our shared address book with the push() method :

var contact2 = adbk.child("other_contacts");
contact2.push({
  birthday: "February 25, 1943",
  firstName: "George",
  lastName: "Harrisson",
  phoneNumber: "020 1234 8879"
});
contact2.push({
  birthday: "July 7, 1940",
  firstName: "Ringo",
  lastName: "Starr",
  phoneNumber: "020 1234 4561"
});
var contact2 = adbk.child("other_contacts");
contact2.push(new HashMap(){
  {put("birthday", "February 25, 1943");}
  {put("firstName", "George");}
  {put("lastName", "Harrisson");}
  {put("phoneNumber", "020 1234 8879");}
});
contact2.push(new HashMap(){
  {put("birthday", "July 7, 1940");}
  {put("firstName", "Ringo");}
  {put("lastName", "Starr");}
  {put("phoneNumber", "020 1234 4561");}
});
let contact2 : WCWebcom = adbk.child("other_contacts")!
contact2.push( [ "birthday" : "February 25, 1943" ,
                         "firstName": "George",
                         "lastName" : "Harrisson",
                         "phoneNumber" : "020 1234 8879"]  as NSObject)
contact2.push( [ "birthday" : "July 7, 1940" ,
                         "firstName": "Ringo",
                         "lastName" : "Starr",
                         "phoneNumber" : "020 1234 4561"]  as NSObject)

With push() method, for each contact saved an unique Id was generated so in our example data now looks like this:

{
  "other_contacts": {
    "-JtJIbH-AMSjUj-e-QAR": {
      "birthday": "February 25, 1943",
      "firstName": "George",
      "lastName": "Harrisson",
      "phoneNumber": "020 1234 8879"
    },
    "-JtJIbHINoEONq8fxNds": {
      "birthday": "July 7, 1940",
      "firstName": "Ringo",
      "lastName": "Starr",
      "phoneNumber": "020 1234 4561"
    }
  }
}

For getting this Unique ID , we could use the name() method like this example :

// Generate a new contact
var contact2 = adbk.child("other_contacts");
var newContact = contact2.push({
    firstName: "Yoko",
    lastName: "Ono",
    phoneNumber: "020 1234 0999"
});

// Get the unique ID generated by push()
var contactID = newContact.name();
// Generate a new contact
Webcom contact2 = adbk.child("other_contacts");
Webcom newContact = contact2.push(new HashMap(){
  {put("firstName", "Yoko");}
  {put("lastName", "Ono");}
  {put("phoneNumber", "020 1234 0999");}
});

// Get the unique ID generated by push()
String contactID = newContact.name();
// Generate a new contact
let contact2 = adbk.child("other_contacts")
let newContact = contact2?.push([
                                 "firstName": "Yoko",
                                 "lastName" : "Ono",
                                 "phoneNumber" : "020 1234 0999"]  as NSObject)

// Get the unique ID generated by push()
let contactID = newContact!.name

Reading data

To read data from a [[service]] application we can use on() method with four following event types :

Method Description
value This method is used to read the entire content of an application database.
child_added This method is used to read only the new data which was added to an application database.
child_changed This method is used to read only data which was updated within an application database.
child_removed This method is used to read only data which was deleted or removed from an application database.

These four methods make it possible to add an asynchronous callback function to read data from the application database. The callback function receives a snapshot of the application's data. A snapshot is a picture of the data at a given time. When we call the method val() on a snapshot returns the JavaScript object representation of the data. If the application database is empty (no data), the snapshot will be null.

These four methods make it also possible to detach the callback function. Callbacks could be removed by specifying the event type and the callback function to be removed, like the following examples:

// To remove all value callbacks
adbk.off("value");

// To remove all child_added callbacks
adbk.off("child_added");

// To remove all callbacks of all type (value, child_added, child_changed, child_removed)
adbk.off();
// To remove all value callbacks
adbk.off(Query.Event.VALUE);

// To remove all child_added callbacks
adbk.off(Query.Event.CHILD_ADDED);

// To remove all callbacks of all type (value, child_added, child_changed, child_removed)
adbk.off();
// To remove all value callbacks
adbk.offEventType(.value)

// To remove all child_added callbacks
adbk.offEventType(.childAdded)

// To remove all callbacks of all type (value, child_added, child_changed, child_removed)
adbk.goOffline()

Reading data by using « value »

In our previous example, we could read our contacts's data by using value :

var adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// Attach an asynchronous callback to read contacts in our address book
adbk.on("value", function(snapshot) {
  console.log(snapshot.val());
});
Webcom adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// Attach an asynchronous callback to read contacts in our address book
adbk.on(Query.Event.VALUE, new OnQuery(){
  @Override
  public void onComplete(DataSnapshot snapData, @Nullable String prevName) {
    // code to handle current data
    Object value = snapData.value();
  }
  @Override
  public void onCancel(WebcomError error) {
    // event subscription was canceled because of permission reason
  }
  @Override
  public void onError(WebcomError error) {
    // an error occured
  }
});
let adbk : WCWebcom = WCWebcom(url : "[[baseUrl]]/base/webcom-addressbook/contacts")
adbk.onEventType(.value, withCallback: { (snapData, prevName) in
            if (snapData != nil){
                // code to handle current data
                let value = snapData?.exportVal()
            } else {
                // an error occured
            }
        }) { (error) in
            if (error != nil){
                // event subscription was canceled because of permission reason
            }
        }

In this example, value is used to make a snapshot of the application database contents. The snapshot is launched the first time with all data including child data and again every time data will be changed. This function will be called on each time new data is added to our database. In our example, value will be returned all contacts in our address book, and everytime a new contact is added.

Reading data by using « child_added »

This function is used to retrieve a list of data from the application database. The « child_added » is launched once for each existing child and then again every time a new child is added to the database. In our example, « child_added » will be returned all contacts in our address book, and everytime a new contact is added.

If we wanted to retrieve only the data on each new contact added to our address book, we can use « child_added » like the following example:

//Reference to our address book
var adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

//add contact
adbk.set({
  jimix: {
    birthday: "November 27, 1942",
    firstName: "Jimi",
    lastName: "Hendrix",
    phoneNumber: "045 1234 6541",
    email: "jimix@spaceland.com"
  }
});

//launch snapshot
adbk.on("child_added", function(snapshot, prevChildKey) {
  var newContact = snapshot.val();
  console.log("First name: " + newContact.firstName);
  console.log("Last name: " + newContact.lastName);
  console.log("Previous contact: " + prevChildKey);
});
//Reference to our address book
Webcom adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

//add contact
adbk.set(new HashMap(){
  {put("jimix", new HashMap(){
    {put("birthday", "November 27, 1942");}
    {put("firstName", "Jimi");}
    {put("lastName", "Hendrix");}
    {put("phoneNumber", "045 1234 6541");}
    {put("email", "jimix@spaceland.com");}
  });}
});

//launch snapshot
adbk.on(Query.Event.CHILD_ADDED, new OnQuery(){
  @Override
  public void onComplete(DataSnapshot snapData, @Nullable String prevName) {
    // code to handle current data
    Map<String, Object> newContact = (Map) snapData.value();
    Log.i("CHILD_ADDED", "First name: " + newContact.get("firstName"));
    Log.i("CHILD_ADDED", "Last name: " + newContact.get("lastName"));
    Log.i("CHILD_ADDED", "Previous contact: " + prevName);
  }
  @Override
  public void onCancel(WebcomError error) {
    // event subscription was canceled because of permission reason
  }
  @Override
  public void onError(WebcomError error) {
    // an error occured
  }
});
//Reference to our address book

let adbk : WCWebcom = WCWebcom(url : "[[baseUrl]]/base/webcom-addressbook/contacts")
//launch snapshot
adbk.onEventType(.childAdded, withCallback: { (snapData, prevName) in
  if (snapData != nil){
        // code to handle current data
        let value = snapData?.exportVal() as! [String : Any]
        print(value["lastName"] as! String)
        print(value["email"] as! String)
  } else {
                // an error occured
  }
}) { (error) in
     if (error != nil){
                // event subscription was canceled because of permission reason
          }
      }
//add contact
adbk.set([ "jimix" : [
  "birthday" : "November 27, 1942",
  "firstName" : "Jimi",
  "lastName" : "Hendrix",
  "phoneNumber" : "045 1234 6541",
  "email" : "jimix@spaceland.com"
  ] 
   as NSObject)

In our example the snapshot contain a contact object. We can access to all properties of this object by calling them directly. We could also access to the previous contact with the argument prevChildKey.

Reading data by using « child_changed »

This function is used to retrieve changes on a list of data from the application database. The « child_changed » event is launched when a child node is modified (including all children subnodes). For example, in our address book we could use this method to read updated contacts like this:

// Reference of our address book
var contacts = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// Update Contact's data
var mccContact = contacts.child("macca");
mccContact.update({
    "address": "London 2358644689"
});

// Launch snapshot
contacts.on("child_changed", function(snapshot) {
  var changedContact = snapshot.val();
  console.log("The contact '" changedContact.firstName + changedContact.lastName + "'was updated");
  console.log("Address: " + changedContact.address);
});
// Reference to our address book
Webcom adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// Update Contact's data
adbk.child("macca").update(new HashMap(){
  {put("address", "London 2358644689");}
});

// launch snapshot
adbk.on(Query.Event.CHILD_CHANGED, new OnQuery(){
  @Override
  public void onComplete(DataSnapshot snapData, @Nullable String prevName) {
    // code to handle current data
    Map<String, Object> changedContact = (Map) snapData.value();
    Log.i("CHILD_CHANGED", String.format("The contact '%s %s' was updated.", changedContact.get("firstName"), changedContact.get("lastName")));
    Log.i("CHILD_ADDED", "Address: " + changedContact.get("address");
  }
  @Override
  public void onCancel(WebcomError error) {
    // event subscription was canceled because of permission reason
  }
  @Override
  public void onError(WebcomError error) {
    // an error occured
  }
});
//Reference to our address book
let adbk : WCWebcom = WCWebcom(url : "[[baseUrl]]/base/webcom-addressbook/contacts")
// Launch snapshot
contacts.onEventType(.childChanged, withCallback: { (snapshot, str) in

    if (snapshot != nil){
       var changedContact = snapshot?.exportVal() as! [String : Any]
       print("The contact : \(changedContact["firstName"] as! String) \(changedContact["lastName"] as! String)  was updated")
       print("Adress : \(changedContact["address"] as! String)")         
     }

})
// Update Contact's data
var mccContact = contacts.child("macca")
mccContact?.update(["address" : "London 2358644689"] as NSObject)

Reading data by using « child_removed »

This function is used to retrieve deleted or removed data from the application database. The « child_removed » event is launched immediately when a child is removed (including all children subnodes). For example, in our address book we could use this method to read deleted contacts like this:

//Reference of our address book
var adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// delete Contact's data
var mccContact = contacts.child("macca");
mccContact.remove();

//Launch snapshot
adbk.on("child_removed", function(snapshot) {
  var deletedContact = snapshot.val();
  console.log("The following contact '" + deletedContact.lastName + "' has been deleted");
});
// Reference to our address book
Webcom adbk = new Webcom("[[baseUrl]]/base/webcom-addressbook/contacts");

// Update Contact's data
adbk.child("macca").remove();

// launch snapshot
adbk.on(Query.Event.CHILD_REMOVED, new OnQuery(){
  @Override
  public void onComplete(DataSnapshot snapData, @Nullable String prevName) {
    // code to handle current data
    Map<String, Object> deletedContact = (Map) snapData.value();
    Log.i("CHILD_CHANGED", String.format("The following contact '%s %s' has been deleted.", deletedContact.get("firstName"), deletedContact.get("lastName")));
  }
  @Override
  public void onCancel(WebcomError error) {
    // event subscription was canceled because of permission reason
  }
  @Override
  public void onError(WebcomError error) {
    // an error occured
  }
});
//Reference to our address book
let adbk : WCWebcom = WCWebcom(url : "[[baseUrl]]/base/webcom-addressbook/contacts")
// launch snapshot
adbk.onEventType(.childRemoved,  withCallback: { (snapData, prevName) in
            if (snapData != nil){
                // code to handle current data
                let deletedContact = snapData?.exportVal() as! [String:Any]
                print(" The following contact \(deletedContact["firstName"] as! String) has been deleted ")
            } else {
                // an error occured
            }
        }) { (error) in
            if (error != nil){
                // event subscription was canceled because of permission reason
            }
        }
// Update Contact's data
adbk.child("macca")?.remove()

Reading the last or first n elements of a list

It is possible to add filter when you subscribe to a node and only keep a view of last (or first) n elements for example. Key order definition

//Reference of a list of messages
var ref = new Webcom("[[baseUrl]]/base/webcom-chat/messagelist");

//subscribe to last 5 elements of the list
ref.limit(5).on("value", function(snapshot) {
  var last5Messages = snapshot.val();
  console.log("The last five messages are" , last5Messages);
});

// or :

//subscribe to first 5 elements of the list
ref.startAt().limit(5).on("value", function(snapshot) {
  var first5Messages = snapshot.val();
  console.log("The first five messages are" , first5Messages);
});
// Available soon
// Available soon

Reading a range of keys

endAt and startAt can be used to query a range of keys. Key order definition

var ref = new Webcom("[[baseUrl]]/base/directory/names");

ref.set({"a":"va","b":"vb","c":"vc","d":"vd"})

//subscribe to names between keys "b" and "c"
ref.startAt("b").endAt("c").once("value", function(snapshot) {
  console.log("entries from b to c are" , snapshot.val());
  // =>  {"b":"vb","c":"vc"}
});
// Available soon
// Available soon

Reading from a key name with a limit

It is for example possible to query the 5 first names in a directory starting at a value. Key order definition

var ref = new Webcom("[[baseUrl]]/base/directory/names");

ref.set({"a":"va","b":"vb","c":"vc","d":"vd"})

//subscribe to 2 first names starting at keys "b"
ref.startAt("b").limit(2).once("value", function(snapshot) {
  console.log("entries are" , snapshot.val());
  // =>  {"b":"vb","c":"vc"}
});
// Available soon
// Available soon