Dark Hippo
Dark Hippo

Reputation: 1265

Unable to compare date in MongoDB

I'm very new to MongoDB, but have what I believed to be a very simple query.

I have a Protein object that inherits from IProtein (yes, my naming sucks)

public interface IProtein
{
    int Count { get; set; }
    DateTime Date { get; set; }
}

I want to return the FirstOrDefault from the collection based on a date comparison of the Date field of the protein object and Today

    public IProtein GetProteinForDay(DateTime day)
    {
        var collection = _db.GetCollection<IProtein>(DB_COLLECTION);

        var query = collection.AsQueryable<IProtein>()
                              .Where(p => p.Date == day.Date);

        var protein = query.FirstOrDefault();
        return protein;
    }

Unfortunately, I've gone through so many different variations of trying to match dates using MongoDB (some using Linq, some not) that I've completely lost focus on how far I got with each one.

This is my current code, it returns the error Unable to determine the serialization information for the expression: p.Date

What is wrong with my query (yes, it probably is something very simple) and how do I actually compare dates with a MongoDB / Linq query?

Upvotes: 0

Views: 2990

Answers (1)

Saleem
Saleem

Reputation: 8978

Well, It's disappointing that .net DateTime doesn't work seamlessly with MongoDB driver. I believe support should be baked into driver.

Anyway you'll need to take couple of steps to make .net and MongoDB work together.

1) Decorate Date field in your interface with BsonDateTimeOptions attribute to tell MongoDB driver how to serialize .net DateTime. See BsonDateTimeOptions Documentation

Interface should looks like

public interface IProtein
{
    int Count { get; set; }

    [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
    DateTime Date { get; set; }
}

2) In your GetProteinForDay function, replace

var collection = _db.GetCollection<IProtein>(DB_COLLECTION);
var query = collection.AsQueryable<IProtein>()
                              .Where(p => p.Date == day.Date);

with

var collection = db.GetCollection<Protein>(DB_COLLECTION);
var query = collection.Find(p => p.Date == day); 

Notice that, I have replaced interface IProtein with concrete implementation of interface, in my case Protein.

Update: Full program is attached as reference.

Source document:

{
  _id: ObjectID('5964ebf315c46ab80b2c20f3),
  Count: 10,
  Date: '2017-07-11 00:00:00.000'
}

Test Program:

using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;

namespace mongoT
{
    public interface IProtein
    {
        ObjectId Id { get; set; }
        int Count { get; set; }

        [BsonDateTimeOptions(Kind = DateTimeKind.Local)]
        DateTime Date { get; set; }
    }

    public class Protein : IProtein
    {
        public ObjectId Id { get; set; }
        public int Count { get; set; }
        public DateTime Date { get; set; }

        public override string ToString()
        {
            return $"{nameof(Id)}: {Id}, {nameof(Count)}: {Count}, {nameof(Date)}: {Date}";
        }
    }

    class Program
    {
        private static string DB = "ProteinsDB";
        private static string COLLECTION = "Proteins";

        static void Main(string[] args)
        {
            var result = GetProteinForDay(DateTime.Now.Date);
            Console.WriteLine(result);
        }

        public static IProtein GetProteinForDay(DateTime day)
        {
            var client = new MongoClient();
            var db = client.GetDatabase(DB);

            var collection = db.GetCollection<Protein>(COLLECTION);
            var query = collection.Find(p => p.Date == day.Date);

            var protein = query.FirstOrDefault();
            return protein;
        }
    }
}

Upvotes: 4

Related Questions