In our previous tutorials, we took a look at in-memory and file persistence component implementations. Another frequent choice of persistence is Pip.Service’s MongoDb persistence. This persistence stores data in MongoDB - a popular document-oriented database.
The most basic implementation of this component is the MongoDbPersistence class defined in the MongoDb module. It is capable of storing a collection of documents, opening and closing connections, and performing a few simple CRUD operations.
This is a basic component that stores data items of any type. Some basic operations for creating, getting, and deleting are already included. More advanced CRUD operations over the data items can be implemented in child classes by accessing the this._collection or this._model properties. This component also contains methods for opening and closing connections using the credentials provided.
The example below demonstrates a class that implements the MongoDB persistence component for the Beacon data model.
And this is how we could use such a class:
As mentioned earlier, the MongoDbPersistence contains methods for opening and closing connections. To connect to the appropriate database and collection, we need to first configure the connection with all necessary parameters. MongoDbPersistence uses the MongoDbConnection class for establishing connections.
The MongoDbConnection class provides MongoDB connectivity using a plain driver. To reduce the number of database connections needed, a connection can be defined and then shared through multiple persistence components.
By default, MongoDbPersistence tries to establish a local connection on MongoDb’s default port - 27017. If the desired MongoDb server is located elsewhere, the persistence should be configured with the corresponding host and port information. Persistence configuration can be performed in a number of ways.
The example below demonstrates how the ConfigParams class can be used for persistence configuration. To learn more about this class, and about microservice configuration in general, be sure to read this.
Likewise, a connection can be configured using a configuration file. In this case, there exist two approaches:
1) configuring multiple persistences using a common MongoDbConnection,
2) configuring a single persistence with its own, private MongoDbConnection.
To perform configuration using a single MongoDbConnection, one of the following descriptors should be used:
pip-services:connection:mongodb:*:1.0 or pip-services3:connection:mongodb:*:1.0.
To learn more about references, descriptors, and component references, follow this link.
First, add an element with the “pip-services” descriptor to the configuration file.
...
# MongoDb Connection
- descriptor: "pip-services:connection:mongodb:default:1.0"
connection:
host: localhost
port: 30000
...
Next, register the persistence as a component in the microservice’s Factory:
And add the DefaultMongoDbFactory to the microservice’s ProcessContainer:
If we’re configuring just a single connection to the Beacons MongoDB persistence, the connection configuration should use the “beacons” descriptor:
...
# MongoDb persistence
- descriptor: "beacons:persistence:mongodb:default:1.0"
connection:
host: localhost
port: 30000
…
The implementation we will be working with going forward is called the IdentifiableMongoDbPersistence. It stores and processes data objects that have a unique ID field and implement the IIdentifiable interface defined in the Commons module.
IdentifiableMongoDbPersistence implements a number of CRUD operations that are based on working with the model's id in a predefined manner. In addition, it provides methods for getting paginated results and listing data using detailed filter, sort, and even projection parameters.
We can build upon the IdentifiableMongoDbPersistence by overriding its ComposeFilter method:
In most scenarios, child classes only need to override the GetPageByFilter(), GetListByFilter(), or DeleteByFilter() operations using a custom filter function (like the ComposeFilter function in the example above). All of the other operations can be used straight out of the box. Developers can implement custom methods by directly accessing the data objects, which are stored in the _collection property. See the MongoDb module’s API documentation for more details.
Persistence components in the Pip.Services Toolkit use a number of data patterns. IdentifiableMongoDbPersistence, for example, supports Filtering. This pattern allows clients to use a FilterParams object to describe a subset of data using key-value pairs. These FilterParams can then be used for retrieving data in accordance with the specified search criteria (see the Commons module).
In the persistence component, the developer is responsible for parsing FilterParams and passing a filter function to the persistence’s methods of the base class.
Another common data pattern is Paging. It is used to retrieve large datasets in chunks, through multiple calls to the storage. A client can ask for the results to be paged by specifying a set of PagingParams, which include the starting position and the number of objects to return. Clients can also request the total number of items in the dataset using PagingParams, but this parameter is optional. A DataPage object with a subset of the data will be returned as the result.
As mentioned above, developers can also implement custom persistence methods. The _collection property can be used to access data objects from within such methods. Below is an example of a custom GetOneByNameAsync persistence method.
When we put everything together, we end up with the following component:
The following example demonstrates how we can use our newly created persistence for writing and reading Beacon objects to a MongoDB: