MongoDB Array Query

本文关键字:Query Array MongoDB | 更新日期: 2023-09-27 18:04:36

我是新的mongodb,我想弄清楚如何做一些更复杂的查询。我有一个文档,它有一个嵌套的DateTime数组。

这是我的数据:

{ "_id" : ObjectId("537d0b8c2c6b912520798b76"), "FirstName" : "Mary", "LastName" : "Johnson", "Age" : 27, "Phone" : "555 555-1212", "Dates" : [ISODate("2014-05-21T21:34:16.378Z"), ISODate("1987-01-05T08:00:00Z")] }
{ "_id" : ObjectId("537e4a7e2c6b91371c684a34"), "FirstName" : "Walter", "LastName" : "White", "Age" : 52, "Phone" : "800 123-4567", "Dates" : [ISODate("1967-12-25T08:00:00Z"), ISODate("2014-12-25T08:00:00Z")] }

我想做的是返回文档,其中日期数组包含一个范围之间的值。在我的测试用例中,范围是1/1/1987和1/10/1987,所以我希望得到上面列出的第一个文档(Mary Johnson),因为1/5/1987在日期数组中,并且位于1/1/1987和1/10/1987之间。

我想能够做到这一点与MongoDB命令行实用程序和c#驱动程序。

使用c#驱动程序,我尝试了以下LINQ查询(基于MongoDB文档中的示例):

DateTime beginRange = new DateTime(1987, 1, 1);
DateTime endRange = new DateTime(1987, 1, 10);
var result = from p in people.AsQueryable<Person>()
    where p.Dates.Any(date => date > beginRange && date < endRange)
    select p;

上面的代码从c#驱动程序代码中抛出一个异常:

Any仅支持序列化为文档的项。当前的序列化器是DateTimeSerializer,必须实现IBsonDocumentSerializer才能参与Any查询。

当我尝试直接查询MongoDB数据库时,我尝试了以下操作:

db.People.find( {Dates: { $gt: ISODate("1987-01-01"), $lt: ISODate("1987-01-10") } } )

这个查询的结果是两个文档都返回,而不是只有一个在Dates数组中有1/5/1987的文档返回。

编辑:

我从c#驱动程序中找到了一种方法。它不像我想的那么干净,但它是可行的。

我想,既然有一种方法可以直接从命令实用程序中获得我想要的东西,那么必须有一种方法可以通过从c#驱动程序中执行相同的查询来从c#驱动程序中执行。

string command = "{Dates : { $elemMatch : { $gt: ISODate('"" + beginRange.ToString("yyyy-MM-dd") + "'"), $lt: ISODate('"" + endRange.ToString("yyyy-MM-dd") + "'") } } } ";
var bsonDoc = BsonSerializer.Deserialize<BsonDocument>(command);
var queryDoc = new QueryDocument(bsonDoc);
MongoCursor<Person> p = people.Find(queryDoc);

MongoDB Array Query

c# Driver

正如异常所暗示的那样,只要数组是原始类型(DateTime)而不是真正的文档,就不能使用c#驱动程序做你想做的事情。

来自MongoDB Linq Any的描述:

只有当枚举对象的元素被序列化为文档时才会起作用。有一个服务器错误阻止此操作针对原语。

我猜你可以在DateTime值周围创建一个文档包装器所以你仍然可以这样做:

var result = people.AsQueryable<Person>().Where(
    person => person.Dates.Any(date => 
        date.Value > beginRange && date.Value < endRange));

.

public class DocumentWrapper<T>
{
    public ObjectId Id { get; private set; }
    public T Value { get; private set; }
    public DocumentWrapper(T value)
    {
        Id = ObjectId.GenerateNewId();
        Value = value;
    }
}

原生查询

对于您的查询,它实际上并不等同于Linq查询。那就是:

{ 
    Dates : 
    {
        $elemMatch :
        { 
            $gt: ISODate("1987-01-01"), 
            $lt: ISODate("1987-01-10") 
        } 
    }
}

关于$elemMatch的更多信息