Saturday, August 18, 2012

RavenDB Awesome Feature of the Day, Formatted Indexes

RavenDB Awesome Feature of the Day, Formatted Indexes:
There is a chance that you’ll look at me strangely for calling this the “Feature of the Day”. But that is actually quite a important little feature.
Here is the deal, let us say that you have the following index:

public class Orders_Search : AbstractIndexCreationTask<Order, Orders_Search.ReduceResult>
{
public class ReduceResult
{
public string Query { get; set; }
public DateTime LastPaymentDate { get; set; }
}

public Orders_Search()
{
Map = orders => from order in orders
let lastPayment = order.Payments.LastOrDefault()
select new
{
Query = new object[]
{
order.FirstName,
order.LastName,
order.OrderNumber,
order.Email,
order.Email.Split('@'),
order.CompanyName,
order.Payments.Select(payment => payment.PaymentIdentifier),
order.LicenseIds
},
LastPaymentDate = lastPayment == null ? order.OrderedAt : lastPayment.At
};
}
}





And you are quite happy with it. But that is the client side perspective. We don’t have any types on the server, so you can’t just execute this there. Instead, we send a string representing the index to the server. That string is actually the output of the linq expression, which looks like this:

image

This is… somewhat hard to read, I think you’ll agree. So we had some minimal work done to improve this, and right now what you’ll get is (you’ll likely see it roll off the screen, that is expected):

docs.Orders
.Select(order => new {order = order, lastPayment = order.Payments.LastOrDefault()})
.Select(__h__TransparentIdentifier0 => new {Query = new System.Object []{__h__TransparentIdentifier0.order.FirstName, __h__TransparentIdentifier0.order.LastName, __h__TransparentIdentifier0.order.OrderNumber, __h__TransparentIdentifier0.order.Email, __h__TransparentIdentifier0.order.Email.Split(new System.Char []{'@'}), __h__TransparentIdentifier0.order.CompanyName, __h__TransparentIdentifier0.order.Payments
.Select(payment => payment.PaymentIdentifier), __h__TransparentIdentifier0.order.LicenseIds}, LastPaymentDate = __h__TransparentIdentifier0.lastPayment == null ? __h__TransparentIdentifier0.order.OrderedAt : __h__TransparentIdentifier0.lastPayment.At})



This is still quite confusing, actually. But still better than the alternative.
As I said, it seems like a little thing, but those things are important. An index in its compiled form that is hard to understand for a user is a support issue for us. We needed to resolve this issue.
The problem is that source code beautifying is non trivial. I started playing with parsers a bit, but it was all way too complex. Then I had an affiany. I didn’t actually care about the code, I just wanted it sorted. There aren’t many C# code beautifiers around, but there are a lot for JavaScript.
I started with the code from http://jsbeautifier.org/, which Rekna Anker had already ported to C#. From there, it was an issue of making sure that for my purposes, the code generated the right output. I had to teach it C# idioms such as @foo, null coalescent and lambda expressions, but that sounds harder than it actually was. With that done, we go this output:

docs.Orders.Select(order => new {
order = order,
lastPayment = order.Payments.LastOrDefault()
}).Select(__h__TransparentIdentifier0 => new {
Query = new System.Object[] {
__h__TransparentIdentifier0.order.FirstName,
__h__TransparentIdentifier0.order.LastName,
__h__TransparentIdentifier0.order.OrderNumber,
__h__TransparentIdentifier0.order.Email,
__h__TransparentIdentifier0.order.Email.Split(new System.Char[] {
'@'
}),
__h__TransparentIdentifier0.order.CompanyName,
__h__TransparentIdentifier0.order.Payments.Select(payment => payment.PaymentIdentifier),
__h__TransparentIdentifier0.order.LicenseIds
},
LastPaymentDate = __h__TransparentIdentifier0.lastPayment == null ? __h__TransparentIdentifier0.order.OrderedAt : __h__TransparentIdentifier0.lastPayment.At
})



And this is actually much better. Still not good enough, mind. we can do better than that. It is a simple change:

docs.Orders.Select(order => new {
order = order,
lastPayment = order.Payments.LastOrDefault()
}).Select(this0 => new {
Query = new System.Object[] {
this0.order.FirstName,
this0.order.LastName,
this0.order.OrderNumber,
this0.order.Email,
this0.order.Email.Split(new System.Char[] {
'@'
}),
this0.order.CompanyName,
this0.order.Payments.Select(payment => payment.PaymentIdentifier),
this0.order.LicenseIds
},
LastPaymentDate = this0.lastPayment == null ? this0.order.OrderedAt : this0.lastPayment.At
})



And now we got to something far more readable Smile.


DIGITAL JUICE

No comments:

Post a Comment

Thank's!