Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filtering by Clr-Type on a multi-map index in RavenDb

I'm storing objects in RavenDb that all inherit from a base class and I am using a Multi-Map index to search across all subtypes like this:

public class BaseRequest
{
    public Guid Id { get; set; }
    public string RequestName { get; set; }
    public DateTime RequiredDate { get; set; }
}

public class RequestA: BaseRequest
{
    public string SomethingForA { get; set; }
}

public class RequestB : BaseRequest
{
    public int SomethingForB { get; set; }
}

public class AllRequests: AbstractMultiMapIndexCreationTask
{
    public AllRequests()
    {
        AddMap<RequestA>(requests => from req in requests select new {req.RequestName, req.RequiredDate});
        AddMap<RequestB>(requests => from req in requests select new {req.RequestName, req.RequiredDate });
    }   
}

And I search it like this:

    public BaseRequest[] Search(string requestType, string requestName, DateTime? requestDate = null) {
        var q = RavenSession.Query<BaseRequest, AllRequests>();

        if (!string.IsNullOrEmpty(requestName)) {
            q = (IRavenQueryable<BaseRequest>)Queryable.Where(q, x => x.RequestName == requestName);
        }
        if (requestDate.HasValue) {
            q = (IRavenQueryable<BaseRequest>) Queryable.Where(q, x => x.RequiredDate == requestDate);
        }

        return q.ToArray();
    }

What I would like to do is further filter the results by the CLR-Type of the request. Is it possible to do this with the "Raven-Entity-Name" metadata field, and if so how?

I know I could just use Query<RequestA>() etc but it is likely that I will need to filter by multiple types at a later date and these types will be dynamically chosen by the user.

Note: What I really want to do is use the index to pull back Requests given a certain RequestName, but only return the RequestA's (and later only RequestA, RequestC and RequestDs). I don't want to do multiple queries to get back each type individually, I want to do the one query that queries by the parameters and sometimes type.

The system is being designed so that Requests types will be getting added every month or so and we will probably end up with a couple of hundred different types. I'd like to dynamically search for a set of specific types with given parameters.

like image 626
Russell Troywest Avatar asked Oct 23 '22 22:10

Russell Troywest


1 Answers

Worked it out. I needed to use MetadataFor(req) to add the clr type to the index and then use a specific map type to query with. Like this:

public class AllRequests: AbstractMultiMapIndexCreationTask
{
    public class SearchMap {
      public string RequestName{get;set;}
      public DateTime RequiredDate{get;set;}
      public string RequestType{get;set;}
    }

   public AllRequests(){
        AddMap<RequestA>(requests => from req in requests select new {req.RequestName, req.RequiredDate, RequestType = MetadataFor(req)["Raven-Clr-Type"]});
        AddMap<RequestB>(requests => from req in requests select new {req.RequestName, req.RequiredDate, RequestType = MetadataFor(req)["Raven-Clr-Type"] });
    }   
}

I can then query against actual fields of the Request, and also by type like this:

   public BaseRequest[] Search(string requestType, string requestName, DateTime? requestDate = null) {
        var q = RavenSession.Query<AllRequests.SearchMap, AllRequests>();

        if (!string.IsNullOrEmpty(requestName)) {
            q = (IRavenQueryable<BaseRequest>)Queryable.Where(q, x => x.RequestName == requestName);
        }
        if (requestDate.HasValue) {
            q = (IRavenQueryable<BaseRequest>) Queryable.Where(q, x => x.RequiredDate == requestDate);
        }

        if (!string.IsNullOrEmpty(requestType))
        {
            q =
                q.Where(
                    x =>
                    x.RequestType == GetClrTypeFromPassedInValue(requestType));
        }
        return q.As<BaseRequest>().ToArray();
    }
like image 60
Russell Troywest Avatar answered Dec 21 '22 16:12

Russell Troywest