Reputation: 365
I have this class, let’s call it ”Device”. This class has several properties, among one which is a collection property (of string values).
In RavenDB there could be 5000 instances of “Device”, where each of them CAN have a list of string values in the collection property. Let’s call this property “MyStringValues”. My question revolves around the best possible way to search ravendb for a Device instance that contains a string value in it’s collection property.
A very simple example:
void Main()
{
var d1 = new Device();
d1.Id = "device-1";
d1.MyStringValues.Add("123");
d2.MyStringValues.Add("456");
var d2 = new Device();
d2.Id = "device-2";
d2.MyStringValues.Add("789");
d2.MyStringValues.Add("abc");
}
public class Device{
public Device(){
MyStringValues = new List<string>();
}
public string Id {get;set;}
public IList<string> MyStringValues {get;set;}
}
In the method I seek to construct I pass a string value. Based on that string I want to receive a Device. What would be the best way to retrieve this “Device”? Since the number of devices can be up to 5000 I can’t fetch them all and start looping over them. There must be a better (faster) way of doing this. What do you say guys?
Upvotes: 0
Views: 953
Reputation: 1872
You can create an index that match your MyStringValues list, and than query it using LINQ's Any.
Your Index will be something like:
public class Devices_ByStringValue : AbstractIndexCreationTask<Device>
{
public override string IndexName => "Devices/ByStringValue";
public Devices_ByStringValue()
{
Map = devices => from device in devices
select new { device.MyStringValues };
}
}
Now you can query it like:
var devices = session.Query<Device>()
.Where(x => x.MyStringValues.Any(s => s == searchTerm))
.ToList();
Here is a complete console application example:
class Program
{
static void Main(string[] args)
{
Console.Write("> Enter your search term: ");
var searchTerm = Console.ReadLine();
using (var session = DocumentStoreHolder.Instance.OpenSession())
{
var devices = session.Query<Device>()
.Where(x => x.MyStringValues.Any(s => s == searchTerm))
.ToList();
foreach (var device in devices)
{
Console.WriteLine(device.Id);
foreach (var s in device.MyStringValues)
Console.WriteLine($" - {s}");
}
}
Console.ReadKey();
}
}
public class Device
{
public Device()
{
MyStringValues = new List<string>();
}
public string Id { get; set; }
public IList<string> MyStringValues { get; set; }
}
public class Devices_ByStringValue : AbstractIndexCreationTask<Device>
{
public override string IndexName => "Devices/ByStringValue";
public Devices_ByStringValue()
{
Map = devices => from device in devices
select new { device.MyStringValues };
}
}
public class DocumentStoreHolder
{
static DocumentStoreHolder()
{
Instance = new DocumentStore
{
Url = "http://localhost:8080/",
DefaultDatabase = "RavenTest",
};
Instance.Initialize();
Serializer = Instance.Conventions.CreateSerializer();
Serializer.TypeNameHandling = TypeNameHandling.All;
Instance.Initialize();
IndexCreation.CreateIndexes(typeof(Devices_ByStringValue).GetTypeInfo().Assembly, Instance);
}
public static DocumentStore Instance { get; }
public static JsonSerializer Serializer { get; }
}
Upvotes: 4
Reputation: 387
Create an index which will contain the data from MyStringValues. It can contain multiple values in an array for each record.
Then you can create an index query and filter only records which contain a given value using .Where(x => x.MyStringValues.Contains(filteredValue))
.
Then load all matching documents either using streaming (if the number of matching records can be high) or using Load (of you know the upper limit of documents to load).
To test the index in the studio, you can query the index using a simple MyStringValues:abc
query.
Upvotes: 0