I have a list of documents in my RavenDB instance. Those documents have a list of nested objects that contain a number value in a string format. I need to find the document that has the highest value in a specific nested object of this list. I have no control over the document layout.
Here's the query that I'm using, and a simplified version of the test data:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using Raven.Embedded;
namespace RavenTest
{
class Program
{
static void Main(string[] args)
{
EmbeddedServer.Instance.StartServer();
using (var store = EmbeddedServer.Instance.GetDocumentStore("Embedded"))
{
GenerateTestData(store);
QueryForEmployeeWithHighestGenericDataValue(store);
}
}
private static void QueryForEmployeeWithHighestGenericDataValue(IDocumentStore store)
{
using (var session = store.OpenSession())
{
var query = session.Query<Employee>()
.Where(x => x.NestedGenericData != null)
.Where(x => x.NestedGenericData.Any(y => y.Key == "SomeKeyToFind"))
.Select(x =>
new
{
Value = x.NestedGenericData.First(y => y.Key == "SomeKeyToFind").Value,
Employee = x.FirstName
})
.Where(x => x.Value != null)
.OrderByDescending(x => x.Value, OrderingType.AlphaNumeric);
var employeeWithHighestValue = query.FirstOrDefault();
if (employeeWithHighestValue.Value != "42")
{
var allObjectsOrderedWithoutNulls = JsonConvert.SerializeObject(
query.ToList(),
new JsonSerializerSettings { Formatting = Formatting.Indented });
throw new Exception($"Should be 42, but is {employeeWithHighestValue.Value}");
}
}
}
private static void GenerateTestData(IDocumentStore store)
{
using (var session = store.OpenSession())
{
// generate Id automatically
session.Store(new Employee
{
FirstName = "John10",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeKeyToFind",
Value = "10"
},
}
});
session.Store(new Employee
{
FirstName = "JohnNull",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeKeyToFind",
Value = null
},
}
});
session.Store(new Employee
{
FirstName = "John3",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeKeyToFind",
Value = "3"
},
}
});
session.Store(new Employee
{
FirstName = "John42",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeKeyToFind",
Value = "42"
},
}
});
session.Store(new Employee
{
FirstName = "JohnNoGenericData",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeOtherKey",
Value = "123"
},
}
});
session.Store(new Employee
{
FirstName = "John1",
NestedGenericData = new List<GenericData>
{
new GenericData
{
Key = "SomeKeyToFind",
Value = "1"
},
new GenericData
{
Key = "SomeOtherKey",
Value = "123"
},
}
});
// send all pending operations to server, in this case only `Put` operation
session.SaveChanges();
}
}
}
internal class GenericData
{
public string Key { get; set; }
public string Value { get; set; }
}
internal class Employee
{
public string FirstName { get; set; }
public List<GenericData> NestedGenericData { get; set; }
}
}
This code, if pasted into a new .NET core console app with Install-Package RavenDB.Embedded -Version 4.1.0 installed and executed, will return employeeWithHighestValue whose Value field equals to "10" and the Employee field equals to "John10".
The value of the allObjectsOrderedWithoutNulls (i.e. the dump of the whole query) will contain this:
[
{
"Value": "10",
"Employee": "John10"
},
{
"Value": null,
"Employee": "JohnNull"
},
{
"Value": "3",
"Employee": "John3"
},
{
"Value": "42",
"Employee": "John42"
},
{
"Value": "1",
"Employee": "John1"
}
]
This neither has the projection entries with x.Value == null filtered off, nor is it ordered descendingly by the Value field (AlphaNumerically, or otherwise).
The question:
What is wrong with my query?
Why do the LINQ statements after the Select seem to not apply?
What is the correct way to get the data that I need?