为不同类型的嵌套文档创建动态构建器过滤器 - MongoDB C#Driver

时间:2017-10-28 14:58:44

标签: c# mongodb mongodb-query filtering

我有一个名为some_collection的集合。 some_collection的架构是这样的:(架构由C#DTO决定)

{
   _id: ObjectId(.....),
   firstName: "fName",
   lastName: "lName",
   someType: 4, 
   innerObject: {
      // see below
   }
}

在我的C#代码中,innerObject是一个抽象类,并且有多个子类。这些子类具有变体属性,因此MongoDB集合中的文档并不完全相同。它们所属的子类的类型由单个someType文档中的some_collection字段划分。因此,audit_collection中包含2种不同类型嵌套文档的2个文档的示例:

   {
       _id: ObjectId('first'),
       firstName: "Jane",
       lastName: "Smith",
       someType: 0, 
       innerObject: {
          prop1: "foo",
          prop2: "bar",
          aCollectionOfStrings: ["a", "b", "c"] // this is what I wanna search
       }
    },
{
   _id: ObjectId('second'),
   firstName: "John",
   lastName: "Doe",
   someType: 3, 
   innerObject: {
      prop1: "baz",
      prop2: "foobarbaz",
      aCollectionOfObjects: [
          {
            myProp: "hello", // this is what I want to search
            irrelevantProp: "blah" 
          }, 
          {
            myProp: "hello5", // this is what I want to search
            irrelevantProp: "blah" 
          },
          {
            myProp: "hello1", // this is what I want to search
            irrelevantProp: "blah" 
          }
      ] 
   }
}

这个问题的用例是我想搜索用户提供的字符串,这可以存在于firstnamelastname属性中(位于顶层文档,以及所有文档共享它,这么容易),以及对象的一些内部属性(因为嵌套的内部文档在模式中是不同的,所以更难做到)。例如:

对于someType == 0,我会搜索myDocument.innerObject.aCollectionOfStrings,而使用someType == 3时,我会搜索每个 myDocument.innerObject.aCollectionOfObjects的{​​{1}属性。

在我的C#代码中,如果我拉完整个集合,然后对它使用LINQ操作,我有一个C#函数来确定如何来搜索整个文档(基本上它会检查值myProp,然后基于此,知道要搜索的属性)及其嵌套文档,并且可以在C#代码中进行过滤。

然而,在重构使用Builders Filters之后,我无法将该C#过滤器函数传递给Filter(显然,因为所有Builder正在构建一个MongoDB查询,我认为):

someType

filter = filter & Builders<MyOwnType>.Filter.Eq(a => CheckIfObjectHasString(a, search), true);类似于:

CheckIfObjectHasString

我想到的一个解决方案是在文档插入期间,在最顶层的private bool CheckIfObjectHasString(MyOwnType doc, string search) { if(doc.someType == 0) { return doc.innerObject.aCollectionOfStrings.Where(s => s.ToLower().Contains(search)).Any(); } else if(doc.someType == 3) { return doc.innerObject.aCollectionOfObjects.Where(d => d.myProp.ToLower().Contains(search)).Any(); } else if(...) { // etc. } } 文档上创建一个属性,它具有所有可搜索的材料,但这似乎是不洁净的。如何构建一个类似上面的过滤器,而无需在LINQ或我刚才提到的解决方案中进行处理?

1 个答案:

答案 0 :(得分:0)

我最终做的是将原始numpy.nan == numpy.nan与我要查找的内容相比较BsonDocument,并将$or嵌入到$regex中他们各自的领域。我不知道在MongoDB中搜索文档而不使用应用程序代码时,您的查询是一个要比较的文档。

BsonDocuments

然后,即使它是private BsonDocument FindSearch(string searchString) { // escape the searchString var filterDocument = new BsonDocument(); var searchDocument = new BsonDocument(); searchDocument.Add(("$regex", $".*{searchString}.*")); searchDocument.Add("$options", "i"); // to ignore case when performing the regex var searchCriterias = new BsonArray(); // an array because I'll be $or-ing it searchCriterias.Add(new BsonDocument("innerObject.aCollectionOfObjects.myProp", textSearch)); // access each nested object's property searchCriterias.Add(new BsonDocument("innerObject.aCollectionOfStrings", textSearch)); // even though it's an array of strings, I can just search without having to iterate them filterDocument.Add("$or", searchCriterias); // $or-ing it also saved me from having to check the existence of properties, before doing the search } ,我也可以将其用于已存在的BsonDocument Builders,例如:

Filter