无法使用C#Driver过滤MongoDB中的日期字段

时间:2016-05-04 13:37:48

标签: c# json mongodb linq mongodb-.net-driver

我使用Newtonsoft JsonTextWriter手动序列化大量POCO并将结果保存为MongoDB BsonDocument。

//
// POCO to store in MongoDB
public class Session
{

    public DateTime? StartUTCTimestamp { get; set; }

    public DateTime? StartTimestamp { get; set; }

    public DateTime? EndTimestamp { get; set; }

    public void ToJSON(ref JsonTextWriter writer)
    {
        Session session = this;            

        writer.WriteStartObject(); // {

        writer.WritePropertyName("StartUTCTimestamp");
        writer.WriteValue(session.StartUTCTimestamp);

        writer.WritePropertyName("StartTimestamp");
        writer.WriteValue(session.StartTimestamp);

        writer.WritePropertyName("EndTimestamp");
        writer.WriteValue(session.EndTimestamp);


        writer.WriteEndObject(); // }
    }
}

测试应用程序中用于导入数据的方法,从SQL Server数据库中检索所有 Session 对象(使用Telerik的Open Access ORM),将结果存储在名单。我通过调用POCO上定义的ToJSON()方法(见上文)序列化每个Session,并传入对JsonTextWriter的引用。然后使用C#MongoDB驱动程序将生成的JSON字符串反序列化为BsonDocument,然后将其保存到Mongo。 (以下示例来自ASP.NET Web窗体页面,因此警报框用户控件)。

private void LoadData()
{
    DateTime startDate = new DateTime(2015,12,31,23,59,59);
    var collection = _database.GetCollection<BsonDocument>("sessions");

    using (DbContext ctx = new DbContext())
    {
        List<Session> sessions = ctx.Sessions.Where().ToList();

        foreach (Session item in sessions)
        {
            JsonTextWriter writer = null;
            try
            {
                StringWriter sw = new StringWriter();
                writer = new JsonTextWriter(sw);
                writer.CloseOutput = true;
                item.ToJSON(ref writer);
                String json = sw.ToString();

                BsonDocument doc = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(json);

                collection.InsertOne(doc);
                this.ucAlertMsg.Show("bg-info", "Completed without exception");
            }
            catch (Exception ex)
            {
                while (ex.InnerException != null) { ex = ex.InnerException; }
                this.ucAlertMsg.Show("bg-danger", ex.Message);
            }
            finally
            {
                writer.Close();
            }
        }
    }
}

这样可以保存文档,但是,我无法有效地查询文档,在日期范围内进行过滤。我相信,根据我读过的其他几篇文章和文章,这可能是因为该值存储为字符串而不是&#34; ISODate()&#34;。

// 
// This is what IS saved
{
    "_id" : ObjectId("5729128cd9017a248cbe6284"),
    "StartUTCTimestamp" : "2015-12-15T23:24:06",
    "StartTimestamp" : "2015-12-15T18:24:06",
    "EndTimestamp" : "2015-12-15T18:26:59",
}

//
// Is this what I need? 
{
    "_id" : ObjectId("5729128cd9017a248cbe6284"),
    "StartUTCTimestamp" : ISODate("2015-12-15T23:24:06"),
    "StartTimestamp" :  ISODate("2015-12-15T18:24:06"),
    "EndTimestamp" :  ISODate("2015-12-15T18:26:59"),
}

在我的LoadData()方法中,我在TextWriter上尝试了很多配置,根据我读过的一些文章看起来应该有帮助......

StringWriter sw             = new StringWriter();
writer                      = new JsonTextWriter(sw);
writer.DateFormatHandling   = DateFormatHandling.IsoDateFormat;
writer.CloseOutput          = true;

分配&#34; IsoDateFormat&#34;到&#34; DateFormatHanding&#34;对作家的设定没有产生任何影响。我也试过&#34; MicrosoftDateFormat&#34;并且数据仍然存储为字符串,但格式不同。

实际问题

这就是所有的设置......问题是&#34;如何根据日期搜索MongoDB文档&#34;?

使用MongoDB的C#驱动程序允许我使用Linq进行搜索。这是我正在使用的linq查询。

IMongoCollection<Session> collection = database.GetCollection<Session>("sessions");
DateTime startDate  = (this.StartDate.HasValue) ? this.StartDate.Value : DateTime.Now.AddDays(-7);
DateTime endDate    = (this.EndDate.HasValue) ? this.EndDate.Value : DateTime.Now;
var data            = collection.Find<Session>(e => e.StartTimestamp.Value >= startDate && e.StartTimestamp.Value <= endDate).ToList();

由于JSON直接映射回Session POCO,我应该可以使用该类型(?)。我可以成功过滤Session POCO中的其他字段。它只是让我适应的日期。

我猜测我的实现中存在某些不妥或疏忽,或者由于数据存储为字符串,因此无法将其作为日期(?)进行比较。

非常感谢任何见解。

谢谢, -G

2 个答案:

答案 0 :(得分:2)

没有多少人看过这篇文章,所以也许我对它的评价很差,但对于那个赞成它的人来说,我确实来到了我认为更像是&#34;工作周期&#34;而不是一个实际的解决方案,但也许它也会帮助你。

取代或除了以典型的字符串格式保存日期(&#34; yyy / MM / dd HH:mm:ss&#34;),我将日期保存为Int64的即可。解析器/编写器将这些识别为数字并将其存储为数字。当然,数字很容易排序和排序。

所以原帖的 Session 模型现在看起来像这样......

//
// POCO to store in MongoDB
public class Session
{

    public DateTime? StartUTCTimestamp { get; set; }

    public DateTime? StartTimestamp { get; set; }

    public DateTime? EndTimestamp { get; set; }


    //
    // If the StartUTCDate is defined then return the number of "ticks" in the date
    [BsonElement("StartUTCTimestampTicks")]
    public Int64? StartUTCTimestampTicks 
    { 
        get 
        { 
            return (this.StartUTCTimestamp.HasValue) ?
                    (Int64?)this.StartUTCTimestamp.Value.Ticks : 
                    null; 
        } 
    }

    //
    // If the StartDate is defined then return the number of "ticks" in the date
    [BsonElement("StartTimestampTicks")]
    public Int64? StartTimestampTicks
    { 
        get 
        { 
            return (this.StartTimestamp.HasValue) ?
                    (Int64?)this.StartTimestamp.Value.Ticks : 
                    null; 
        } 
    }

    //
    // If the EndDate is defined then return the number of "ticks" in the date
    [BsonElement("EndTimestampTicks")]
    public Int64? EndTimestampTicks
    { 
        get 
        { 
            return (this.EndTimestamp.HasValue) ?
                    (Int64?)this.EndTimestamp.Value.Ticks : 
                    null; 
        } 
    }

    public void ToJSON(ref JsonTextWriter writer)
    {
        Session session = this;            

        writer.WriteStartObject(); // {


        if (session.StartUTCTimestamp.HasValue)
        {
            writer.WritePropertyName("StartUTCTimestamp");
            writer.WriteValue(session.StartUTCTimestamp);

            writer.WritePropertyName("StartUTCTimestampTicks");
            writer.WriteValue(session.StartUTCTimestampTicks);
        }

        if (session.StartTimestamp.HasValue)
        {
            writer.WritePropertyName("StartTimestamp");
            writer.WriteValue(session.StartTimestamp);

            writer.WritePropertyName("StartTimestampTicks");
            writer.WriteValue(session.StartTimestampTicks);
        }

        if (session.EndTimestamp.HasValue)
        {
            writer.WritePropertyName("EndTimestamp");
            writer.WriteValue(session.EndTimestamp);

            writer.WritePropertyName("EndTimestampTicks");
            writer.WriteValue(session.EndTimestampTicks);
        }


        writer.WriteEndObject(); // }
    }
}

如果已定义,我已添加了显示每个日期中刻度数的属性。如果不是,他们只返回 null 。我必须使用 BsonElement 属性来装饰它们,以使它们能够被JsonTextWriter识别(不确定为什么这些需要它而其他属性没有),否则我收到错误。

我还修改了模型上的ToJSON()方法,以检查在序列化值之前是否提供了日期。如果未定义日期,那么我们不会将json元素添加到文档中。

存储在MongoDB中的结果文档现在看起来像......

{
    "_id" : ObjectId("572b4486d9016b151846b8ed"),
    "StartUTCTimestamp" : "2016-04-24T17:02:12",
    "StartUTCTimestampTicks" : NumberLong(635971141320000000),
    "StartTimestamp" : "2016-04-24T13:02:12",
    "StartTimestampTicks" : NumberLong(635970997320000000),
    "EndTimestamp" : "2016-04-24T13:05:16",
    "EndTimestampTicks" : NumberLong(635970999160000000)
}

为什么这些值存储在&#34; NumberLong()&#34;数据类型函数和日期不存储在&#34; ISODate()&#34;函数对我来说是一个谜,但它现在允许我根据Ticks而不是日期字符串查询日期和日期范围。

IMongoCollection<Session> collection = database.GetCollection<Session>("sessions");
DateTime startDate  = (this.StartDate.HasValue) ? this.StartDate.Value : DateTime.Now.AddDays(-7);
DateTime endDate    = (this.EndDate.HasValue) ? this.EndDate.Value : DateTime.Now;
var data            = collection.Find<Session>(e => e.StartTimestampTicks.Value >= startDate.Ticks && e.StartTimestampTicks.Value <= endDate.Ticks).ToList();

这实际上让我考虑不保存日期字符串。我将它们更多地保留为标签,因为它们在查看实际的JsonDocument时更具可读性。但是,只要通过用户界面查看文档的主要用途,将Ticks转换为更易读的日期字符串表示是一件微不足道的事情。

无论如何......我希望这有帮助...有人...... :)

答案 1 :(得分:2)

必须使用BsonValue.Create(yourDate)创建过滤器表达式中的日期;

示例:

   IMongoDatabase db = GetMongoDbConnection();
   IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument> ("yourCollectionName");
   DateTime date = BsonValue.Create(DateTime.Now.Date);
   var filter = Builders<BsonDocument>.Filter.Gt("dateFieldToFilterOn", date);
   List<BsonDocument> bsonDocuments = await collection.Find(filter).ToListAsync();