MongoDB drivers exist for a whole range of programming languages, including Dart. We will use mongo_dart
in our app, which is a server-side driver implemented purely in Dart. Simply add mongo_dart: any
to your app's pubspec.yaml
, and issue pub install. In the code, write:
import 'package:mongo_dart/mongo_dart.dart';
todo_mongodb
is a version of todo_server_dartling_mysql
, but now use MongoDB as a persistent data source (clone the project from https://github.com/dzenanr/todo_mongodb). It contains a client app todo_client_idb
, identical to the one in the previous section, which stores data in IndexedDB
. The server part todo_server_dartling_mongodb
can be started by running bin/server.dart
; you should see in the editor or the console:
Server at http://127.0.0.1:8080;
If you see the following exception, it means that the mongod server has not yet been started:
SocketException: Connection failed (OS Error: No connection could be made because the target machine actively refused it., errno = 10061), address = 127.0.0.1, port = 27017.
Run a Dartium client (todo_client_idb/web/app.html
) from the editor and a JavaScript client in Chrome or another browser. Fill in some tasks if you see nothing to do and click the To Server button. The mongod
console outputs that it has created a data file with index, and that it has inserted a number of rows:
Fri Aug 23 11:19:55.839 [FileAllocator] allocating new datafile datadb odo.1, filling with zeroes... Fri Aug 23 11:19:55.842 [conn1] build index todo.tasks { _id: 1 } Fri Aug 23 11:19:55.844 [conn1] build index done. scanned 0 total records. 0.002 secs Fri Aug 23 11:19:55.846 [conn1] insert todo.tasks ninserted:1 keyUpdates:0 locks( micros) w:412870 412ms Fri Aug 23 11:19:56.466 [FileAllocator] done allocating datafile datadb odo.1, size: 128MB, took 0.625 secs
Synchronize the other clients by clicking their From Server button. To verify that the data is actually in MongoDB, open a Mongo shell by typing mongo
in a console and issuing the commands to retrieve the documents from the tasks
collection in the todo
database:
> use todo
The output will be as follows:
switched to db todo
The command to retrieve the documents is as follows:
> db.tasks.find()
The output will be as follows:
{ "_id" : ObjectId("5217293b479e4132cdecef0e"), "title" : "administration", "comp leted" : false, "updated" : ISODate("2013-08-16T09:14:50.569Z") } ...
Running test/mongodb_test.dart
is an alternative to create and populate the database todo
with a tasks collection. From the structure of our app, we can deduce that the data access code resides in lib/persistence/mongodb.dart
, which contains the classes TodoDb
and TaskCollection
. Let us now see how our Dart code reaches out to MongoDB. In the main
method of the server script, we see:
void main() { db = new TodoDb(); (1) db.open().then((_) { (2) start(); }); }
Line (1)
calls the constructor from TodoDb
, and in line (2)
, the database is opened. The following is the code from the TodoDb
class:
class TodoDb implements ActionReactionApi { static const String DEFAULT_URI = 'mongodb://127.0.0.1/'; static const String DB_NAME = 'todo'; TodoModels domain; DomainSession session; MvcEntries model; Tasks tasks; Db db; TaskCollection taskCollection; TodoDb() { (3) var repo = new TodoRepo(); domain = repo.getDomainModels('Todo'), domain.startActionReaction(this); session = domain.newSession(); model = domain.getModelEntries('Mvc'), tasks = model.tasks; } Future open() { Completer completer = new Completer(); db = new Db('${DEFAULT_URI}${DB_NAME}'), (4) db.open().then((_) { (5) taskCollection = new TaskCollection(this); (6) taskCollection.load().then((_) { (7) completer.complete(); }); }).catchError(print); return completer.future; } close() { db.close(); } react(ActionApi action) { if (action is AddAction) { taskCollection.insert((action as AddAction).entity); } else if (action is RemoveAction) { taskCollection.delete((action as RemoveAction).entity); } else if (action is SetAttributeAction) { taskCollection.update((action as SetAttributeAction).entity); } } }
The constructor starting in line (3)
does everything necessary to start up the Dartling model. The open
method creates a new MongoDB database in line (4)
(with the substituted value):
db = new Db('mongodb://127.0.0.1/todo );
The Db
class comes from mongo_dart
; it has an open
method called in line (5)
. From the then
keyword, we see that it returns a Future as expected. As dictated by Dartling, any change in the app's data calls react to update the model and the database, as TodoDb
listens to actions in the model through the line:
domain.startActionReaction(this));
When an action happens, all listeners are informed about that action in the react
method for listeners. The TaskCollection
class, whose object is constructed in line (6)
, is the closest we will get to MongoDB in this project, again, using Futures throughout. The code is as follows:
class TaskCollection { static const String COLLECTION_NAME = 'tasks'; TodoDb todo; DbCollection dbTasks; TaskCollection(this.todo) { dbTasks = todo.db.collection(COLLECTION_NAME); (8) } Future load() { (9) Completer completer = new Completer(); dbTasks.find().toList().then((taskList) { taskList.forEach((taskMap) { var task = new Task.fromDb(todo.tasks.concept, taskMap); todo.tasks.add(task); }); completer.complete(); }).catchError(print); return completer.future; } Future<Task> insert(Task task) { var completer = new Completer(); var taskMap = task.toDb(); dbTasks.insert(taskMap).then((_) { print('inserted task: ${task.title}'), completer.complete(); }).catchError(print); return completer.future; } Future<Task> delete(Task task) { var completer = new Completer(); var taskMap = task.toDb(); dbTasks.remove(taskMap).then((_) { print('removed task: ${task.title}'), completer.complete(); }).catchError(print); return completer.future; } Future<Task> update(Task task) { var completer = new Completer(); var taskMap = task.toDb(); dbTasks.update({"title": taskMap['title']}, taskMap).then((_){ print('updated task: ${task.title}'), completer.complete(); }).catchError(print); return completer.future; } }
It contains a DbCollection
object (also defined in mongo_dart
) named dbTasks
, which mirrors the tasks collection in the database through the assignment in line (8
) of the constructor. In line (9
), its load
method (called in the previous code snippet in line (7
)) calls find()
on dbTasks
. When the results return, a new Task object is made for each document, found, and added to the tasks collection. The insert
, delete
, and update
methods respectively call insert, remove, and update on the DbCollection
object dbTasks
. Notice the exceptional handling clause that will signal any error:
.catchError(print);
When a server starts, all data from a database are loaded into the model in the main memory. When the model changes, these changes are propagated immediately to the database through actions/reactions. In this approach, a database system is used only minimally: all searches for the data are done in the main memory without using the slower database system. With Dartling, a model is not dependent on MongoDB—the model does not call the insert
, update
, and remove
methods. The use of (only a few methods of) the mongo_dart
driver shields us from using and knowing the slightly more elaborate MongoDB commands in our Dart code. The complete API reference of the driver can be found at http://vadimtsushko.github.io/mongo_dart/mongo_dart/Db.html.
Again, we clearly see the advantages of using a modeling framework and a well-structured library; our data access code in lib/persistence was all we needed to adapt while changing from the MySQL version to the MongoDB version!
3.147.75.221