- 前面几篇文章详细的介绍了,如何使用 Ubuntu 系统进行各种模式的集群部署.今天这里我们就开始正式的使用.
- 由于 MongoDB 操作很多,很多操作还很复杂,需要一些功底,所以这里先从数据库界的 Hello World(CRUD)开始.
- 本来想直接上来就讲操作的,才发现 Mongodb 的语法和一般数据库不一样,还得先介绍下查询条件.
- 查询条件常在更新和查询时候使用,当然使用聚合管道的时候也需要使用到.
- 这里先将支持的操作符分类列举出来.参考官方文档
比较(Comparison)
名字 |
含义 |
英文原文 |
$eq |
匹配 等于(==) 指定值的值 |
Matches values that are equal to a specified value. |
$gt |
匹配 大于(>) 指定值的值 |
Matches values that are greater than a specified value. |
$gte |
匹配 大于等于(>=) 指定值的值 |
Matches values that are greater than or equal to a specified value. |
$in |
匹配数组中指定的任何值 |
Matches any of the values specified in an array. |
$lt |
匹配 小于(<) 指定值的值 |
Matches values that are less than a specified value. |
$lte |
匹配 小于或等于(<=) 指定值的值 |
Matches values that are less than or equal to a specified value. |
$ne |
匹配 所有不等于(!=) 指定值的值 |
Matches all values that are not equal to a specified value. |
$nin |
与数组中指定的值均不匹配 |
Matches none of the values specified in an array. |
逻辑(Logical)
名字 |
含义 |
英文原文 |
$and |
用逻辑和连接查询子句,返回符合两个子句条件的所有文档 |
Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. |
$not |
反映一个查询表达式的效果,并返回与查询表达式不匹配的文件 |
Inverts the effect of a query expression and returns documents that do not match the query expression. |
$nor |
用逻辑 NOR 连接查询子句,返回所有不能匹配两个子句的文档 |
Joins query clauses with a logical NOR returns all documents that fail to match both clauses. |
$or |
用逻辑 OR 连接查询子句,返回符合任一子句条件的所有文档 |
Joins query clauses with a logical OR returns all documents that match the conditions of either clause. |
元素(Element)
名字 |
含义 |
英文原文 |
$exists |
匹配有指定字段的文档 |
Matches documents that have the specified field. |
$type |
如果一个字段是指定的类型,则选择文件 |
Selects documents if a field is of the specified type. |
评价(Evaluation)
这个英文翻译过来有点不知道理解了 🤣,看别人也这么翻译的,就暂且这么叫吧,这个方面的选择器,目前在工作中大部分尚未遇到,后期研究了再细究.
名字 |
含义 |
英文原文 |
$expr |
允许在查询语言中使用聚合表达式 |
Allows use of aggregation expressions within the query language. |
$jsonSchema |
根据给定的 JSON 模式验证文档 |
Allows use of aggregation expressions within the query language. |
$mod |
对一个字段的值进行模数运算,并选择具有指定结果的文档 |
Validate documents against the given JSON Schema. |
$regex |
选择那些数值与指定的正则表达式相匹配的文件 |
Selects documents where values match a specified regular expression. |
$text |
执行文本搜索 |
Performs text search. |
$where |
匹配满足 JavaScript 表达式的文档 |
Matches documents that satisfy a JavaScript expression. |
地理位置(Geospatial)
名字 |
含义 |
英文原文 |
$geoIntersects |
选择与 GeoJSON 几何体相交的几何体 |
Selects geometries that intersect with a GeoJSON geometry.The 2dsphere index supports geoIntersects. |
$geoWithin |
在一个有边界的 GeoJSON 几何体中选择几何体 |
Selects geometries within a bounding GeoJSON geometry. The 2dsphere and 2d indexes support geoWithin. |
$near |
返回靠近某个点的地理空间对象.需要地理空间索引 |
Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support near. |
$nearSphere |
返回与球体上的点附近的地理空间对象.需要地理空间索引 |
Returns geospatial objects in proximity to a point on a sphere. Requires a geospatial index. The 2dsphere and 2d indexes support nearSphere. |
数组(Array)
名字 |
含义 |
英文原文 |
$all |
匹配包含查询中指定的所有元素的数组 |
Matches arrays that contain all elements specified in the query. |
$elemMatch |
如果数组字段中的元素与所有指定的元素匹配,则选择文档 |
Selects documents if element in the array field matches all the specified elemMatch conditions. |
$size |
如果数组字段为指定大小,则选择文档 |
Selects documents if the array field is a specified size. |
按位(Bitwise)
名字 |
含义 |
英文原文 |
$bitsAllClear |
匹配数字或二进制值,其中一组比特位置的值均为 0 |
Matches numeric or binary values in which a set of bit positions all have a value of 0. |
$bitsAllSet |
匹配数字或二进制值,其中一组比特位置的值都是 1 |
Matches numeric or binary values in which a set of bit positions all have a value of 1. |
$bitsAnyClear |
匹配数字或二进制值,其中一组比特位置的任何一个比特的值为 0 |
Matches numeric or binary values in which any bit from a set of bit positions has a value of 0. |
$bitsAnySet |
匹配数字或二进制值,其中一组比特位置的任何一个比特的值为 1 |
Matches numeric or binary values in which any bit from a set of bit positions has a value of 1. |
投影运算符(Projection Operators)
名字 |
含义 |
英文原文 |
$ |
投射一个数组中符合查询条件的第一个元素 |
Projects the first element in an array that matches the query condition. |
$elemMatch |
投射一个数组中符合指定的elemMatch 条件的第一个元素 |
Projects the first element in an array that matches the specified elemMatch condition. |
$meta |
投射在 text 操作中分配的文档分数 |
Projects the document's score assigned during text operation. |
$slice |
限制从一个数组中投射的元素的数量.支持跳过和限制分片 |
Limits the number of elements projected from an array. Supports skip and limit slices. |
杂项运算符(Miscellaneous Operators)
名字 |
含义 |
英文原文 |
$comment |
为一个查询谓词添加注释 |
Adds a comment to a query predicate. |
$rand |
产生一个 0 到 1 之间的随机浮点数 |
Generates a random float between 0 and 1. |
字段更新(Field Update Operators)
名字 |
含义 |
英文原文 |
$currentDate |
将一个字段的值设置为当前日期,可以是日期或时间戳 |
Sets the value of a field to current date, either as a Date or a Timestamp. |
$inc |
将字段的值按指定的数量递增 |
Increments the value of the field by the specified amount. |
$min |
只在指定值小于现有字段值时更新字段 |
Only updates the field if the specified value is less than the existing field value. |
$max |
仅当指定的值大于现有字段的值时才更新字段 |
Only updates the field if the specified value is greater than the existing field value. |
$mul |
将字段的值乘以指定的数量 |
Multiplies the value of the field by the specified amount. |
$rename |
重命名一个字段 |
Renames a field. |
$set |
在文档中设置一个字段的值 |
Sets the value of a field in a document. |
$setOnInsert |
设置一个字段的值,如果一个更新操作导致了一个文档的插入.对修改现有文档的更新操作没有影响 |
Sets the value of a field if an update results in an insert of a document. Has no effect on update operations that modify existing documents. |
$unset |
从一个文档中删除指定的字段 |
Removes the specified field from a document. |
- 如上就是常用的一些查询操作符.其实不必害怕需要学习这么多,其实常用的就是比较和逻辑运算符,别的操作可以先了解下,当需要的时候再去官网查询.
- 接下来我们先使用 Navicat,或者 MongoDB Compass 连接到 MongoDB 进行一些常规的 CRUD 操作.我这里使用 Navicat,因为他支持中文.
- 首先我们新增一些测试数据.
- 直接点击新建查询.输入如下代码,点击执行.
db.getCollection('crud').insertOne({
'index': 0,
'name': '张三',
'age': 12,
'gender': '女',
'birthday': new Date('2023-01-14')
});
- 通过上述操作我们可以发现,mongodb 相当的方便,不需要先创建好数据库和集合即可直接插入数据.这也是我非常喜欢他的原因之一.虽然我这里提前创建了 test 数据库.
- 接下来介绍插入多条数据的操作.
var list = [];
for (var i = 0; i < 100; i++) {
list.push({
"index": i,
"name": "张三",
"age": i + 1,
"gender": "女",
"birthday": new Date('2023-01-14')
});
}
db.getCollection("crud").insertMany(list);
- 通过上述代码就可以发现 mongodb 的"SQL 语句"其实就是 JavaScript 代码,比起 SQL 语句,JavaScript 的逻辑清晰,更易理解了.
- 所以在可视化管理工具中,我们可以利用 JavaScript 代码来执行一些操作和查询以及复杂算法.
- 新增和批量新增均已经写完了,接下来就是更新了.
- 比如我们需要更新所有 index = 0 的 name 属性为李四
db.getCollection("crud").updateMany({
'index': 0
}, {
$set: {
'name': '李四'
}
});
db.getCollection("crud").updateMany({
'index': {
$eq: 0
}
}, {
$set: {
'name': '李四'
}
});
- 接下来是删除操作.
- 这里举例,删除 index 为 1-10 的所有文档
db.getCollection("crud").deleteMany({
'index': {
$lte: 10,
$gt: 0
}
});
- 再举个栗子,删除 index 在数组 [ 11, 13, 15, 49, 38, 25, 90, 99 ] 中的文档.
db.getCollection("crud").deleteMany({
'index': {
$in: [ 11, 13, 15, 49, 38, 25, 90, 99 ]
}
});
- 查询的话其实我们已经不用讲了,只需要知道查询的函数为 find,因为前边的更新和删除函数中的查询条件,其实和 find 的参数要求一样的.
- 这里还是举个例子.
db.getCollection("crud").find();
db.getCollection("crud").find({});
db.getCollection("crud").find({
'age': {
$gt: 20
}
});
db.getCollection("crud").find({
'age': 20,
'name': '李四'
});
- 至此,常规的使用 Navicat 进行 MongoDB 的 CRUD 就基本讲完了.有什么问题可以提问私信或者留言给我.
C#和.NET 7 如何使用呢?(从 0 开始)
- 既然是从 0 开始,那么我们就真从 0 开始.
- 不使用我封装过的 MongoDB 驱动,当然想了解的可以去看看源码
- 首先我们使用 VS 创建一个空白的 WebApi 项目.并且添加 MongoDB.Driver Nuget 包
- 然后创建一个 BaseDbContext.cs 用来作为 DBContext 的基类,其代码如下:
public class BaseDbContext
{
public IMongoClient Client { get; private set; } = default!;
public IMongoDatabase Database { get; private set; } = default!;
internal static T CreateInstance<T>(string connStr, string db = "hoyo") where T : BaseDbContext
{
var t = Activator.CreateInstance<T>();
var mongoUrl = new MongoUrl(connStr);
var clientSettings = MongoClientSettings.FromUrl(mongoUrl);
t.Client = new MongoClient(clientSettings);
var dbName = !string.IsNullOrWhiteSpace(mongoUrl.DatabaseName) ? mongoUrl.DatabaseName : db;
t.Database = t.Client.GetDatabase(dbName);
return t;
}
}
- 再创建一个 MongoExtension.cs 方便我们注册服务和配置 Mongodb 的一些行为.代码如下.
public static class MongoExtension
{
public static IServiceCollection AddMongoDbContext<T>(this IServiceCollection services, string connStr) where T : BaseDbContext
{
RegistryConventionPack();
var db = BaseDbContext.CreateInstance<T>(connStr);
_ = services.AddSingleton(db).AddSingleton(db.Database).AddSingleton(db.Client);
return services;
}
private static void RegistryConventionPack(bool first = true)
{
if (first)
{
var pack = new ConventionPack
{
new CamelCaseElementNameConvention(),
new IgnoreExtraElementsConvention(true),
new NamedIdMemberConvention("Id","ID"),
new EnumRepresentationConvention(BsonType.String),
};
ConventionRegistry.Register($"hoyo-pack-{Guid.NewGuid()}", pack, _ => true);
BsonSerializer.RegisterSerializer(new DateTimeSerializer(DateTimeKind.Local));
BsonSerializer.RegisterSerializer(new DecimalSerializer(BsonType.Decimal128));
BsonSerializer.RegisterSerializer(new DateOnlySerializer());
}
var idpack = new ConventionPack
{
new StringObjectIdIdGeneratorConvention()
};
ConventionRegistry.Register($"id-pack{Guid.NewGuid()}", idpack, _ => true);
}
}
internal class StringObjectIdIdGeneratorConvention : ConventionBase, IPostProcessingConvention
{
public void PostProcess(BsonClassMap classMap)
{
var idMemberMap = classMap.IdMemberMap;
if (idMemberMap is null || idMemberMap.IdGenerator is not null) return;
if (idMemberMap.MemberType == typeof(string)) _ = idMemberMap.SetIdGenerator(StringObjectIdGenerator.Instance).SetSerializer(new StringSerializer(BsonType.ObjectId));
}
}
- 由于上边临时决定添加一个 DateOnly 类型,而 Mongo 不支持这种新类型,所以我们需要自己实现他的序列化方式.所以这里加进来也是为了教大家如何去做类似的事情,比如 TimeOnly,以及他们对应的可空类型.
internal sealed class DateOnlySerializer : StructSerializerBase<DateOnly>
{
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateOnly value)
{
var str = value.ToString("yyyy-MM-dd");
context.Writer.WriteString(str);
}
public override DateOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var str = context.Reader.ReadString();
var success = DateOnly.TryParse(str, out var result);
return success ? result : throw new("不受支持的数据格式.");
}
}
- 基础的事情做好了后,就创建前边用到的数据类型的实体. Person.cs ,同时再性别这个字段使用枚举类型,来体现 MongoDB 将枚举转化成字符串格式存储的能力.
public class Person
{
public string Id { get; set; } = string.Empty;
public int Index { get; set; }
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
public Gender Gender { get; set; } = Gender.男;
public DateOnly Birthday { get; set; }
}
public enum Gender
{
男,
女
}
- 这些内容创建好后,我们紧接着创建我们实际使用的 DbContext.cs 用来管理我们业务中使用的集合.
public sealed class DbContext : BaseDbContext
{
public IMongoCollection<Person> Person => Database.GetCollection<Person>("person");
}
- 接下来再去 Programe.cs 中注入服务即可通过依赖注入来使用我们的 DbContext 了.这里贴上完整代码,便于观察.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
#region MongoDB服务注入
var dbStr = builder.Configuration.GetConnectionString("Mongo");
builder.Services.AddMongoDbContext<DbContext>(dbStr!);
#endregion
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
_ = app.UseSwagger().UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
- 接下来我们就正式使用 C#操作 MongoDB 了.
- 首先先删除模板项目的 WeatherForecast.cs 及对 WeatherForecast 控制器改个名.毕竟我们不需要看天气 😁.
- 我这里将名称改为了 MongoCrudController.cs,通过调整为如下内容.
[ApiController, Route("[controller]")]
public class MongoCrudController : ControllerBase
{
private readonly DbContext _db;
public MongoCrudController(DbContext db)
{
_db = db;
}
}
- 害,终于完成了符合我风格的基本配置.现在可以开始正式写 Crud 的代码了.
[ApiController, Route("[controller]")]
public class MongoCrudController : ControllerBase
{
private readonly DbContext _db;
public MongoCrudController(DbContext db)
{
_db = db;
}
private readonly FilterDefinitionBuilder<Person> _bf = Builders<Person>.Filter;
private readonly UpdateDefinitionBuilder<Person> _bu = Builders<Person>.Update;
[HttpPost("one")]
public async Task<Person> InsertOne()
{
var person = new Person
{
Name = "张三",
Age = 20,
Birthday = DateOnly.FromDateTime(DateTime.Now),
Gender = Gender.女,
Index = 0
};
await _db.Person.InsertOneAsync(person);
Console.WriteLine(person.Id);
return person;
}
[HttpPost("many")]
public async Task<IEnumerable<Person>> InsertMany()
{
var list = new List<Person>();
for (var i = 0; i < 100; i++)
{
list.Add(new()
{
Name = "张三",
Age = 20 + i,
Birthday = DateOnly.FromDateTime(DateTime.Now),
Gender = i % 2 == 0 ? Gender.女 : Gender.男,
Index = i
});
}
await _db.Person.InsertManyAsync(list);
return list;
}
[HttpGet("all")]
public async Task<IEnumerable<Person>> FindAll()
{
return await _db.Person.Find(_bf.Empty).ToListAsync();
}
[HttpGet("IndexIs0")]
public async Task<IEnumerable<Person>> FindIndexIs0()
{
return await _db.Person.Find(_bf.Eq(c => c.Index, 0)).ToListAsync();
}
[HttpPut("one/{index:int}")]
public async Task UpdateOne(int index)
{
_ = await _db.Person.UpdateOneAsync(c => c.Index == index, _bu.Set(c => c.Age, 17), new() { IsUpsert = true });
}
[HttpPut("IndexIs0")]
public async Task UpdateIndexIs0()
{
_ = await _db.Person.UpdateManyAsync(_bf.Eq(c => c.Index, 0), _bu.Set(c => c.Name, "李四"));
}
[HttpDelete("one/{index:int}")]
public async Task DeleteOne(int index)
{
_ = await _db.Person.DeleteOneAsync(c => c.Index == index);
}
[HttpDelete("many")]
public async Task DeleteMany()
{
var indexs = new[] { 12, 25, 14, 36, 95, 42 };
_ = await _db.Person.DeleteManyAsync(c => indexs.Contains(c.Index));
}
}
- 至此,基本上常规的 CRUD 操作就已经完成,可以启动程序后使用 Swagger 进行接口测试.
- 同时代码我也会推送至 GitHub 有需要参考的可以看看.
- 本文中使用的 MongoDB 的扩展和封装均已经通过更完善的方式在别的库中实现.前边已经提到过链接,欢迎使用.
- 有什么疑问或者不明白的地方可以给我留言.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步