Jamez
Jamez

Reputation: 1597

RavenDb querying simple map transform index

I've been struggling with this for a while now.

I've got a client object (clients/513) that looks like this:

{  
  "Risks": [
    {
      "Id": "421eacf0-14e9-4004-ab0b-95d20e976aac",
      "RiskFactor": "ElectricalEquipment",
      "Description": "Should be allowed to play with electronics."
    },
    {
      "Id": "4bbecbe2-acfc-45c3-b87a-3321e1eca95a",
      "RiskFactor": "ViolenceToStaffVerbal",
      "Description": "Tourettes"
    }
}

I've created an index that has the following code:

Map

from c in docs.Clients
from r in c.Risks
select new { ClientId = c.Id, RiskId = r.Id }

Transform

from c in results
from r in c.Risks
select new { ClientId = c.Id, RiskId = r.Id }

I THINK I understand that a MAP just defines which properties you wish to be able to search on. and that the TRANSFORM, returns the actual data in a certain shape.

I'm wanting to return ClientId, RiskId and maybe some other properties relating to risk so that I can do .As<ViewModel>() , however I seem to be getting some inconsistent results when I execute the query. (It likes to change how many results it returns, depending on how many times I execute the query, sometimes 4, sometimes 5).

Also: filtering by RiskId seems to return one and sometimes More than one Risk, with id's other than the specified Id.

Any help would be much appreciated.

Upvotes: 1

Views: 406

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241450

You don't need a transform. Here's an index that will be much more useful for what you're trying to do:

public class ClientRiskIndex : AbstractIndexCreationTask<Client, ClientRiskResult>
{
    public ClientRiskIndex()
    {
        Map = clients => from c in clients
                         from r in c.Risks
                         select new {
                                        ClientId = c.Id,
                                        RiskId = r.Id,
                                        r.RiskFactor,
                                        r.Description
                                    };

        StoreAllFields(FieldStorage.Yes);
    }
}

This assumes a class structure as follows:

public class Client
{
    public string Id { get; set; }
    public IList<Risk> Risks { get; set; }
}

public class Risk
{
    public Guid Id { get; set; }
    public string RiskFactor { get; set; }
    public string Description { get; set; }
}

public class ClientRiskResult
{
    public string ClientId { get; set; }
    public Guid RiskId { get; set; }
    public string RiskFactor { get; set; }
    public string Description { get; set; }
}

Now when you query, you can do something like:

session.Query<ClientRiskResult, ClientRiskIndex>()
       .Where(x => x.RiskFactor == "ElectricalEquipment")
       .AsProjection<ClientRiskResult>();

And if you wanted full-text search over the risk description, just add this line to the index definition:

Index(x => x.Description, FieldIndexing.Analyzed);

Then you can search like this:

session.Query<ClientRiskResult, ClientRiskIndex>()
       .Search(x => x.Description, "electronics")
       .AsProjection<ClientRiskResult>();

Be aware that by storing data in the index and projecting from it, you are getting "eventually consistent" results. That is probably the source of the results you described. You need to either allow for time for the index to become nonstale, or you need to specifically customize the query to wait.

In the real world, you'll have plenty of time for indexing to catch up. In your testing, simulate this by waiting with .Customize(x=> x.WaitForNonStaleResults()) in your query.

Upvotes: 6

Related Questions