RavenDB Results Transform doesn't like the as keyword

122 views Asked by At

I'm using the following results transformer to try and retrieve less data from a mixed collection of documents. All documents inherit from a base type, but I want to pull back some of the fields from derived types, if present.

Here was my attempt at it (cut down for brevity):

    public DocumentAsSearchResultTransformer()
    {
        TransformResults = docs => from doc in docs
                                    // need several casts to get the properties of the lower cast variables
                                    let subDoc = doc as SubDocumentType
                                    let subDoc2 = doc as SubDocumentType2
                                    select new SearchResult
                                    {
                                        EntityId = doc.EntityId,
                                        Name = doc.Name
                                        // start pulling out document type specific fields
                                        Categories = subDoc != null ? subDocCategories : null,
                                        UserTags = subDoc2 != null ? subDoc2.UserTags : null
                                    };
    }

However, when I try and get this results transformer onto the server, I receive the following exception:

System.ServiceModel.ServiceActivationException: The service '/MyService/MyService.svc' cannot be activated due to an exception during compilation.  The exception message is: Exception has been thrown by the target of an invocation..
    ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
    ---> Raven.Abstractions.Exceptions.BadRequestException: Could not understand query: Could not understand query: 

[DomRegion FileName=, Begin=(3, 18), End=(-1, -1)]: Error - Unexpected symbol As' [DomRegion FileName=, Begin=(6, 27), End=(-1, -1)]: Error - Unexpected symbolAs' [DomRegion FileName=, Begin=(9, 33), End=(-1, -1)]: Error - Unexpected symbol As' ---> System.Net.WebException: The remote server returned an error: (400) Bad Request. at System.Net.HttpWebRequest.GetResponse() at Raven.Client.Connection.HttpJsonRequest.ReadJsonInternal(Func1 getResponse) --- End of inner exception stack trace --- at Raven.Client.Connection.HttpJsonRequest.HandleErrors(WebException e) at Raven.Client.Connection.HttpJsonRequest.ReadJsonInternal(Func1 getResponse) at Raven.Client.Connection.HttpJsonRequest.ReadResponseJson() at Raven.Client.Connection.ServerClient.DirectPutTransformer(String name, OperationMetadata operationMetadata, TransformerDefinition definition) at Raven.Client.Connection.ReplicationInformer.TryOperation[T](Func2 operation, OperationMetadata operationMetadata, OperationMetadata primaryOperationMetadata, Boolean avoidThrowing, T& result, Boolean& wasTimeout) at Raven.Client.Connection.ReplicationInformer.ExecuteWithReplication[T](String method, String primaryUrl, OperationCredentials primaryCredentials, Int32 currentRequest, Int32 currentReadStripingBase, Func2 operation) at Raven.Client.Connection.ServerClient.ExecuteWithReplication[T](String method, Func2 operation) at Raven.Client.Indexes.AbstractTransformerCreationTask.Execute(IDatabaseCommands databaseCommands, DocumentConvention documentConvention) at Raven.Client.Indexes.IndexCreation.CreateIndexes(ExportProvider catalogToGetnIndexingTasksFrom, IDocumentStore documentStore) at MyService.MyService..ctor() in c:\Users\me\repos\Admin\MyService\MyService.svc.cs:line 51 --- End of inner exception stack trace --- // more stack trace (probably not needed)

So, it looks like I'm casting wrong. What's the best way of me getting to these derived class properties? Or am I attempting the impossible?

Any help appreciated.

2

There are 2 answers

0
Craig Brett On BEST ANSWER

I didn't understand what was meant by trying to use AsDocument and my attempts at using it resulted in ugly code that didn't work, so I gave that up and tried to come up with another idea.

It turns out that the <T> in the AbstractTransformerCreationTask doesn't have to align with the document types in the index, it just gives you an easier time of intellisense.

So, to solve this, I made my transformer class use the SearchResult type as the <T>, as well as being the type I returned.

public class DocumentAsSearchResultTransformer : AbstractTransformerCreationTask<SearchResult>
{
    ...
}

And I was then able to assign the properties from the documents normally.

TransformResults = docs => (from doc in docs
    select new SearchResult
    {
        Name = doc.Name,
        Categories = doc.Categories, // this is from a derived document type really, but if the doc doesn't have it it'll be null
        ..
    }

Which works great!

I then ran into the problem though that I didn't want some of the bigger properties coming back, but I wanted to compute some values off of them. Which is thankfully easy to solve. If you include those properties you don't want coming back from your search in your SearchResult model, you can then use them in your transform, even if you don't send them back from the transform.

TransformResults = docs => (from doc in docs
    select new SearchResult
    {
        Name = doc.Name,
        Categories = doc.Categories,
        // pretend that doc.Votes is a collection of large objects that you don't want coming back from a search
        UpvotesCount = doc.Votes != null ? doc.Votes.Count(v => v.Type == "Upvote") : 0
    }

Since you've left the Votes property of your selected SearchResult null, it won't come back from the query. But your computed property will.

Looking at the transform that it makes from this on the server side, it's a pretty neat transform doing it this way, too.

Hope this helps someone else handling a similar situation.

2
Ayende Rahien On

You are trying to use a type that doesn't exists on the server. You can get the dynamic behavior by calling AsDocument(doc) on the instance on the client side.