如何通过GUID _id查找文档?

6

我使用这段代码来检索一些文档:

var client = new MongoClient(connectionString);
var database = client.GetDatabase(databaseName);
var collection = database.GetCollection<BsonDocument>(collectionName);

var json = "{created: {$gte: ISODate(\"2018-12-20T00:00:00.000Z\"), $lt: 
ISODate(\"2018-12-21T00:00:00.000Z\")}}";
BsonDocument query = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json);
var documents = collection.Find(query).Limit(10);

结果看起来像这样:
{ "_id" : CSUUID("75c5634c-b64b-4484-81f5-5b213228e272"), ..., "created" : ISODate("2018-12-20T23:59:13.375Z") }

我在尝试按_id过滤时无法检索到相同的文档。以下是我尝试过的筛选器(使用与上面相同的代码),但未能检索到文档:

var json = "{ \"_id\" : \"75c5634c-b64b-4484-81f5-5b213228e272\" }";
var json = "{ \"_id\" : CSUUID(\"75c5634c-b64b-4484-81f5-5b213228e272\") }";
var json = "{ \"_id\" : new BinData(4, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : BinData(4, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : new BinData(3, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : BinData(3, \"TGPFdUu2hESB9VshMijicg==\") }";
var json = "{ \"_id\" : { $eq: \"TGPFdUu2hESB9VshMijicg==\" } }";
var json = "{ \"_id\" : { $binary: \"TGPFdUu2hESB9VshMijicg==\", $type: 4 } }";
var json = "{ \"_id\" : { $binary: \"TGPFdUu2hESB9VshMijicg==\", $type: 3 } }";

注意,TGPFdUu2hESB9VshMijicg==是从guid获取一个base64编码字符串得到的:

Convert.ToBase64String((new Guid("75c5634c-b64b-4484-81f5-5b213228e272")).ToByteArray())

所有查询都没有抛出任何异常,但是它们没有返回任何文档。


我尝试重现这种情况,但在我的机器上使用CSUUID参数的第二个 json 正常工作。你能确认一下你正在使用哪个版本的MongoDB / MongoDB C#驱动程序吗? - mickl
MongoDB 2.6.12和C#驱动程序是v2.7.2(通过NuGet安装的MongoDB.Driver)。 - Eilert Hjelmeseth
我在本地安装了mongodb 2.6.3,并创建了一个插入文档到新集合的测试,成功检索到了该文档。不确定这个其他集合有什么不同。免责声明:我是mongodb的新手。 - Eilert Hjelmeseth
2个回答

7
MongoDb的C#驱动程序会尽力让你远离Json(Bson)表示法。简单来说,你有三种使用C#与MongoDb交互的方式:
  • 使用原始的Json(非常“服务器端”的编程方式)。
  • 使用瑞士军刀式的BsonDocument类。
  • 使用类型化的C#类。
当然,还有三种方式的组合,这使得事情变得更加糟糕 :-)
因此,在你的情况下,这是如何使用BsonDocument方式(不使用任何JSON):
var client = new MongoClient(myConnectionString);
var db = client.GetDatabase("myDb");
var guid = Guid.NewGuid();

// create an untyped document
var doc = new BsonDocument { { "_id", guid } };
var coll = db.GetCollection<BsonDocument>("myColl");
coll.InsertOne(doc);

// Builders<T> is central to help you build all sorts of mongodb JSON jargon (filters, sort, projections, etc.)
// instead of building it by yourself
var filter = Builders<BsonDocument>.Filter.Eq(new StringFieldDefinition<BsonDocument, Guid>("_id"), guid);
var foundDoc = coll.Find(filter).FirstOrDefault();
Console.WriteLine(foundDoc["_id"]);

以下是如何使用类型化文档的方法(不需要任何JSON和BsonDocument):

var client = new MongoClient(myConnectionString);
var db = client.GetDatabase("myDb");
var guid = Guid.NewGuid();

// create a class
var doc = new MyDoc { Id = guid };
var coll = db.GetCollection<MyDoc>("myColl");
coll.InsertOne(doc);

// we use a type that correspond to our busines layer/logic
// that's the easier way because you can use Linq syntax so we're far from JSON and document issues
// plus it's super readable in C#
var foundDoc = coll.Find(d => d.Id == guid).FirstOrDefault();
Console.WriteLine(foundDoc.Id);
...

// the typed-document (class)
class MyDoc
{
    [BsonId]
    public Guid Id { get; set; }

    ... other properties...
}

如您所见,最后一种方法更简单,但我们不能总是使用它。顺便说一句,遗憾的是该驱动程序不允许从BsonDocument派生MyDoc,因为这样我们将真正拥有最佳选择(编译但抛出异常...如果MongoDb C#开发人员看到这个...)。
现在,关于guids,您会注意到Console.WriteLine(foundDoc["_id"])显示UuidLegacy:0x87fa981983de774b998868046e257b19,因为MongoDb与guids有着悠久的历史。
正如您发现的那样,您可以更改BsonDefaults.GuidRepresentation。默认情况下,它是CSharpLegacy。
以下是在代码(客户端或服务器)中将guids显示为字符串时使用的前缀列表:
UUID:标准的(不是遗留的)
LUUID:遗留的(仅在服务器上可见)
CSUUID:C#遗留(仅在客户端上可见)
JUUID:Java遗留(仅在客户端上可见)
PYUUID:Python遗留(仅在客户端上可见)
第2和第3种方法也可以使您免于这些“内部”MongoDb问题的困扰。如果您使用这些方法,则无需更改BsonDefaults.GuidRepresentation。
因此,我的建议是尽量远离Json,在使用C#编程MongoDb时。

6
在创建MongoClient()之前加入以下内容解决了我的问题:
BsonDefaults.GuidRepresentation = GuidRepresentation.Standard;

看起来在C#端,MongoDB驱动程序将其解释为具有二进制子类型3的UUID。然而,在集合中保存的文档具有二进制子类型4。

此外,在更改之后,检索到的文档显示为“UUID()”,而不是“CSUUID()”:

{ "_id" : UUID("75c5634c-b64b-4484-81f5-5b213228e272"), ..., "created" : ISODate("2018-12-20T23:59:13.375Z") }

在我花费了比我愿意承认的更多时间来搜索网络和测试许多理论之后,通过浏览这篇文章,我终于有所突破:https://www.codeproject.com/Articles/987203/%2FArticles%2F987203%2FBest-Practices-for-GUID-data-in-MongoDB 该链接摘自以下内容:

MongoDB drivers usually store UUIDs as Binary fields with the legacy 0x03 subtype assigned by default. This configuration can be changed:

C#:

You can override the driver’s default settings and configure it to use the Binary 0x04 subtype by modifying the value of BsonDefaults.GuidRepresentation:

BsonDefaults.GuidRepresentation = GuidRepresentation.Standard; 

You can also modify GuidRepresentation at the server, database and collection level.

编辑:

这是我最终使用的json过滤器:

var json = "{ \"_id\" : UUID(\"75c5634c-b64b-4484-81f5-5b213228e272\") }";

注意:在设置GuidRepresentation时的关键信息是“甚至在创建MongoClient()之前”。 - Matt Williams

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接