在Mongodb中使用AsQueryable进行过滤 - ExpandoObject。

问题描述 投票:1回答:1

我试图使用AsQueryable和LINQ函数查询mongodb数据库。

我目前的数据库结构是,我有一些集合,主要是一个记录,在C#中是这样定义的。

public class Record  
    {
        [BsonElement("formName")]
        public string FormName { get; set; }

        [BsonElement("created")]
        public DateTime Created { get; set; }

        [BsonElement("createdBy")]
        public string CreatedBy { get; set; }

        [BsonId]
        [BsonIgnoreIfDefault]
        [BsonRepresentation(BsonType.ObjectId)]
        private string InternalId { get; set; }

        [BsonElement("recordId")]
        [Newtonsoft.Json.JsonProperty("recordId")]
        public string Id { get; set; }

        [BsonElement("organisationId")]
        public string OrganisationId { get; set; }

        // TODO: Consider making configurable
        private string appName = "MediLog";

        [BsonElement("appName")]
        public string AppName
        {
            get { return this.appName; }
            set { this.appName = value; }
        }

        [BsonElement("schemaVersion")]
        public int SchemaVersion { get; set; }

        [BsonElement("contents")]
        public ExpandoObject Contents { get; set; }

        [BsonElement("modified")]
        public DateTime Modified { get; set; }

        [BsonElement("modifiedBy")]
        public string ModifiedBy { get; set; }

        [BsonElement("majorVersion")]
        public int MajorVersion { get; private set; }

        [BsonElement("minorVersion")]
        public int MinorVersion { get; private set; }

        [BsonElement("version")]
        public string Version
        {
            get
            {
                return (MajorVersion + "." + MinorVersion);
            }

            set
            {
                MajorVersion = Convert.ToInt32(value?.Substring(0, value.IndexOf(".", StringComparison.OrdinalIgnoreCase)), CultureInfo.InvariantCulture);
                MinorVersion = Convert.ToInt32(value?.Substring(value.IndexOf(".", StringComparison.OrdinalIgnoreCase) + 1), CultureInfo.InvariantCulture);
            }
        }

    }

和你一样,数据主要存储在 Contents 这是 ExpandoObject 这就是我的问题所在。

我正试图做这样的事情。

 var collection =
                database.GetCollection<Record>("recordData").AsQueryable()
                    .Where(x =>  x.Contents.SingleOrDefault(z => z.Key == "dateOfBirth").Value.ToString().Length > 0)
                        .ToList();  

但我得到了这个异常:

System.InvalidOperationException HResult=0x80131509 Message={document}{contents}.SingleOrDefault(z => (z.Key == "dateOfBirth")).Value.ToString().Length is not supported.

另外,当我试了一下。

  var collection1 = database.GetCollection<Record>("recordData").AsQueryable()
                .Where(x => (DateTime)(x.Contents.SingleOrDefault(z => z.Key == "dateOfBirth").Value)  > DateTime.Now)
                .ToList();

我得到了这个异常。

System.InvalidOperationException
  HResult=0x80131509
  Message=Convert({document}{contents}.SingleOrDefault(z => (z.Key == "dateOfBirth")).Value) is not supported.

另外,当我这样做的时候,

var collection =
database.GetCollection(“recordData”).AsQueryable()
.Where(x => (DateTime)x.Contents.First(z => z.Key == “dateOfBirth”).Value > DateTime.Now ).ToList();

我得到了这个异常。

System.NotSupportedException: ‘The expression tree is not supported: {document}{contents}’

所以我的问题是,我怎样才能在一个ExpandoObject类型的对象上使用AsQueryable和LINQ进行查询?

谢谢!我正在尝试查询mong的数据,我的问题是如何使用AsQueryable和LINQ对ExpandoObject类型的对象进行查询。

c# mongodb linq mongodb-.net-driver
1个回答
1
投票

无论你想传递到 .Where() 方法是一个表达式树,需要翻译成MongoDB查询语言。C#编译器将接受 ExpandoObject的方法,如 SingleOrDefault (扩展方法),但当这种表达式树需要翻译成Mongo查询时,这在运行时将失败。

没有什么特别之处 ExpandoObject 当你使用MongoDB时。它必须以某种方式被翻译成BSON文档(MongoDB格式),例如下面的代码。

dynamic o = new ExpandoObject();
o.dateOfBith = new DateTime(2000, 1, 1);
r.Contents = o;
col.InsertOne(r);

插入 ExpandoObject 一样 BsonDocumentDictionary<string, T> 所以,它仅仅成为。

{ "_id" : ObjectId(",,,"), "contents" : { "dateOfBith" : ISODate("2000-01-01T07:00:00Z") } }

在你的数据库中。

知道了你可以使用 点阵法. 还有一个 StringFieldDefinition 类可以利用,因为你不能从 ExpandoObject 的方式。

var fieldDef = new StringFieldDefinition<Record, DateTime>("contents.dateOfBith");
var filter = Builders<Record>.Filter.Gt(fieldDef, new DateTime(2000, 1, 1));

var res = col.Find(filter).ToList();

1
投票

这种表达式的转换在LINQ中并不好用。如果你把字段定义为字符串,它的效果会更好。比你可以 Inject 你的 Where 挽留 IQueryable 如果你喜欢>

更新(你可以有多个条件,以及不同的空检查)

string connectionString = "mongodb://localhost:27017";
var client = new MongoClient(connectionString);
var db = client.GetDatabase("test");
var records = db.GetCollection<Record>("recordData");
//dynamic content = new ExpandoObject();
//content.dateOfBirth = DateTime.Today;
//records.InsertOne(new Record
//{
//    AppName = "my app", Created = DateTime.Today, CreatedBy = "some user", FormName = "form name",
//    OrganisationId = "organization id", SchemaVersion = 1, Version = "1.1", Contents = content
//});
// first option for multiple conditions:
var filter =
    Builders<Record>.Filter.Lt("contents.dateOfBirth", DateTime.Now) &
    Builders<Record>.Filter.Gt("contents.dateOfBirth", DateTime.Now.AddDays(-10)) &
    // checking if the field is there. Whether the value is null or not.
    Builders<Record>.Filter.Exists("contents.dateOfBirth") &
    // checking if the field is there, and it's not null
    Builders<Record>.Filter.Ne("contents.dateOfBirth", BsonNull.Value);
// second option for multiple conditions
var dateOfBirths = records.AsQueryable().Where(_ => filter.Inject()).Where(x => x.AppName == "my app").ToList();

从那里,你可以继续你的 IQueryable 语法。

更新2

如果是 .Where(x => x.AppName.Contains("app")) 你会得到

"pipeline" : [
        {
            "$match" : {
                "contents.dateOfBirth" : {
                    "$exists" : true,
                    "$gt" : ISODate("2020-05-15T12:48:14.483+02:00"),
                    "$lt" : ISODate("2020-05-25T12:48:14.480+02:00"),
                    "$ne" : null
                }
            }
        },
        {
            "$match" : {
                "appName" : /app/g
            }
        }
    ]

从剖析数据库。所以 <string>.Contains(<string>) 落实在 IQueryable 作为不在内存中的正则表达式过滤器。

© www.soinside.com 2019 - 2024. All rights reserved.