gabriel giovanini
gabriel giovanini

Reputation: 189

Can't query attachments on RavenDB

I'm trying query documents alongside with the list of attachments on RavenDB.

I have the following object:

   public class Post
    {
        public string Id { get; set; }
        public string Url { get; set; }
        public Profile Profile { get; set; }
        public DateTime CreatedOn { get; set; }
        [JsonProperty("@metadata")]
        public Metadata Metadata { get; set; }
    }

    public class Metadata
    {
        [JsonProperty("@attachments")]
        public List<AttachmentName> Attachments { get; set; }
    }

When I try to execute the following query:

return await query.Select(x => new Repository.SummaryPost
            {
                Id = x.Id,
                CreatedOn = x.CreatedOn,
                Url = x.Url,
                AttachmentNames = x.Metadata.Attachments.Select(x => x.Name),
                Profile = new Repository.SummaryProfile
                {
                    Id = x.Profile.Id,
                    Name = x.Profile.Name,
                    Url = x.Profile.Url,
                    Source = new Repository.SummarySource
                    {
                        Id = x.Profile.Source.Id,
                        Name = x.Profile.Source.Name,
                        Url = x.Profile.Source.Url
                    }
                }
            }).ToListAsync()

It tries to run the following RQL:

from 'Posts' as x 
order by CreatedOn desc 
select { 
    Id : id(x), 
    CreatedOn : x.CreatedOn, 
    Url : x.Url, 
    AttachmentNames : x.@[email protected](function(x){return x.Name;}), 
    Profile : 
    {
        Id:x.Profile.Id,
        Name:x.Profile.Name,
        Url:x.Profile.Url,
        Source:{
            Id:x.Profile.Source.Id,
            Name:x.Profile.Source.Name,
            Url:x.Profile.Source.Url
        }
    }
}
limit $p0, $p1

Which is invalid and it throws the following error:

 ---> Esprima.ParserException: Line 5: Unexpected token ILLEGAL
   at Esprima.Scanner.ThrowUnexpectedToken(String message)
   at Esprima.Scanner.ScanPunctuator()
   at Esprima.JavaScriptParser.NextToken()
   at Esprima.JavaScriptParser.ParseLeftHandSideExpressionAllowCall()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseUpdateExpression()
   at Esprima.JavaScriptParser.ParseUnaryExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseExponentiationExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseBinaryExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseConditionalExpression()
   at Esprima.JavaScriptParser.ParseAssignmentExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseObjectProperty(Token hasProto)
   at Esprima.JavaScriptParser.ParseObjectInitializer()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParsePrimaryExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseLeftHandSideExpressionAllowCall()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseUpdateExpression()
   at Esprima.JavaScriptParser.ParseUnaryExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseExponentiationExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseBinaryExpression()
   at Esprima.JavaScriptParser.InheritCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseConditionalExpression()
   at Esprima.JavaScriptParser.ParseAssignmentExpression()
   at Esprima.JavaScriptParser.IsolateCoverGrammar[T](Func`1 parseFunction)
   at Esprima.JavaScriptParser.ParseExpression()
   at Esprima.JavaScriptParser.ParseReturnStatement()
   at Esprima.JavaScriptParser.ParseStatement()
   at Esprima.JavaScriptParser.ParseStatementListItem()
   at Esprima.JavaScriptParser.ParseScript(Boolean strict)
   at Raven.Server.Documents.Queries.QueryMetadata.HandleSelectFunctionBody(BlittableJsonReaderObject parameters) in C:\Builds\RavenDB-Stable-5.1\51018\src\Raven.Server\Documents\Queries\QueryMetadata.cs:line 601

This is due to x.@metadata.@attachments if I run the same query without the metadata on Raven Studio, it runs without problems.

I'd like to know if I'm missing something, or if there is a proper way to achieve that.

Upvotes: 1

Views: 184

Answers (1)

gabriel giovanini
gabriel giovanini

Reputation: 189

After some try and error attempts I could figure it out how to properly do it.

First, there is no need to use those JsonProperty notation. Second, to properly query the metadata we need to use getMetadata(x) from RQL, and to generate that query we need to modify that LINQ to use RavenQuery.Metadata(x). Something like this:

 return await query.Select(x => new Repository.SummaryPost
            {
                Id = x.Id,
                CreatedOn = x.CreatedOn,
                Url = x.Url,
                Metadata = RavenQuery.Metadata(x)["@attachments"],
                Profile = new Repository.SummaryProfile
                {
                    Id = x.Profile.Id,
                    Name = x.Profile.Name,
                    Url = x.Profile.Url,
                    Source = new Repository.SummarySource
                    {
                        Id = x.Profile.Source.Id,
                        Name = x.Profile.Source.Name,
                        Url = x.Profile.Source.Url
                    }
                }
            }).ToListAsync();

That that will generate the following RQL:

from 'Posts' as x 
order by CreatedOn desc 
select { 
    Id : id(x), 
    CreatedOn : x.CreatedOn, 
    Url : x.Url, 
    Metadata : getMetadata(x)["@attachments"], 
    Profile : {
        Id:x.Profile.Id,
        Name:x.Profile.Name,
        Url:x.Profile.Url,
        Source:{
            Id:x.Profile.Source.Id,
            Name:x.Profile.Source.Name,
            Url:x.Profile.Source.Url
            }
    }
    
} 
limit $p0, $p1

Fetching the list of documents alongside with the list of attachments.

Further we can even be more specific and query only the attachment names, by adding the following line to LINQ query:

AttachmentNames = ((IEnumerable<AttachmentName>) RavenQuery.Metadata(x)["@attachments"]).Select(x => x.Name)

It will generate the following RQL for the AttachmentNames field:

AttachmentNames : getMetadata(x)["@attachments"].map(function(x){return x.Name;})

Querying only the names.

Upvotes: 1

Related Questions