-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started with the NoSQL Database API
The goal of this guide is to get you familiarized with the Predix SDK For iOS's NoSQL Local Database API .
You must have:
- Completed all tasks from our Getting Started Guide
- Knowledge of Xcode and Swift programming concepts
- General understanding of NoSQL Databases
1. Open or access your database. You can add the following configuration containing the database name, local file system URL, and a list of indexes to create within the database.
Example:
let configuration = Database.OpenDatabaseConfiguration.default
do {
let database = try Database.open(with: configuration, create: true)
if let database = database {
// ... the database is now open, and ready for interactions
}
} catch let error {
// ... handle error...
}
Note - A database refers to a physical set of files on the device, multiple calls to open the same database result in returning the same Database object. It is advised to avoid retaining multiple references to the same database. If a database is already open, use the _openedWith_ function to retrieve a reference to it.
2. Close your database when the database operations are completed.
**** (How ?)****
Notes - After closing the database:
-
No database API methods should be called on the database object. It is advised to set the database object to nil or ensure it goes out of scope after calling close.
-
Care must be taken to ensure no calls are made to any other references to that database.
**** (How ?)****
Deleting a database first closes the database, then removes the physical database files from the device. Once deleted, the local database no longer exists and it cannot be recovered.
To learn more about Databases, see the No SQL Local Database Programming Guide
Documents are the central data structure of the PredixSDK database. The Document object is a type of dictionary where the dictionary keys must be strings. Accessing values from a dictionary is similar to accessing values from any other Dictionary class in Swift.
Example:
let myValue = myDocument[myKey]
You can perform the following with the Documents:
-
- Write/ Update/Save Documents
-
- Read/Fetch Documents
-
- Delete Documents
To read more about Documents and different components of the Document, see No SQL Local Database Programming Guide
2. Creating (Write/Update/Save) Documents
The _save_ method automatically determines if the document already exists. The _save_ method is especially useful in scenarios where a developer does not remember if a Document is created or updated. If a document exists, then the method updates it, otherwise add it.
However, in some cases this is not desired so the Database object also includes _add_ and _update_ methods that return errors if the operation is not appropriate for the provided Document object.
Examples: Save Method
let database = Database.openedWith(Database.Configuration())
let document1: Document = ["aString": "string data", "anInt": 123, "aDouble": 3.14]
database.save(document1) { result in
switch result {
case .success(let savedDocument):
print("Saved document with id: \(savedDocument.metadData.id)")
case .failed(let error)
print("Error saving document: \(error)")
}
}
Add/Update Method
3.Fetching (Read) Documents
The _fetchDocument_ method allows you to retrieve a document from the Database. This method returns an optional Document object.
Example:
let database = Database.openedWith(Database.Configuration())
let myDocumentId = "my_document"
database.fetchDocument(myDocumentId) { fetchedDocument in
if let document = fetcheDocument {
print("Fetched document with id: \(document.metadData.id)")
} else {
print("No document with id: \(myDocumentId) exists")
}
}
4. Fetching Multiple Documents You can fetch multiple documents in a single call using the fetchDocuments method. This method takes an array of document ids, and the completion handler is provided a [String: Document] dictionary where the dictionary key is a document id. If any strings in the input array are not associated with a Document, then those keys are excluded from the output dictionary.
Example:
let database = Database.openedWith(Database.Configuration())
// a document with the id of "my_document" exists, but not "not_a_document_id".
let ids = ["my_document_id", "not_a_document_id"]
database.fetchDocuments(ids) { fetchedDocuments in
for (_, document) in fetchedDocuments {
print("fetched document: \(document.metaData.id)")
}
}
// Will print:
// fetched document: my_document_id
You can call the _delete_ method to delete the documents form the database. The _delete_ method takes the ID of the Document that you want to delete. The completion handler for this method is passed an UpdateResult enumeration containing the ID of the deleted document.
Example:
let database = Database.openedWith(Database.Configuration())
database.delete("my_document_id") { result in
switch result {
case .success(let deletedDocumentId):
print("Deleted document with id: \(deletedDocumentId)")
case .failed(let error)
print("Error deleting document: \(error)")
}
}
Data replication with a PredixSync backend service is a powerful tool that you can use to access Predix data while offline and share this data with other system users. You can choose two key replication options:
Repeating Replication — automatically detects changes and replicates them as needed, until explicitly stopped or when the application shuts down. This type of replication is useful when you want to ensure all changes from one system are sent to another system as soon as possible.
Bidirectional Replication — refers to the changes being sent from the PredixSync server to the client and from the client back to the PredixSync server. This type of replication is useful for read-only systems or systems that want to receive changes immediately but delay sending changes to the server.
To learn more about Replication concepts, see the NoSQL Database Programming Guide
1. Supply the PredixSync service URL and choose two key options: repeating and bidirectional.
2. Set the ReplicationConfiguation object's repeating property to true to enable the repeating replication.
If the property is set to false, the replication is non-repeating.
In other words, a single exchange of data will occur and the replication is completed. Replication must start again for another exchange of data to take place.
3. Set the ReplicationConfiguation object's bidirectional property to true to enable the bidirectional replication. If the property is set to false, data replicates from the PredixSync service to the client. No client changes are sent to the server.
The following ReplicationConfiguration options are easy to manage because the ReplicationConfiguration object has static initializers to create common types of replication.
Example:
Create a repeating, bidirectional replication configuration:
let replicationConfig = ReplicationConfiguration.repeatingBidirectionalReplication(with: myPredixSyncURL)
Create a non-repeating, bidirectional replication:
let replicationConfig = ReplicationConfiguration.oneTimeBidirectionalReplication(with: myPredixSyncURL)
Create a non-repeating, non-bidirectional replication:
let replicationConfig = ReplicationConfiguration.oneTimeServerToClientReplication(with: myPredixSyncURL)
Prerequisites:
- Knowledge of Predix Sync Service
1. To start the replication exchange enter:
database.startReplication(with: replicationConfig)
2. To stop the replication exchange enter:
database.stopReplication()
Notes -
- Stopping a non-repeating replication is not necessary, the replication automatically stops when the data exchange is completed. However, you can use
_stopReplication()_on a non-repeating replication to cancel a long-running in-process replication. - All replication work happens in a background queue so there may be a slight delay between calling these methods and the data exchange starting or ending.
Replication uses a standard delegate pattern to provide information on the current replication status. The object associated with the replicationStatusDelegate property of the database is called for the following replication events:
- replicationDidComplete
- replicationIsSending
- replicationIsReceiving
- replicationFailed
Information in each of these events allow you to handle errors, update status UI, or know when a data exchange is completed.
Using Indexs/Queries is a fast and efficient way to interact with data from the database. An index consists of three components: a String name, a String version, and mapping closure. Queries are how Indexes are used. You cannot run a query without an index. In a query, you can identify the index keys that you are interested in finding, and then run the query returns results matching the index keys of your choice.
To read more about Indexes, Queries, and the additional Map/Reduce function, see the NoSQL Database Programming Guide
You can create an Index and then run a query against that Index to request a document.
- Knowledge or Map/Reduce
- General Understanding of Indexes and Queries
- Create an Index. You can create an Index---(How ??)--.
Example:
- Run a query. You can run a query by "key" or "range".
Example:
Query by Key — Provides a list of explicit keys. The system returns the index rows that match these keys. The structure QueryByKeyList is used to create these type of queries:
In the following example, let us assume that the database has an index called "ColorIndex" defined, where the key is the name of a color. This query will only return rows where the key is one of the four listed colors.
let query= QueryByKeyList()
query.keys = ["red", "green", "blue", "purple"]
database.runQuery(on: "ColorIndex", with: query) { queryEnumerator in
print("Returned \(queryEnumerator.count) rows")
}
Query by Range — Specifies a range with a starting key and an ending key. It returns all keys falling within this range, as sorted by the index. Sorting rules vary by the data type of the key, so strings are sorted alphabetically, numbers sorted numerically, etc. Additionally, leaving a start key nil indicates that the query should begin at the very first row of the index; a nil end key indicates the results should end at the last row of the index, thus providing a "less than" and "greater than" type query:
In the following example, let us assume that the database has an index called "InvoiceCostIndex" defined, where the key is a numerical value. This query will return all index rows where the value of the key is 1000 or greater.
let query= QueryByKeyRange()
query.startKey = 1000
database.runQuery(on: "InvoiceCostIndex", with: query) { queryEnumerator in
print("Returned \(queryEnumerator.count) invoices with a total cost greater than 1000")
}
The query completion handler provides a QueryResultEnumerator object, which enumerates over a collection of QueryResultRow objects. The QueryResultRow has properties for the index key, the index value, and the document id of the document that generated the key/value pair in the index.
Example: In the following example, the system prints an invoice number (from the value of the index) and the total cost (from the key of the index) for all documents that have a cost value of 1000 or greater.
let query= QueryByKeyRange()
query.startKey = 1000
database.runQuery(on: "InvoiceCostIndex", with: query) { queryEnumerator in
print("Returned \(queryEnumerator.count) invoices with a total cost greater than 1000")
while let queryRow = queryEnumerator.next() {
print(" Invoice: \(queryRow.value) - total cost: \(queryRow.key)")
}
}
- See NoSQL Database Programming Guide
- Check out the NoSQL Local Database API Reference documentation
- See the example project
Getting Started Guides
Features
How-To Guides:
- Using Authentication API to Authenticate a User
- Using Online API to make Network Requests
- Using Time Serires API to Fetch Time Series Data
- Disable Usage Analytic tracking
API Documentation: