1

Suppose I have the following class to be serialized and stored as a RavenDB's document:

public class Block
{
    public string Id { get; set; }
    public List<List<dynamic>> data { get; set; }
}

After storing, a document like this can be seen in the database:

{
"Id": "f539770a",
"columns": [ 
              [ 90, 91, 92, 93, 94 ], 
              [ "C", "D", "A", "B", "C" ] 
           ] }

I want to execute a query to retrieve the Nth list of values inside the "Columns" field:

session.Query<Block>().Where(b => b.Id == "f539770a").Select(b =>b.columns[i]);

And I get the following Error:

{"Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[System.Object]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath '__document_id'."}

It seems like the query is working (the server returns http 200), but there is a client-side deserialization problem.

Am I missing something?

Thanks!!

UPDATE:

I changed the data structure as the error seems to suggest:

public class Column
{
    public List<dynamic> data { get; set; }
}

public class Block
{
    public string Id { get; set; }
    public List<Column> columns { get; set; }
}

The stored document is like:

{
"Id": "f539770a",
"columns": [
             { "data": [ 95, 96, 97, 98, 99 ] },
             { "data": [ "D", "A", "B", "C", "D" ] }
           ]}

After executing this query:

session.Query<Block>().Include<Block>(b => b.columns).Where(b => b.parentFileId == dbFileDescriptor.Id).Select(b => b.columns[i])

I get no exception, but the nested array is not loaded:

Nested array not loading

Community
  • 1
  • 1
user1275011
  • 1,552
  • 1
  • 16
  • 36

1 Answers1

2

Remove the Include. That's not what it's used for. The Include is for pre-loading references to other documents so you don't have to make multiple database trips. You might be making RavenDB look for Block's and their corresponding Column documents. See documentation here.

session.Query<Block>().Where(b => b.parentFileId == dbFileDescriptor.Id).Select(b => b.data[i]);

EDIT:

If you're going to look something by the Id, use Load not Query.

session.Load<Block>("f539770a").data[i];

I verified that this works with a raven unit test using RavenDB.Tests.Helpers nuget package and Shouldly for the assertion.

    public class SoQuestion : RavenTestBase
    {
        [Fact]
        public void GetBlock_Success()
        {
            using (var docStore = NewDocumentStore())
            {
                using (var session = docStore.OpenSession())
                {
                    session.Store(new Block
                    {
                        Id = "f539770a",
                        data = new List<List<dynamic>>()
                        {
                            new List<dynamic>()
                            {
                                90,
                                91,
                                92,
                                93,
                                94
                            },
                            new List<dynamic>()
                            {
                                "C",
                                "D",
                                "A",
                                "B",
                                "C"
                            }
                        }
                    });
                    session.SaveChanges();
                }

                docStore.WaitForStaleIndexesToComplete();

                //Act
                var block = GetBlock(docStore);

                //Assert
                block.ShouldNotBe(null);
                block.data.ShouldNotBeEmpty();
            }
        }

        private Block GetBlock(IDocumentStore documentStore)
        {
            using (var session = documentStore.OpenSession())
            {
                return session.Load<Block>("f539770a");
            }
        }

        public class Block
        {
            public string Id { get; set; }
            public List<List<dynamic>> data { get; set; }
        }
    }
gaunacode.com
  • 365
  • 2
  • 13
  • Still not working. The Load().columns[i] retrieves the whole block, and then the column[i] is selected locally. The idea is to minimize the required transport between the database and the client by requesting only the required element. By the way, does RavenDB supports this kind of partial data retrieval, or does everything in client's memory? – user1275011 Mar 10 '15 at 17:32
  • I'm looking for a similar behavior you would get in MongoDB with the query: this.DBFileBlocks.Find(query).SetFields(Fields.Include(fb => fb.rows[x]). this transports only the selected data, not the whole document. – user1275011 Mar 10 '15 at 17:40
  • So if I understand correctly, you're selecting these arrays in the document within a for-loop. Load just the document first. You have it in memory, then you can iterate over the list of `data` elements. That would only be 1 trip to the database. Loading the whole document is far more efficient than using document relationships. Deserializing a whole document if far quicker than something traveling over the wire. Now if you're expecting your document to be extremely large then you have a different problem. (1 MB+/doc) – gaunacode.com Mar 10 '15 at 19:13
  • The entire problem can be reduced to this "I want to retrieve the Nth element of an array, given that such element is also an array". It's just projecting the results, what Select should do. If Select is being performed clientside, then that would be the roughest query implementation a database could have, which, I hope, is not the case for RavenDB. – user1275011 Mar 10 '15 at 19:34
  • hence that's why you should keep this in mind when modeling your documents. If you don't want this behavior then put your sub classes into a their own documents. Optionally, you can try to figure out how to place this into a static index. – gaunacode.com Mar 10 '15 at 21:03