Thanks a lot Michael. I really appreciate your comments!
I've implemented a dummy SubQueryTreeVisitor:
internal class SubQueryTreeVisitor : RelinqExpressionVisitor
{
protected override Expression VisitSubQuery(SubQueryExpression expression)
{
return base.VisitSubQuery(expression);
}
}
I've set a breakpoint inside
VisitSubQuery, and it's reached. Perfect!
this.backend.DigitalInputs.Where(di => di.Properties.Any(p => p.Key.Equals("p1") && p.Value.StartsWith(string.Empty)) && di.Matter.EndsWith(string.Empty));
Transformation process is fairly straightforward. It needs to change any
MemberExpression by a new
DSLFieldExpression (this is already implemented on QuerySourceReferencePropertyTransformingTreeVisitor class).
Example:
di.Matter.EndsWith(string.Empty) -> [MemberExpression ([di].Matter)] is translated as [DSLFieldExpression (name: "matter", type: field)].EndsWith(string.Empty)
There is a little trouble: As far as you could figure out seeing the Linq sentence, di.Properties might seem a relational ship between di entities and its Properties entities.
However, it's not quite true.
In C#, di.Properties is a list of Property object. Each Property object has a Key and a Value.
It's like this due to my WebAPI backend is schemaless (ElasticSearch), so I send each schema column as a Property(Key, Value).
So, each di has a list of Property with Key and Value.
My DSL is using WebAPI concepts. So, there is no a di entity related with several property entities.
A sample of last Linq sentence translated to my DSL:
search digitalinputs query property.p1 eq "" and field.matter "+"
So, I would need to translate Any() expression by a DSLFieldExpression (name: "p1", type: property).
When VisitSubQuery is reached, SubQueryExpression expression.QueryModel parameter is:
from IndexableProperty p in [di].Properties where [p].Key.Equals("p1") && [p].Value.StartsWith(string.Empty) select [p] => Any()
As far I'm able to figure out, I'd need to pick a VisitMember() up for [di].Properties and:
- Analyze [p] is from "Properties" property.
- Translate it to a DSLExpression (name: ?, type: property).
- Analyze [p].Key and fill expression up with (name: "p1", type: property).
I've already commented before a QuerySourceReferencePropertyTransformingTreeVisitor visitor exist in order to change MemberExpression by a DSLFieldExpression. How to perform it again on the subquery?
Code (QuerySourceReferencePropertyTransformingTreeVisitor.cs):
/// <summary>
/// Replaces MemberExpression instances like [QuerySourceReferenceExpression].PropertyName with <c ref="LuceneQueryFieldExpression"/>
/// </summary>
internal class QuerySourceReferencePropertyTransformingTreeVisitor : RelinqExpressionVisitor
{
private MemberExpression parent;
private DSLFieldExpression queryField;
protected override Expression VisitMember(MemberExpression expression)
{
this.parent = expression;
var result = base.VisitMember(expression);
return this.queryField ?? result;
}
protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpression expression)
{
var propertyInfo = parent.Member as PropertyInfo;
var propertyType = propertyInfo.PropertyType;
if (propertyType.IsEnum)
{
propertyType = Enum.GetUnderlyingType(propertyType);
}
queryField = new LQLFieldExpression(null, null, propertyInfo.Name, propertyType);
return base.VisitQuerySourceReference(expression);
}
}