Skip to content

NoSQL Database Advance Topics

Arpillai edited this page Dec 15, 2017 · 6 revisions

Advanced Topics

Triggers

DatabaseChangeDelegate

A developer can establish a trigger to be informed of any database changes, by associating a databaseChangeDelegate with the database. This delegate is called for all database changes. The delegate will receive an array of DocumentChangedDetails, which includes the document id of the changed document, if the source of the change was replication or not, and if the document change was a deletion or not.

Documents

Id factory

Documents created without an id, are automatically given an id. This id is generated in a static closure on the Document class: idFactory. By default a document id will be generated from a UUID, however by replacing this static closure, a developer can use a custom-scheme to generate document ids.

date formatter

While the JSON format does not recognize a Data type, the Document object will recogize data that is a date, and automatically convert it to a Date type. By default the Document class will use the ISO8601 date format standard, however a custom DataFormatter can be replace this by assigning an object to the static dateFormatter property.

Database Configuration

Default

The default database configuration, returned by OpenDatabaseConfiguration.default uses "pm" as the default name, and a subdirectory under the Application Support directory for it's path. These defaults can be changed by using a subclass of OpenDatabaseConfiguration and overriding the defaultDatabaseName() and defaultLocation() methods. In this way, a developer could create several subclasses of OpenDatabaseConfiguration to support several defaults easily.

CompletionQueue

By default, all completion handlers for the asynrouc database methods will be called back on the main queue. If another queue is desired, a the OpenDatabaseConfiguration initializer includes a completionQueue parameter that can be used to provide a custom completion handler queue for the database.

Equality

Two Database.Configuration objects are equal if their database name, and file location are equal. All databases opened with equal configurations return the same database object.

Indexes and Queries

Map/Reduce

An optional capability to Indexes is provideing a Reduce function. This function defined as:

typealias Reduce = (_ keys: [Any], _ values: [Any], _ rereduce: Bool) -> (Any)

Allows map/reduce technique queries where the result rows of the query are summarized by the reduce function before the results are returned.

The reduce function takes an ordered list of key/value pairs. These are the the keys and values from the index, as specified by the query parameters. The reduce function then aggregates these results together into a single object, then returns that object.

Common use cases are to provide subtotals, or averages, or summations of data.

Rereduce

The rereduce flag is used when querying large data sets. When the data set is large enough, the underlying system will break the map/reduce into smaller chucks, run the reduce on each chunk, then run reduce again on the reduced chunks. When this happens, the rereduce flag will true, the key array will be empty, and the value array will contain the partial reduced values.

Example:

Given an index that emits the type string of each document, and no value.

 let reduce =  { ( keys, values, rereduce) in
 
     var result: [String: Int] = [:]
 
     // if this is not a rereduce
     if !rereduce {
         // count each unique key value
         if let sKeys = keys as? [String] {
             for key in sKeys {
                 var count = result[key] ?? 0
                 count += 1
                 result[key] = count
             }
         }
     } else {
         // This is a rereduce, then our value array will be an array of
         // dictionaries of unique key values and their counts from above.
         if let counts = values as? [[String: Int]] {
             // for each result array
             for count in counts {
                 // for each key in the result
                 for key in count.keys {
                     // count and compile a final result dictionary
                     var count = result[key] ?? 0
                     count += 1
                     result[key] = count
                 }
             }
         }
     }
 
     // Return the unique key values
     // Note that regardless of the rereduce flag the result is the same data type. 
     // This must always be the case.
     return result
 }

Sorting

Indexes are sorted according to their keys. For simple data types like strings and number types this order is obvious. However, using a key that is an array is particularly useful to achieve a grouped sorting. Elements are compared in order of their array index. So, all the first array elements are compaired, then all the second elements, etc.

Observing Queries

Queries can be run in the background, and a closure called when the query results change. This is known as "observing" the query. The database function observeQuery(on: with: changehandler:) is used to observe the query. The changeHandler parameter is a closure that will receive the QueryResultEnumerator whenever changes to the database cause the query results to update. This function returns a QueryObserver object. To stop observing the query, and clean up system resources use the database method removeObserver().

Replication

Replication has two ways of controlling what documents are replicated. On the client side, a filter can be established to prevent some documents from being sent from the client to the server. Additionally, channels can be used to prevent the server from sending documents to the client from the server.

Filters

To establish a filter, assign a replicationFilterDelegate to the database. This delegate will be called to evaluate each local document and return true if the document should be sync'd to the server. The delegate's method will receive the document being evaluted, and an optional dictionary of filterParameters. These filter parameters are set in the ReplicationConfiguration object that initiated the replication.

Channels

Channels are part of the Predix Sync service. In the metadata of all documents is an array of strings, the channels property. These are the channel names. On the server side, Predix Sync can be configured to limit the channels a user has access to. Additionally, the ReplicationConfiguration has a limitToChannels property. An empty limitToChannels property (the default) will result in the client receiving all docuemnts the user has access to. However, if channel names are added to the limitToChannels property only those documents that contain those channels will be sent from the server to the client. This doesn't override the server security settings, but rather can further reduce the documents the client receives for that replication configuration.