【问题标题】:How can I filter child collection comparing datetime property with another one plus timespan value?如何过滤子集合,将 datetime 属性与另一个一加时间跨度值进行比较?
【发布时间】:2019-12-16 07:24:27
【问题描述】:

例如这个

var items = await database.GetCollection<CollectionItem>("collection").AsQueryable()
    .Where(r => r.SubCollection.Any(i =>
        i.DateTimeProp < i.OtherDateTimeProp + TimeSpan.FromMinutes(59))
    .ToListAsync();

给我 mongo 驱动程序错误,不支持的过滤器:

({}{DateTimeProp}

有没有办法做到这一点?谢谢!

【问题讨论】:

  • 您必须使用$expr 运算符来比较同一文档中的两个字段。驱动程序不支持它afaik。请参阅下面的答案以了解另一种方法。

标签: c# .net mongodb mongodb-query mongodb-.net-driver


【解决方案1】:

mongo 驱动程序中的日期时间范围支持很弱。我使用自定义类将ticksisodate 存储在mongodb 的单独属性中,因此我可以像这样进行范围查询:

  var result = collection.AsQueryable()
                         .SelectMany(t =>
                             t.SubCollection,
                            (t, s) => new
                             {
                                isMatch = s.DateTimeProp.Ticks < s.OtherDateTimeProp.Ticks + TimeSpan.FromMinutes(59).Ticks,
                                t.ID
                              })
                         .Where(x => x.isMatch)
                         .Select(x => x.ID)
                         .ToList();

自定义日期类:

    public class Date
    {
        private long ticks = 0;
        private DateTime date = new DateTime();

        public long Ticks
        {
            get => ticks;
            set { date = new DateTime(value); ticks = value; }
        }

        public DateTime DateTime
        {
            get => date;
            set { date = value; ticks = value.Ticks; }
        }

        public static implicit operator Date(DateTime dt)
        {
            return new Date { DateTime = dt };
        }

        public static implicit operator DateTime(Date dt)
        {
            if (dt == null) throw new NullReferenceException("The [Date] instance is Null!");
            return new DateTime(dt.Ticks);
        }
    }

这是一个完整的测试程序:

using MongoDB.Entities;
using MongoDB.Entities.Core;
using System;
using System.Linq;

namespace StackOverflow
{
    public class Test : Entity
    {
        public string Name { get; set; }
        public SubTest[] SubCollection { get; set; }
    }

    public class SubTest
    {
        public Date DateTimeProp { get; set; }
        public Date OtherDateTimeProp { get; set; }
    }

    public class Date
    {
        private long ticks = 0;
        private DateTime date = new DateTime();

        public long Ticks
        {
            get => ticks;
            set { date = new DateTime(value); ticks = value; }
        }

        public DateTime DateTime
        {
            get => date;
            set { date = value; ticks = value.Ticks; }
        }

        public static implicit operator Date(DateTime dt)
        {
            return new Date { DateTime = dt };
        }

        public static implicit operator DateTime(Date dt)
        {
            if (dt == null) throw new NullReferenceException("The [Date] instance is Null!");
            return new DateTime(dt.Ticks);
        }
    }

    public class Program
    {
        private static void Main(string[] args)
        {
            new DB("test", "localhost");

            (new[] {
                new Test{
                    Name = "one",
                    SubCollection = new[]{
                        new SubTest{ OtherDateTimeProp = DateTime.UtcNow, DateTimeProp = DateTime.UtcNow.AddMinutes(50)}, // should match
                        new SubTest{ OtherDateTimeProp = DateTime.UtcNow, DateTimeProp = DateTime.UtcNow.AddMinutes(60)}
                    }
                },
                new Test{
                    Name = "two",
                    SubCollection = new[]{
                        new SubTest{ OtherDateTimeProp = DateTime.UtcNow, DateTimeProp = DateTime.UtcNow.AddMinutes(60)},
                        new SubTest{ OtherDateTimeProp = DateTime.UtcNow, DateTimeProp = DateTime.UtcNow.AddMinutes(60)}
                    }
                }
            }).Save();

            var result = DB.Queryable<Test>() // use collection.AsQueryable() for official driver
                           .SelectMany(t =>
                                t.SubCollection,
                               (t, s) => new
                               {
                                   isMatch = s.DateTimeProp.Ticks < s.OtherDateTimeProp.Ticks + TimeSpan.FromMinutes(59).Ticks,
                                   t.ID,
                                   t.Name
                               })
                           .Where(x => x.isMatch)
                           .Select(x => x.ID)
                           .ToList();
        }
    }
}

【讨论】:

  • 谢谢!似乎是一个有效的答案。尽管我重新安排了解决方案并使架构变得更简单,而无需查询我的子集合。
【解决方案2】:

对于 DateTime,您应该使用 SQL 函数中的方法

https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.sqlserver.sqlfunctions?view=entity-framework-6.2.0

它实际上计算给定时间间隔内的日期之间的差异 这可以像

一样使用
    SqlFunctions.DateDiff("minutes", i.DateTimeProp , i.OtherDateTimeProp) < 59

当然它仍然需要MongoDB-EF Provider的支持,但应该实现。

【讨论】:

  • 谢谢,不过我没用EF,只有mongo官方的.NET驱动。
【解决方案3】:

你可以这样做:

var otherDateTime = new DateTime(2019, 12, 16, 16, 33, 20);
var filter = Builders<RootObject>.Filter.Gt(x => x.DataTimeProp, otherDateTime.AddMinutes(59));
var result = (await context.MyCollection.FindAsync(filter)).ToList();

【讨论】:

  • 我需要过滤子集合
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多