|
| 1 | +# MongoDB Tutorial |
| 2 | + |
| 3 | +This tutorial is meant to be a beginners guide to using MongoDB. |
| 4 | + |
| 5 | +## Get Started |
| 6 | + |
| 7 | +The fastest way to connect to a MongoDB instance is to use Docker. |
| 8 | + |
| 9 | +### Installing Docker Desktop |
| 10 | + |
| 11 | +Follow the follow installation guides to get Docker Desktop installed locally: |
| 12 | + |
| 13 | +[Docker Desktop](https://www.docker.com/products/docker-desktop/) |
| 14 | + |
| 15 | +### Playing with MongoDB with C# |
| 16 | + |
| 17 | +#### Installing the .NET SDK |
| 18 | + |
| 19 | +Before we get started, you'll need to have the .NET SDK installed, which can be installed via the download site: |
| 20 | + |
| 21 | +[Download .NET SDK](https://dotnet.microsoft.com/en-us/download) |
| 22 | + |
| 23 | +#### Project Setup |
| 24 | + |
| 25 | +Let's first create a directory where we'll add our C# project. |
| 26 | + |
| 27 | +```bash |
| 28 | +mkdir C:\dev\MongoDBCSharp |
| 29 | +cd C:\dev\MongoDBCSharp |
| 30 | +``` |
| 31 | + |
| 32 | +We'll then create a new console app using the `dotnet new` command within the .NET CLI. |
| 33 | + |
| 34 | +```bash |
| 35 | +dotnet new console |
| 36 | + |
| 37 | +The template "Console App" was created successfully. |
| 38 | + |
| 39 | +Processing post-creation actions... |
| 40 | +Restoring C:\dev\MongoDBCSharp\MongoDBCSharp.csproj: |
| 41 | + Determining projects to restore... |
| 42 | + Restored C:\dev\MongoDBCSharp\MongoDBCSharp.csproj (in 98 ms). |
| 43 | +Restore succeeded. |
| 44 | +``` |
| 45 | + |
| 46 | +We'll then need to add a dependacy from NuGet so that we can connect to a MongoDB instance. Again this can be done via the CLI. |
| 47 | + |
| 48 | +```bash |
| 49 | +dotnet add package MongoDB.Driver |
| 50 | +``` |
| 51 | + |
| 52 | +We'll also add a second dependacy which will help us output our results to the console called `Dumpify`. |
| 53 | + |
| 54 | +```bash |
| 55 | +dotnet add package Dumpify |
| 56 | +``` |
| 57 | + |
| 58 | +#### Database Interaction |
| 59 | + |
| 60 | +To do the next section we'll need to start up a local MongoDB instance, this again can be done via docker with the offical `mongo` image. |
| 61 | + |
| 62 | +```bash |
| 63 | +docker run -p 27017:27017 mongo:latest |
| 64 | +``` |
| 65 | + |
| 66 | +To interact with the database, the C# driver gives us a few objects to interact with the database. |
| 67 | + |
| 68 | +##### MongoClient |
| 69 | + |
| 70 | +The `MongoClient` is the client for interacting to any MongoDB instance. |
| 71 | + |
| 72 | +```csharp |
| 73 | +var client = new MongoClient(); |
| 74 | +``` |
| 75 | +##### IMongoDatabase |
| 76 | + |
| 77 | +The `IMongoDatabase` is the object for interacting to a single database within a MongoDB instance. |
| 78 | + |
| 79 | +```csharp |
| 80 | +var database = client.GetDatabase("test"); |
| 81 | +``` |
| 82 | + |
| 83 | +##### IMongoCollection<T> |
| 84 | + |
| 85 | +The `IMongoCollection<T>` is the object used for interacting to a single collection within a MongoDB instance. |
| 86 | +You'll notice this is a generic method, meaning that we can use standard [C# POCOs](https://en.wikipedia.org/wiki/Plain_old_CLR_object), but for the time being we'll use a BsonDocument which is a representation of BSON Document. |
| 87 | + |
| 88 | +```csharp |
| 89 | +var collection = database.GetCollection<BsonDocument>("vehicles"); |
| 90 | +``` |
| 91 | + |
| 92 | +### Querying Collections |
| 93 | + |
| 94 | +Within a database you'll find collections, collections are somewhat similar to tables within a relational database, so you might have a collection to store `vehicles` and another one to store `drivers`. |
| 95 | + |
| 96 | +When accessing the collection everything is created dynamically when required within MongoDB so we are not required to upfront declare a collection, we can just use it. |
| 97 | + |
| 98 | +So let's just query a `vehicles` and a `drivers` collection within the `test` database. |
| 99 | + |
| 100 | +Can your `Program.cs` file to the following code: |
| 101 | + |
| 102 | +```csharp |
| 103 | + |
| 104 | +using Dumpify; |
| 105 | +using MongoDB.Bson; |
| 106 | +using MongoDB.Driver; |
| 107 | + |
| 108 | +var client = new MongoClient(); |
| 109 | + |
| 110 | +// Get the test database |
| 111 | +var database = client.GetDatabase("test"); |
| 112 | + |
| 113 | +// Get the vehicles and drivers collection |
| 114 | +var vehicles = database.GetCollection<BsonDocument>("vehicles"); |
| 115 | +var drivers = database.GetCollection<BsonDocument>("drivers"); |
| 116 | + |
| 117 | +// Find items in our collection |
| 118 | +var results1 = await vehicles.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync(); |
| 119 | +var results2 = await drivers.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync(); |
| 120 | + |
| 121 | +// Output Results |
| 122 | +results1.Dump(); |
| 123 | +results2.Dump(); |
| 124 | +``` |
| 125 | + |
| 126 | +You'll notice that there is no error because there is no collection, this is one of MongoDB benefits of how easy it is to get going without any complex setup! But as you may have notice nothing is returned, this is because we've got nothing in these collections yet! |
| 127 | + |
| 128 | +MongoDB has one of the best documentation sites out there with pretty much all the information you'll ever need. We'll refer back to the documentation in some of these sections but feel free to take a browse: [The MongoDB Manual](https://docs.mongodb.com/manual/) |
| 129 | + |
| 130 | +## Be a Developer |
| 131 | + |
| 132 | +We're going to go though some basic developer operations that you might need to execute while running MongoDB. |
| 133 | + |
| 134 | +### Inserting data |
| 135 | + |
| 136 | +To insert data in to MongoDB we can execute 2 functions on our collection `InsertOneAsync()` and `InsertManyAsync()`. |
| 137 | + |
| 138 | +To insert a single document in to our vehicles collection call the `InsertOneAsync` function passing in an object: |
| 139 | + |
| 140 | +```csharp |
| 141 | +var bsonDocument = new BsonDocument |
| 142 | +{ |
| 143 | + ["type"] = "Car", |
| 144 | + ["year"] = "01", |
| 145 | + ["make"] = "Honda", |
| 146 | + ["model"] = "Civic", |
| 147 | + ["registrationNumber"] = "CU57ABC", |
| 148 | + ["colour"] = "red" |
| 149 | +}; |
| 150 | +await vehicles.InsertOneAsync(bsonDocument); |
| 151 | +bsonDocument.ToJson().Dump(); |
| 152 | +``` |
| 153 | +Now we can run our program using `dotnet run`. |
| 154 | +```bash |
| 155 | +dotnet run |
| 156 | + |
| 157 | +"{ "_id" : ObjectId("64539b557936d6b555b7a354"), "type" : "Car", "year" : "01", "make" : "Honda", "model" : "Civic", |
| 158 | +"registrationNumber" : "CU57ABC", "colour" : "red" }" |
| 159 | +``` |
| 160 | + |
| 161 | +As you'll notice that our `BsonDocument` document now has an extra property of `_id` which was set by the driver once the insert completed. |
| 162 | +We can also explicity set an `_id` of our choice and it'll use that, this can be of any support type too. |
| 163 | + |
| 164 | +```csharp |
| 165 | +var bsonDocument = new BsonDocument |
| 166 | +{ |
| 167 | + ["_id"] = 1, |
| 168 | + ["type"] = "Car", |
| 169 | + ["year"] = "01", |
| 170 | + ["make"] = "Honda", |
| 171 | + ["model"] = "Civic", |
| 172 | + ["registrationNumber"] = "CU57ABC", |
| 173 | + ["colour"] = "red" |
| 174 | +}; |
| 175 | +await vehicles.InsertOneAsync(bsonDocument); |
| 176 | +bsonDocument.ToJson().Dump(); |
| 177 | +``` |
| 178 | +```bash |
| 179 | +dotnet run |
| 180 | + |
| 181 | +"{ "_id" : 1, "type" : "Car", "year" : "01", "make" : "Honda", "model" : "Civic", "registrationNumber" : "CU57ABC", |
| 182 | +"colour" : "red" }" |
| 183 | +``` |
| 184 | + |
| 185 | +We can also insert documents that have completely different properties, for our `vehicles` collection let's insert in some bicycles! |
| 186 | + |
| 187 | +```csharp |
| 188 | +var bicycle = new BsonDocument |
| 189 | +{ |
| 190 | + ["type"] = "Bicycle", |
| 191 | + ["bicycleType"] = "Road Bike", |
| 192 | + ["year"] = 2019, |
| 193 | + ["make"] = "Giant", |
| 194 | + ["model"] = "Contend 1", |
| 195 | + ["colour"] = "red" |
| 196 | +}; |
| 197 | +await vehicles.InsertOneAsync(bicycle); |
| 198 | +``` |
| 199 | + |
| 200 | +This can be very beneficial when building applications that are required to store data of different structures. |
| 201 | + |
| 202 | +We can also insert many documents at once using the `insertMany()` function, let's just insert a bunch of vehicles in to our database: |
| 203 | + |
| 204 | +```csharp |
| 205 | +var input = new[] |
| 206 | +{ |
| 207 | + new BsonDocument |
| 208 | + { |
| 209 | + ["type"] = "Car", ["year"] = 2018, ["make"] = "Honda", ["model"] = "Civic", ["registrationNumber"] = "CE57ABC", ["colour"] = "blue" |
| 210 | + }, |
| 211 | + new BsonDocument |
| 212 | + { |
| 213 | + ["type"] = "Car", ["year"] = 2018, ["make"] = "Honda", ["model"] = "Civic", ["registrationNumber"] = "EU57ABC", ["colour"] = "blue" |
| 214 | + }, |
| 215 | + new BsonDocument |
| 216 | + { |
| 217 | + ["type"] = "Car", ["year"] = 2019, ["make"] = "Honda", ["model"] = "Jazz", ["registrationNumber"] = "BD57ABC", ["colour"] = "white" |
| 218 | + }, |
| 219 | + new BsonDocument |
| 220 | + { |
| 221 | + ["type"] = "Car", ["year"] = 2019, ["make"] = "Peugeot", ["model"] = "208", ["registrationNumber"] = "BD57DEF", ["colour"] = "yellow" |
| 222 | + }, |
| 223 | + new BsonDocument |
| 224 | + { |
| 225 | + ["type"] = "Car", ["year"] = 2019, ["make"] = "Renault", ["model"] = "Clio", ["registrationNumber"] = "TT57DEF", ["colour"] = "red" |
| 226 | + }, |
| 227 | + new BsonDocument |
| 228 | + { |
| 229 | + ["type"] = "Bicycle", ["bicycleType"] = "Road Bike", ["year"] = 2019, ["make"] = "Liv", ["model"] = "Avail 3", ["colour"] = "blue" |
| 230 | + }, |
| 231 | + new BsonDocument |
| 232 | + { |
| 233 | + ["type"] = "Bicycle", ["bicycleType"] = "Road Bike", ["year"] = 2019, ["make"] = "Specialized", ["model"] = "Allex E5", ["colour"] = "red" |
| 234 | + }, |
| 235 | + new BsonDocument |
| 236 | + { |
| 237 | + ["type"] = "Bicycle", ["bicycleType"] = "Mountain Bike", ["year"] = 2015, ["make"] = "Specialized", ["model"] = "Rockhopper Expert", ["colour"] = "sliver" |
| 238 | + }, |
| 239 | +}; |
| 240 | + |
| 241 | +await vehicles.InsertManyAsync(input); |
| 242 | +``` |
| 243 | + |
| 244 | +### Querying data |
| 245 | + |
| 246 | +To query data inside MongoDB we can call the `find` function on the collection and pass in a query. The query is a way to declare what we want to find. Let's start by finding our vehicle that we inserted in the previous section with the `_id`. |
| 247 | + |
| 248 | +We'll pass in the an object with an `_id` set to our id to find: |
| 249 | + |
| 250 | +```csharp |
| 251 | +var idToFind = 1; |
| 252 | +var result = await vehicles.Find(Builders<BsonDocument>.Filter.Eq(x => x["_id"], idToFind)).SingleAsync(); |
| 253 | +result.ToJson().Dump(); |
| 254 | +``` |
| 255 | +```bash |
| 256 | +dotnet run |
| 257 | +"{ "_id" : 1, "type" : "Car", "year" : "01", "make" : "Honda", "model" : "Civic", "registrationNumber" : "CU57ABC", |
| 258 | +"colour" : "red" }" |
| 259 | +``` |
| 260 | + |
| 261 | +As you can see we're using `Builders<BsonDocument>.Filter` this allows us to build up complex filter queries. |
| 262 | + |
| 263 | +The above query is using the [`$eq` Operator](https://docs.mongodb.com/manual/reference/operator/query/eq/#op._S_eq), this can alternatively be written like the follow query: |
| 264 | + |
| 265 | +MongoDB has a vast number of query operators which allows you to filter documents in many different ways, the full list of the operators can be found on their [documentation](https://docs.mongodb.com/manual/reference/operator/query/), which are all reflected on the `Builders<BsonDocument>.Filter` as factory methods. |
| 266 | + |
| 267 | +Let's now change this around to query for all bicycles: |
| 268 | + |
| 269 | +```csharp |
| 270 | +var filter = Builders<BsonDocument>.Filter.Eq(x => x["type"], "Bicycle"); |
| 271 | +var result = await vehicles.Find(filter).ToListAsync(); |
| 272 | +result.ToJson(new JsonWriterSettings { Indent = true }).Dump(); |
| 273 | +``` |
| 274 | +```bash |
| 275 | +dotnet run |
| 276 | +"[{ |
| 277 | + "_id" : ObjectId("64539e2e851f98b8186fe829"), |
| 278 | + "type" : "Bicycle", |
| 279 | + "bicycleType" : "Road Bike", |
| 280 | + "year" : 2019, |
| 281 | + "make" : "Liv", |
| 282 | + "model" : "Avail 3", |
| 283 | + "colour" : "blue" |
| 284 | + }, { |
| 285 | + "_id" : ObjectId("64539e2e851f98b8186fe82a"), |
| 286 | + "type" : "Bicycle", |
| 287 | + "bicycleType" : "Road Bike", |
| 288 | + "year" : 2019, |
| 289 | + "make" : "Specialized", |
| 290 | + "model" : "Allex E5", |
| 291 | + "colour" : "red" |
| 292 | + }, { |
| 293 | + "_id" : ObjectId("64539e2e851f98b8186fe82b"), |
| 294 | + "type" : "Bicycle", |
| 295 | + "bicycleType" : "Mountain Bike", |
| 296 | + "year" : 2015, |
| 297 | + "make" : "Specialized", |
| 298 | + "model" : "Rockhopper Expert", |
| 299 | + "colour" : "sliver" |
| 300 | + }]" |
| 301 | +``` |
| 302 | +We may also want to use a [less than operator](https://docs.mongodb.com/manual/reference/operator/query/lt/#op._S_lt) to only return bicycles that are less than the year of 2019. |
| 303 | + |
| 304 | +```javascript |
| 305 | +var filter = Builders<BsonDocument>.Filter.Eq(x => x["type"], "Bicycle") |
| 306 | + & Builders<BsonDocument>.Filter.Lt(x => x["year"], 2019); |
| 307 | +var result = await vehicles.Find(filter).ToListAsync(); |
| 308 | +result.ToJson(new JsonWriterSettings { Indent = true }).Dump(); |
| 309 | +``` |
| 310 | +```bash |
| 311 | +dotnet run |
| 312 | +"[{ |
| 313 | + "_id" : ObjectId("64539e2e851f98b8186fe82b"), |
| 314 | + "type" : "Bicycle", |
| 315 | + "bicycleType" : "Mountain Bike", |
| 316 | + "year" : 2015, |
| 317 | + "make" : "Specialized", |
| 318 | + "model" : "Rockhopper Expert", |
| 319 | + "colour" : "sliver" |
| 320 | + }]" |
| 321 | +``` |
| 322 | +Then we can also extend our query to return all vehicles that are of the colour of yellow as well. We can achieve this by using the [or operator](): |
| 323 | + |
| 324 | +```csharp |
| 325 | +var bicyclesLessThan2019Filter = Builders<BsonDocument>.Filter.Eq(x => x["type"], "Bicycle") |
| 326 | + & Builders<BsonDocument>.Filter.Lt(x => x["year"], 2019); |
| 327 | +var filter = bicyclesLessThan2019Filter | Builders<BsonDocument>.Filter.Eq(x => x["colour"], "yellow"); |
| 328 | +var result = await vehicles.Find(filter).ToListAsync(); |
| 329 | +result.ToJson(new JsonWriterSettings { Indent = true }).Dump(); |
| 330 | +``` |
| 331 | +```bash |
| 332 | +dotnet run |
| 333 | +"[{ |
| 334 | + "_id" : ObjectId("64539e2e851f98b8186fe827"), |
| 335 | + "type" : "Car", |
| 336 | + "year" : 2019, |
| 337 | + "make" : "Peugeot", |
| 338 | + "model" : "208", |
| 339 | + "registrationNumber" : "BD57DEF", |
| 340 | + "colour" : "yellow" |
| 341 | + }, { |
| 342 | + "_id" : ObjectId("64539e2e851f98b8186fe82b"), |
| 343 | + "type" : "Bicycle", |
| 344 | + "bicycleType" : "Mountain Bike", |
| 345 | + "year" : 2015, |
| 346 | + "make" : "Specialized", |
| 347 | + "model" : "Rockhopper Expert", |
| 348 | + "colour" : "sliver" |
| 349 | + }]" |
| 350 | +``` |
| 351 | +Now try build up some of your own queries to query our vehicles collection. |
| 352 | + |
| 353 | +### Updating data |
| 354 | + |
| 355 | +Updating data is similar to query it, we'll use a function called `UpdateOneAsync` on the collection. |
| 356 | + |
| 357 | +We first use the same queries used in finding the data but then we specify an update object on how we want to update the document. These update objects are built up of [update operators](https://docs.mongodb.com/manual/reference/operator/update/). |
| 358 | + |
| 359 | +Let's change the colour of our vehicle with the `_id` of `1` to `'orange'`. |
| 360 | + |
| 361 | +```javascript |
| 362 | +var filter = Builders<BsonDocument>.Filter.Eq(x => x["_id"], 1); |
| 363 | +var update = Builders<BsonDocument>.Update.Set(x => x["colour"], "orange"); |
| 364 | + |
| 365 | +var updateResult = await vehicles.UpdateOneAsync(filter, update); |
| 366 | +updateResult.Dump(); |
| 367 | + |
| 368 | +var doc = await vehicles.Find(filter).SingleAsync(); |
| 369 | +doc.ToJson().Dump(); |
| 370 | +``` |
| 371 | +```bash |
| 372 | +dotnet run |
| 373 | + Acknowledged |
| 374 | +┌──────────────────────────┬───────┐ |
| 375 | +│ Name │ Value │ |
| 376 | +├──────────────────────────┼───────┤ |
| 377 | +│ IsAcknowledged │ True │ |
| 378 | +│ IsModifiedCountAvailable │ True │ |
| 379 | +│ MatchedCount │ 1 │ |
| 380 | +│ ModifiedCount │ 1 │ |
| 381 | +│ UpsertedId │ null │ |
| 382 | +└──────────────────────────┴───────┘ |
| 383 | + |
| 384 | +"{ "_id" : 1, "type" : "Car", "year" : "01", "make" : "Honda", "model" : "Civic", "registrationNumber" : "CU57ABC", |
| 385 | +"colour" : "orange" }" |
| 386 | +``` |
| 387 | +Perfect, now we've got an orange car! |
| 388 | + |
| 389 | +Feel free to make up some of your own updates to the documents. |
| 390 | + |
| 391 | + |
| 392 | +## Querying parkrun Data |
| 393 | + |
| 394 | +See [README.md](README.md) |
0 commit comments