This scenario demonstrates how to store and query non-uniform JSON documents via the help of dynamic
.
Scenario
Let us assume we have a document with non-uniform JSON records, presenting temperature sensor data whereby individual records are identified either via the field detector or sensor.
// Our documents with non-uniform structure
var jsonRecords = @"
[{
'sensor': 'aisle-1',
'timestamp': '2020-01-21 11:19:19.283',
'temperature': 21.2
}, {
'sensor': 'aisle-2',
'timestamp': '2020-01-21 11:18:19.220',
'temperature': 21.6
}, {
'sensor': 'aisle-1',
'timestamp': '2020-01-21 11:17:19.190',
'temperature': 21.6
}, {
'detector': 'aisle-1',
'timestamp': '2020-01-21 11:16:19.100',
'temperature': 20.9
}, {
'sensor': 'aisle-3',
'timestamp': '2020-01-21 11:15:19.037',
'temperature': 21.7,
}, {
'detector': 'aisle-1',
'timestamp': '2020-01-21 11:14:19.100',
'temperature': -1.0
}
]";
To store and later read back these records, we create a wrapper type with a dynamic
property to present our record.
class TemperatureData
{
public int Id { get; set; }
public dynamic Values { get; set; }
}
We then read and serialize our records into our newly introduced intermediate type and persist an array of its instances via BulkInsert
. Lastly, we read back the records, via the non-generic Query
extension method, passing in predicates that take into account the non-uniform fields of the source documents. After reading back the data for sensor-1, we calculate the average of its recorded temperatures:
using (var reader = new StringReader(jsonRecords))
{
// Deserialize our document
var records = theStore.Serializer.FromJson<dynamic[]>(reader);
var docs = records.Select(x => new TemperatureData { Values = x }).ToArray();
// Persist our records
theStore.BulkInsertDocuments(docs);
using (var session = theStore.OpenSession())
{
// Read back the data for "aisle-1"
dynamic[] tempsFromDb = session.Query(typeof(TemperatureData),
"where data->'Values'->>'detector' = :sensor OR data->'Values'->>'sensor' = :sensor", new { sensor = "aisle-1" }).ToArray();
var temperatures = tempsFromDb.Select(x => (decimal)x.Values.temperature);
Assert.Equal(15.675m, temperatures.Average());
Assert.Equal(4, tempsFromDb.Length);
}
}