There are currently 3 ways of reading data from a Webcom application database. All of them are asynchronous:
- Getting the data of a node one shot,
- Subscribing to the data at a node in order to be notified of data changes as long as the subscription holds,
- Refining a subscription using a “query” to be notified of changes of a subset of the data at a node.
Note that realtime subscriptions and queries are not available in the lite Serverless Database service and the REST interface.
However, they provide additional features for getting the data of nodes (see details in the dedicated section).
Getting data one shot
Full Serverless Database service
The simplest way to read some data from a Webcom application database is to get the data at a given node. This operation is one shot, ie you get the data once at the time you request them, and asynchronous, ie it is a given callback or promise that will actually receive the read data.
Under the hood:
- either the network connectivity is up and a request is sent the Webcom back end, and the callback/promise will be asynchronously called/resolved with the response content,
- or the network connectivity is down and the callback/promise is called/resolved immediately (yet asynchronously from a technical point of view) with the data present locally within the client cache (see below) if any or a null value if the client cache is empty.
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const database = app.serverlessDb;
let node = database.rootNode.relativeNode("contacts/john");
node.get()
.then(datasnapshot => {
console.log("Data at node", node.path, "is", JSON.stringify(datasnapshot.val()));
console.log("This data has", datasnapshot.acknowledged() ? "been" : "not been", "acknowledged by the back end");
})
.catch(() => console.log("Data at node", node.path, "cannot be read"));
val app = WebcomApplication.default
val manager = app.datasyncService.createManager()
val node = manager / "contacts" / "john"
node.get { // it: WebcomResult<DatasyncValue>
when (it) {
is WebcomResult.Success -> println("Data at node ${node.path} is ${it.result.asNative} and has ${if (it.result.
acknowledged) "been" else "not been"} acknowledged")
is WebcomResult.Failure -> println("Data at node ${node.path} cannot be read: ${it.error.message}")
}
}
let app = Webcom.defaultApplication
let manager = app.datasyncService.createManager()
guard let node = manager / "contacts" / "john" else {
return
}
node.value { result in
switch result {
case let .success(value):
print("Data at node \(node.path) is \(value.serialized)")
print("This data has \(value.isAcknowledged ? "been" : "not been") acknowledged by the back end")
case .failure:
print("Data at node \(node.path) cannot be read")
}
}
If the requested data cannot be read because of some blocking security rules, then the get operation fails.
Each retrieved piece of data is tagged with its source status: either acknowledged by the Webcom back end or not yet acknowledged (in this case, the value comes from the last write operation registered by the client in its local data cache).
When some data are not yet acknowledged, it is possible to create a subscription to be notified of their forthcoming acknowledgement.
Lite Serverless Database service and REST interface
Neither the lite version of the Serverless Database service nor the REST interface handle a local data cache on the client. As a consequence, the status of data (acknowledged or not by the Webcom back end) is neither available.
Getting the value of a node
Use one of the get()
or
getRawValues()
methods.
The Webcom database stores arrays as JSON objects with numerical keys. The get()
method converts such JSON
objects back to JSON arrays, while the getRawValues()
method returns data as they are really stored.
Let's consider that the contacts/John
node is set to:
{
"firstName": "Jean",
"lastName": "Bonneau",
"Hobbies": ["traveling", "Jazz", "sailing"]
}
Then:
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const database = app.serverlessDbLite;
let node = database.rootNode.relativeNode("contacts/john");
node.get()
.then(value => {
console.log("Data at node", node.path, "is", JSON.stringify(value));
// should display:
// {
// "firstName": "Jean",
// "lastName": "Bonneau",
// "Hobbies": ["traveling", "Jazz", "sailing"]
// }
})
.catch(() => console.log("Data at node", node.path, "cannot be read"));
node.getRawValue()
.then(value => {
console.log("Data at node", node.path, "is", JSON.stringify(value));
// should display:
// {
// "firstName": "Jean",
// "lastName": "Bonneau",
// "Hobbies": {"0": "traveling", "1": "Jazz", "2": "sailing"}
// }
});
In the REST API, getting data at a node is implemented by the following HTTP GET
method:
curl -X GET https://io.datasync.orange.com/datasync/v2/<your-app>/data/contacts/john
Getting truncated children of a node
The lite Serverless Database service and the REST interface provide some additional functions to help looking into potentially large data nodes (with many children).
These functions work well on fragmented nodes, which cannot be handled with a standard read function.
Let's consider from now on the /contacts/Jean
node is:
{
"firstName": "Jean",
"lastName": "Bonneau",
"birthday": "June 18, 1942",
"address": {
"city": "Paris",
"zipCode": "75000"
},
"contacts": {
"email": "jean.bonneau@orange.com",
"phone": "+33(0)6 012 345 678"
},
"Hobbies": ["traveling", "Jazz", "sailing"]
}
The first function retrieves the truncated value of a given node. If this node is a JSON object then the returned value is an object including all children with either:
- their value in case of a literal value (number, boolean or string),
- or
{}
if this child is itself a JSON object (or{".exist":true}
for the REST interface)
Use the getChildren()
method.
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
let node = database.rootNode.relativeNode("contacts/Jean");
node.getChildren()
.then(value => {
console.log("Data at node", node.path, "is", JSON.stringify(value));
// should display:
// {
// "firstName": "Jean",
// "lastName": "Bonneau",
// "birthday": "June 18, 1942",
// "address": {},
// "contacts": {},
// "Hobbies": {},
// }
})
.catch(() => console.log("Data at node", node.path, "cannot be read"));
In the REST API, getting truncated data at a node is implemented by the HTTP GET
method, passing dotExist
to the
shallowMode
query parameter:
curl -X GET https://io.datasync.orange.com/datasync/v2/<your-app>/data/contacts/Jean?shallowMode=dotExist
# returned response body should contain:
# {
# "firstName": "Jean",
# "lastName": "Bonneau",
# "birthday": "June 18, 1942",
# "address": {.exist:true},
# "contacts": {.exist:true},
# "Hobbies": {.exist:true}
# }
Getting child keys of a node
This function returns the keys of the children of a given node as an array of string (keys being in any order).
Use the getKeys()
method.
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
let node = database.rootNode.relativeNode("contacts/Jean");
node.getKeys()
.then(value => {
console.log("Data at node", node.path, "is", JSON.stringify(value));
// should display:
// ["firstName", "lastName", "birthday", "address", "contacts", "Hobbies"]
})
.catch(() => console.log("Data at node", node.path, "cannot be read"));
In the REST API, getting child keys of a node is implemented by the HTTP GET
method, passing keysArray
to the
shallowMode
query parameter.
curl -X GET https://io.datasync.orange.com/datasync/v2/<your-app>/data/contacts/Jean?shallowMode=keysArray
# returned response body should contain:
# ["firstName", "lastName", "birthday", "address", "contacts", "Hobbies"]
Getting the child count of a node
This function returns the number of children of a given node (or -1
if data associated to the current node is
not an object).
Use the getChildCount()
method.
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
let node = database.rootNode.relativeNode("contacts/Jean");
node.getChildCount()
.then(value => {
console.log("Node", node.path, "has", JSON.stringify(value), "children");
// should display: 6
})
.catch(() => console.log("Data at node", node.path, "cannot be read"));
This function is not directly provided by the REST interface.
Instead, you can get the keys of all children (using the previous request), and then count the items of the returned array.
Client local cache
Note: this section is not applicable to lite Serverless Database service or the REST interface.
With the full Serverless Database service, all read operations work with a data cache hosted on the client device to manage access to data even when being offline.
Reading local cache
It is possible to read the data stored at a node directly from the local cache, without requesting the Webcom back end. The dedicated API function works the same as the previous one used for getting the data one shot:
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const database = app.serverlessDb;
let node = database.rootNode.relativeNode("contacts/john");
const datasnapshot = node.getCache();
console.log("Data at node", node.path, "is", JSON.stringify(datasnapshot.val()));
console.log("This data has", datasnapshot.acknowledged() ? "been" : "not been", "acknowledged by the back end");
val app = WebcomApplication.default
val manager = app.datasyncService.createManager()
val node = manager / "contacts" / "john"
val value = node.getCache()
println("Data at node ${node.path} is ${value.asNative} and has ${if (value.acknowledged)
"been" else "not been"} been acknowledged")
let app = Webcom.defaultApplication
let manager = app.datasyncService.createManager()
guard let node = manager / "contacts" / "john" else {
return
}
let value = node.cacheValue
print("Data at node \(node.path) is \(value.serialized) and has \(value.isAcknowledged ? "been" : "not been") acknowledged.")
In this case, you will get either a null value if the data node has never been read from the back end (so that the local cache has not been fed yet) or the last value read from the back end (which may be consequently out of date).
Also, be aware that if you perform a one shot read operation while offline, then it actually results in performing a read operation from the local cache.
Saving and restoring local cache
If you want to deal with some kind of persistent application, this data cache may be retrieved and saved at any time and may be restored at the application startup.
Retrieving the current cache content is done this way (replace “<your-app>” with your actual application identifier):
// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const database = app.serverlessDb;
let cache = database.offlineData;
val myApp = WebcomApplication.default // the app defined by the webcom.properties file
val cacheData = myApp.datasyncService.offlineCache()
let configuration = WebcomConfiguration(identifier: "<my-app>", cacheMode: .manualSaving)
let app = WebcomApplication(configuration: configuration)
let cacheData = app?.datasyncService.cache
Setting the cache content at startup is done this way (replace “<your-app>” with your actual application identifier):
const cache = myMethodToRetrievePreviouslySavedCache();
const app = Webcom.App("<your-app>", {ServerlessDb:{offlineData: cache}});
const database = app.serverlessDb;
val cacheData = myMethodToRetrievePreviouslySavedCache()
val configuration = WebcomConfiguration.fromAsset() // the configuration defined by the webcom.properties file
.withOfflineCache(cacheData)
val myApp = WebcomApplication.fromConfiguration(configuration)
let cacheData = myMethodToRetrievePreviouslySavedCache()
let configuration = WebcomConfiguration(identifier: "<my-app>", cacheMode: .initialized(data: cacheData))
let app = WebcomApplication(configuration: configuration)