Directory
- First, preface
- (i) Operating environment
- Second, pre-preparatory work
- (i) Create Mongodbcontext MongoDB operation Context Class
- (ii) Creation of test classes
- (iii) Create test code
- Three, inline array add element operation
- (i) The Update.set () method replaces the inline array (deprecated)
- (ii) the Update.push () method presses elements directly into an inline array (recommended)
- (iii) the Update.pusheach () method presses multiple elements into an inline array (recommended)
- Iv. Inline Array Delete element operation
- (i) The Update.set () method replaces the inline array (deprecated)
- (ii) Update.pull () method Pull Delete an element (recommended)
- (iii) Update.pullfilter () method Delete filter Delete element (recommended)
- Five, inline array modify element operation
- (a) Update.set () Set first query after modification (not recommended)
- (ii) Update.set () with filter modification (recommended)
- Vi. Inline Array Lookup element operations (LINQ)
- (i) LINQ query a record (recommended)
- (ii) LINQ query paging (recommended)
- Vii. Summary
First, preface
This tutorial is a basic tutorial for getting started. It is mainly that the author encountered a lot of inconveniences when using the MongoDB .Net official driver in the project to operate the embedded document in MongoDB. Talk about the process of the author stepping on the pit.
The author's level is limited, if has the mistake also please criticize correct!
(i) Operating environment
. NET version
.Net Framwork 4.6.2 x64
MongoDB database version
MongoDb 3.6.2 x64
Driver version
MongoDb Driver 2.5
The MongoDB driver used by the author is the official version 2.5 driver, you can compile by downloading GitHub source code, or install it through NuGet Package management tool.
GitHub address: Poke a poke to view, Link
NuGet address: Poke a poke to view, Link
This example source has been uploaded to Gitee
This example source address: poke a poke, Link
Ii. preparatory work (i) Create Mongodbcontext MongoDB operation Context Class
Create a MongoDBContext operation context class to connect to the Mongodb database and manage Collection objects
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Security.Authentication;
using System.Threading.Tasks;
namespace MongoDBEmbeddedOperation
{
public class MongoDbContext
{
#region Fields
/// <summary>
/// cache Mongodb collection
/// </ summary>
private Dictionary <string, object> _collectionsMongoDb;
#endregion
#region Properties
/// <summary>
/// database connection string
/// </ summary>
public string ConnectionString {get;}
/// <summary>
/// Mongo client settings
/// </ summary>
public MongoClientSettings Settings {get;}
/// <summary>
/// Name database
/// </ summary>
public string DatabaseName {get;}
/// <summary>
/// Mongo context
/// </ summary>
public IMongoDatabase DbContext {get;}
#endregion
/// <summary>
/// MongoDb data context
/// </ summary>
/// <param name = "connectionString">
/// Connection string, such as: "mongodb: // username: [email protected]: port / [DatabaseName]? Ssl = true"
/// For details, see: http://www.runoob.com/mongodb/mongodb-connections.html
/// </ param>
public MongoDbContext (string connectionString)
{
if (string.IsNullOrWhiteSpace (connectionString))
throw new ArgumentException ("connectionString connection string cannot be empty!");
try
{
var mongoUrl = new MongoUrl (connectionString);
ConnectionString = connectionString;
Settings = MongoClientSettings.FromUrl (mongoUrl);
if (string.IsNullOrWhiteSpace (mongoUrl.DatabaseName))
throw new ArgumentException ("The database name cannot be empty!");
DatabaseName = mongoUrl.DatabaseName;
// SSL encryption
if (Settings.UseSsl)
{
Settings.SslSettings = new SslSettings
{
EnabledSslProtocols = SslProtocols.Tls12
};
}
var mongoClient = new MongoClient (Settings);
DbContext = mongoClient.GetDatabase (DatabaseName);
}
catch (Exception e)
{
// TODO: log error
throw;
}
}
/// <summary>
/// Get the collection asynchronously
/// </ summary>
/// <typeparam name = "TEntity"> </ typeparam>
/// <returns> </ returns>
public async Task <IMongoCollection <TEntity >> GetCollectionAsync <TEntity> () where TEntity: class
{
// if the collection cache is empty, create one
if (_collectionsMongoDb == null)
{
_collectionsMongoDb = new Dictionary <string, object> ();
}
// Get the collection name, using the standard is to add Set after the entity type name
var collectionName = $ "{typeof (TEntity) .Name} Set";
// If the collection does not exist, create the collection
if (false == await IsCollectionExistsAsync <TEntity> ())
{
await DbContext.CreateCollectionAsync (collectionName);
}
// If the collection does not exist in the cache, then join the cache
if (! _collectionsMongoDb.ContainsKey (collectionName))
{
_collectionsMongoDb [collectionName] = DbContext.GetCollection <TEntity> (collectionName);
}
// fetch the collection from the cache and return
return (IMongoCollection <TEntity>) _ collectionsMongoDb [collectionName];
}
/// <summary>
/// Does the collection exist
/// </ summary>
/// <typeparam name = "TEntity"> </ typeparam>
/// <returns> </ returns>
public async Task <bool> IsCollectionExistsAsync <TEntity> ()
{
var filter = new BsonDocument ("name", $ "{typeof (TEntity) .Name} Set");
// Filter by collection name
var collections = await DbContext.ListCollectionsAsync (new ListCollectionsOptions {Filter = filter});
// check for existence
return await collections.AnyAsync ();
}
}
}
(ii) Creation of test classes
Suppose you are currently an IoT data acquisition project with many sensor nodes (Sensornode), each generating a record (Records), and a record that consists of data values and recording time (Recorddatetime). Requires that its recods embedded array to be deleted and modified operation.
Then from the above requirements can be abstracted from the following object relationship, in practice, it is not recommended to put records directly under Sensornode, because the MongoDB database on a single document has a 16MB size limit .
/// <summary>
/// sensor node
/// </ summary>
public class SensorNode
{
/// <summary>
/// ID
/// </ summary>
public ObjectId Id {get; set;}
/// <summary>
/// Records
/// </ summary>
public List <Record> Records {get; set;}
}
/// <summary>
/// data record
/// </ summary>
public class Record
{
/// <summary>
/// data value
/// </ summary>
public double Data {get; set;}
/// <summary>
/// record time
/// </ summary>
public DateTime RecorDateTime {get; set;}
}
Test the Bson structure for the class, as shown below.
{
"_id" : ObjectId("5aa877e4aa25752ab4d4cae3"),
"Records" : [
{
"Data" : 1962163552.0,
"RecorDateTime" : ISODate("2018-03-16T10:01:14.931Z")
},
{
"Data" : 1111405346.0,
"RecorDateTime" : ISODate("2018-03-16T08:53:14.931Z")
},
......
]
}
(iii) Create test code
Create a new console program and add the following code. Initialize MongodbContext and insert SensorNode for testing.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver.Linq; // <---- This reference is very important
using MongoDB.Driver;
...
private static ObjectId _sensorNodeId;
private static MongoDbContext _dbContext;
private static IMongoCollection <SensorNode> _sensorNodes;
static void Main (string [] args)
{
// Create a dbContext object using a link string
_dbContext = new MongoDbContext ("mongodb: // hynuAdmin: [email protected]: 27017 / HynuIOTA");
// Create the _Sensors database, the entry method cannot use asynchronous, we wait for the result to return
_sensorNodes = _dbContext.GetCollectionAsync <SensorNode> (). Result;
// Create a SensorNode and store it in the MongoDb database
_sensorNodeId = ObjectId.Parse ("5aa877e4aa25752ab4d4cae3");
#region Creating Test Cases
var sensorNode = new SensorNode ()
{
Id = _sensorNodeId,
Records = new List <Record> ()
};
// insert into database
_sensorNodes.InsertOne (sensorNode);
#endregion
}
Add a method for randomly generating Record objects.
/// <summary>
/// Generate count Rocred objects
/// </ summary>
/// <param name = "count"> Generation quantity </ param>
/// <returns> </ returns>
static List <Record> GetRandomRecord (int count)
{
var records = new List <Record> ();
var rd = new Random ();
for (int i = 0; i <count; i ++)
{
records.Add (new Record ()
{
Data = rd.Next (),
RecorDateTime = DateTime.Now.AddMinutes (rd.Next ()% 100)
});
}
return records;
}
Three, inline array add element operation (a) Update.set () method replaces an inline array (deprecated)
This method mainly uses the operator [$set], the idea is to first find out the parent element of the embedded document, then the sub-document array for the pruning operation, and finally the sub-document array is replaced again. This is a low-performance approach and is not recommended unless you are involved in large-volume changes.
#region Method 1. Detect sensor and update Records, $ set operation-insert 100 elements randomly (not recommended)
// Query the sensorNode object to be modified
var sensor = await _sensorNodes.Find (s => s.Id == _sensorNodeId) .FirstOrDefaultAsync ();
// Add random elements to Records
sensor.Records.AddRange (GetRandomRecord (100));
// Build update Bson, replace the original Records with new Records
var update = Builders <SensorNode> .Update.Set (d => d.Records, sensor.Records);
// use Update method
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, update);
#endregion
(ii) the Update.push () method presses elements directly into an inline array (recommended)
This method mainly uses the operator [$push] to increase the element to the sub-document array, the performance is good, recommended to use.
// Build update Bson, add a record using Push method
var updateOne = Builders <SensorNode> .Update.Push (d => d.Records, GetRandomRecord (1) .First ());
// update
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, updateMany);
(iii) the Update.pusheach () method presses multiple elements into an inline array (recommended)
This method also uses the [$push] operator, except that it is allowed to receive arrays when generating bsondocument.
#region method two. $ push operation-randomly insert 100 elements (recommended)
// Build update Bson and add multiple records using PushEach method
var updateMany = Builders <SensorNode> .Update.PushEach (d => d.Records, GetRandomRecord (100));
// update
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, updateMany);
#endregion
Iv. Inline Array Delete element operation (a) Update.set () method replaces an inline array (deprecated)
This way of thinking and the idea of adding elements consistent, low performance, not recommended.
#region method one. $ set delete-delete the first 5 elements (not recommended)
// Query the sensorNode object to be modified
var sensor = await _sensorNodes.Find (s => s.Id == _sensorNodeId) .FirstOrDefaultAsync ();
// delete Record
for (int i = 0; i <5; i ++)
{
sensor.Records.RemoveAt (i);
}
// Build update Bson, replace the original Records with new Records
var update = Builders <SensorNode> .Update.Set (d => d.Records, sensor.Records);
// use Update method
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, update);
#endregion
(ii) Update.pull () method Pull Delete an element (recommended)
// Assuming that the Record to be deleted has been converted to an object by query, then use Pull to delete an element
var record = new Record ()
{Data = 1670941112.0, RecorDateTime = DateTime.Parse ("2018-03-16T09: 36: 14.930Z")};
// Build Pull to remove Bson
var update = Builders <SensorNode> .Update.Pull (s => s.Records, record);
// use Update method
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, update);
(iii) Update.pullfilter () method Delete filter Delete element (recommended)
#region Method Three. PullFilter Deletion-Delete Elements That Meet the Filter Condition (Recommended)
// If r.Data == 339119843.0, remove from array
var update = Builders <SensorNode> .Update.PullFilter (s => s.Records, r => r.Data == 339119843.0);
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, update);
#endregion
Five, inline array modification element operation (a) Update.set () Set first query and then modify (not recommended)
This method is not recommended for low performance.
#region method one. $ set method modification (change the record time of an element to the minimum value)-first query and then modify (not recommended)
// Query the sensorNode object to be modified
var sensor = await _sensorNodes.Find (s => s.Id == _sensorNodeId) .FirstOrDefaultAsync ();
// modify Record value
sensor.Records [0] .RecorDateTime = DateTime.MinValue;
// Build update Bson, replace the original Records with new Records
var update = Builders <SensorNode> .Update.Set (d => d.Records, sensor.Records);
// use Update method
await _sensorNodes.UpdateOneAsync (s => s.Id == _sensorNodeId, update);
#endregion
(ii) Update.set () with filter modification (recommended)
Complete filtering and modification within the database, high performance, recommended use.
#region method two. $ set method modification (change the element record time of Data = 1835821478.0 to the maximum value)-direct filter modification (recommended)
// construct filter
var filter = Builders <SensorNode> .Filter.Where (s => s.Id == _sensorNodeId)
& Builders <SensorNode> .Filter.Where (d => d.Records.Any (r => r.Data == 1835821478.0));
// perform the update
var update = Builders <SensorNode> .Update.Set (d => d.Records [-1] .RecorDateTime, DateTime.MaxValue);
await _sensorNodes.UpdateOneAsync (filter, update);
#endregion
Vi. Inline Array Lookup element operations (LINQ)
For inline array / document query, it is recommended to use Linq or BsonDocument directly in .net. The author focuses on using the Linq method.
The Using MongoDB.Driver.Linqis required before Linq is not available.
(i) LINQ query a record (recommended)
Primarily the use of the SelectMany () method, which is used to select an array.
#region SelectManyQuery the embedded array (Query the first record of Data = 1340695206.0)-(Recommended)
// converted to Queryable
var result = await _sensorNodes.AsQueryable ()
// Find the corresponding sensorNode
.Where (s => s.Id == _sensorNodeId)
// select Records embedded array
.SelectMany (s => s.Records)
// Find elements with Data == 1340695206.0
.Where (r => r.Data == 1340695206.0)
// take the first
.FirstOrDefaultAsync ();
#endregion
(ii) LINQ query paging (recommended)
The main difference between using SelectMany and EF LINQ is the same way
#region SelectMany, Skip, Take Sort Array Paging-(Recommended)
// page number
var index = 4;
// page size
var size = 10;
// converted to Queryable
var page = await _sensorNodes.AsQueryable ()
// Find the corresponding sensorNode
.Where (s => s.Id == _sensorNodeId)
// select Records embedded array
.SelectMany (s => s.Records)
// Sort by record time
.OrderBy (r => r.RecorDateTime)
// skip index-1 page of data
.Skip ((index-1) * size)
// Select a page of data
.Take (size)
// convert to collection
.ToListAsync ();
#endregion
The query operation for the embedded array is mainly realized through aggregation. After the above code is converted to the native mode, the following command is used.
aggregate (
[
{"$ match": {"_id": ObjectId ("5aa877e4aa25752ab4d4cae3")}},
{"$ unwind": "$ Records"},
{"$ project": {"Records": "$ Records", "_id": 0}},
{"$ sort": {"Records.RecorDateTime": 1}}, {"$ skip": 30}, {"$ limit": 10}
]);
If both System.Linq and MongoDB.Driver.Linq are referenced at the same time, the method ambiguity is generated by invoking the Linq method, and the solution is linked as follows.
Linq Method Ambiguity Solution:
http://blog.csdn.net/qq_27441069/article/details/79586216
Vii. Summary
This article briefly introduces the MongoDB C # driver for adding, deleting, modifying, and checking operations on embedded documents. The author's level is limited. If there are errors, please criticize and correct them! Welcome to reprint, please indicate the source!
MongoDB. Net Driver (C # driver)-inline array/embedded document operations (add, delete, modify, query (Linq paging))