在属性上按_t过滤集合

时间:2017-09-19 14:43:30

标签: mongodb mongodb-.net-driver

我已经使用mongo / c#实现了一个场景,我在同一个集合中存储了不同类型的类。一切都很好,我最终可以做到这一点:

var collection = _mongoDb.GetCollection<TLogType>(CollectionNames.Logs).OfType<TLogType>();

现在我需要类似的功能但属性。基本上,我有一系列联系人的集合(正常的,不是多态的)。我希望数组中的每个项目都是特定的联系类型。我提出的文件看起来像这样(为了简洁起见较小):

Contacts" : [ 
    {
        "_t" : [ 
            "Contact", 
            "ExternalContact"
        ],
        "Role" : "Product Emergency",
        "Name" : "Fred",
        "Email" : "fred@gmail",
        "Phone" : "1231",
        "Availability" : "",
    }, 
    {
        "_t" : [ 
            "Contact", 
            "InternalContact"
        ],
        "Role" : "Manager",
        "Name" : "Mickey"
    }
]

我现在想要的是按类型检索联系人。我们有OfType<TResult>但它只适用于MongoQueryable。所以,像这样:

public async Task<IEnumerable<string>> GetContacts<TContactType>(CancellationToken token) where TContactType : Contact
{

    var collection = _mongoDb.GetCollection<Person>(CollectionNames.Person)();

    var projectedListOfContacts =
        await collection.Find(
            s => s.Contacts != null && 
            s.Contacts.OfType<TContactType> // how to filter by type on a property?
        )
            .Project(dto => dto.Contacts).
            .ToListAsync(token);

    ...
} 

无需获取所有联系人并在内存中使用它们的任何方式吗?

相关课程是:

public class Person
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<Contact> Contacts {get; set;}
}

public abstract class Contact : IContact
{
    public string Role { get; private set; }
    public string Name { get; private set; }

    protected Contact(string role, string name)
    {
        Role = role ?? throw new ArgumentNullException(nameof(role));
        Name = name ?? throw new ArgumentNullException(nameof(name));
    }
}

public class ExternalContact : Contact
{

    public string Email { get; private set; }   
    public string Phone { get; private set; }   
    public string Availability { get; private set; }

    public ExternalContact(string role, string name, string email, string phone, string availability)
        : base(role, name)
    {
        Email = email ?? throw new ArgumentNullException(nameof(email));
        Phone = phone ?? throw new ArgumentNullException(nameof(phone));
        Availability = availability;
    }
}

更新:答案帮我解决了..所以最后我可以这样做:

var contacts =
await Collection.Find(s => s.Contacts != null)
    .Project(dto => dto.Contacts.OfType<TContactType>())
    .ToListAsync(token);

原来,要求已经改变..并且Contact数组将始终至少有一种类型。所以没有理由过滤..但我利用了只返回我想要的类型的投影。

1 个答案:

答案 0 :(得分:0)

这里只是疯狂拍摄,但我相信这可能就是你要找的东西,但如果没有mcve,很难确定。

        var nullFilter = Builders<Person<TContactType>>.Filter.Ne(person => person.Contacts, null);
        var typeFilter = Builders<Person<TContactType>>.Filter.OfType<Contact, TContactType>(person => person.Contacts);
        var combinedFilter = Builders<Person<TContactType>>.Filter.And(nullFilter, typeFilter);

        var projectedListOfContact = collection.Find(combinedFilter).Project(dto => dto.Contacts).ToListAsync(token);      

更新构建器

var query = Builders<Person>.Filter.ElemMatch(x => x.Contacts, Builders<Contact>.Filter.OfType<ExternalContact>());
collection.Find(query);

当然也可以与您的通用T一起使用。

这应该返回find({ "Contacts" : { "$elemMatch" : { "_t" : "ExternalContact" } } })