-
I will build on top of the cross-collection search example in the docs . So let's say I wanted to be able to refer to any entity available in the cross-collection search index from enum ReferenceType
{
Company, Project, Employee
}
// types stored in the database
class Reference
{
public Guid ReferenceId { get; set; }
public ReferenceType Type { get; set; }
}
class MyThingEntity
{
// < ... >
public Reference Reference { get; set; }
}
// types returned to users
class ReferenceDto
{
public Guid ReferenceId { get; set; }
public ReferenceType Type { get; set; }
public string Name { get; set; } // coming from Smart_Search.Projection.DisplayName
}
class MyThingDto
{
// < ... >
public ReferenceDto Reference { get; set; }
} My goal is to return I know I could do it in 2 database calls like this var things = session.Query<MyThingEntity>().ToList();
var referenceIds = things.Select(x => x.ReferenceId).ToList();
var combinedDataSet = session
.Query<Smart_Search.Result, Smart_Search>()
.Where(x => x.Id.In(referenceIds))
.ProjectInto<Smart_Search.Projection>()
.ToList();
// then map to MyThingDto in memory but would it be possible to store that data in an index? Furthermore, would it be possible to do the same if |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 6 replies
-
I'm not sure that I'm following. What does your index looks like? You want to get just the employees from that search, for example? |
Beta Was this translation helpful? Give feedback.
-
I made a test explaining what I want to achieve, I hope it makes more sense now using FluentAssertions;
using Raven.Client.Documents;
using Raven.Client.Documents.Indexes;
namespace TestProject1;
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Employee
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public enum ReferenceType
{
Product,
Employee
}
public class Smart_Search : AbstractMultiMapIndexCreationTask<Smart_Search.Result>
{
public class Result
{
public required string Id { get; set; }
public required string DisplayName { get; set; }
public ReferenceType ReferenceType { get; set; }
}
public Smart_Search()
{
AddMap<Product>(products => from p in products
select new Result
{
Id = p.Id,
DisplayName = p.Name,
ReferenceType = ReferenceType.Product
});
AddMap<Employee>(employees => from e in employees
select new Result
{
Id = e.Id,
DisplayName = e.FirstName + " " + e.LastName,
ReferenceType = ReferenceType.Employee
});
Store(x => x.Id, FieldStorage.Yes);
Store(x => x.DisplayName, FieldStorage.Yes);
Store(x => x.ReferenceType, FieldStorage.Yes);
}
}
public class Reference
{
public ReferenceType ReferenceType { get; set; }
public required string Id { get; set; }
}
public class MyThing
{
public string Id { get; set; }
public string Name { get; set; }
public Reference Reference { get; set; }
}
public class ReferenceDto
{
public ReferenceType ReferenceType { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class MyThingDto
{
public string Id { get; set; }
public string Name { get; set; }
public ReferenceDto Reference { get; set; }
}
[TestClass]
public class UnitTest1
{
// this just sets up an in-memory ravendb
private readonly IDocumentStore _store = new TestRavenDb().Store;
public UnitTest1()
{
new Smart_Search().Execute(_store);
}
[TestMethod]
public async Task TestMethod1()
{
// arrange
// so given the data defined here I want to get the result defined in the assert section
var product = new Product()
{
Id = "products/1",
Name = "Product 1"
};
var employee = new Employee()
{
Id = "employees/1",
FirstName = "John",
LastName = "Doe"
};
var thingWithEmployeeReference = new MyThing()
{
Id = "myThings/1",
Name = "My Thing 1",
Reference = new Reference()
{
Id = employee.Id,
ReferenceType = ReferenceType.Employee
}
};
var thingWithProductReference = new MyThing()
{
Id = "myThings/2",
Name = "My Thing 2",
Reference = new Reference()
{
Id = product.Id,
ReferenceType = ReferenceType.Product
}
};
using var session = _store.OpenAsyncSession();
await session.StoreAsync(product);
await session.StoreAsync(employee);
await session.StoreAsync(thingWithEmployeeReference);
await session.StoreAsync(thingWithProductReference);
await session.SaveChangesAsync();
// act
// to get all the data I need - I have to make 2 database calls
var projections = await session
.Query<Smart_Search.Result, Smart_Search>()
.ProjectInto<Smart_Search.Result>()
.ToListAsync();
var myThings = await session
.Query<MyThing>()
.ToListAsync();
var result = myThings
.Select(x =>
{
// then join the data in memory here
var reference = projections.Find(p => p.Id == x.Reference.Id);
return new MyThingDto()
{
Id = x.Id,
Name = x.Name,
Reference = new ReferenceDto()
{
Id = reference.Id,
Name = reference.DisplayName,
ReferenceType = reference.ReferenceType
}
};
})
.ToList();
// I would like to make it somehow in a single database call
// i.e. make the join on some index
// assert
// this is the result I want
var expected = new[]
{
new MyThingDto()
{
Id = "myThings/1",
Name = "My Thing 1",
Reference = new ReferenceDto()
{
Id = "employees/1",
Name = "John Doe",
ReferenceType = ReferenceType.Employee
}
},
new MyThingDto()
{
Id = "myThings/2",
Name = "My Thing 2",
Reference = new ReferenceDto()
{
Id = "products/1",
Name = "Product 1",
ReferenceType = ReferenceType.Product
}
}
};
result.Should().BeEquivalentTo(expected);
}
} |
Beta Was this translation helpful? Give feedback.
-
@ayende I realized that the model I'm working with is actually a bit more complex than that. Here's the updated code and the main reason why I want to 'join' on the index - the index holds other joined data using FluentAssertions;
using Raven.Client.Documents;
using Raven.Client.Documents.Indexes;
namespace TestProject1;
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
// to give some context in my app this is global data
public class System
{
public string Id { get; set; }
public string Name { get; set; }
}
// and this belongs to a specific tenant
public class SystemUsage
{
public string Id { get; set; }
public string SystemId { get; set; }
// string tenantId
}
public enum ReferenceType
{
Product,
SystemUsage
}
public class Smart_Search : AbstractMultiMapIndexCreationTask<Smart_Search.Result>
{
public class Result
{
public required string Id { get; set; }
public required string DisplayName { get; set; }
public ReferenceType ReferenceType { get; set; }
}
public Smart_Search()
{
AddMap<Product>(products => from p in products
select new Result
{
Id = p.Id,
DisplayName = p.Name,
ReferenceType = ReferenceType.Product
});
AddMap<SystemUsage>(usages =>
from u in usages
let system = LoadDocument<System>(u.SystemId)
select new Result
{
Id = u.Id,
DisplayName = system.Name,
ReferenceType = ReferenceType.SystemUsage
});
Store(x => x.Id, FieldStorage.Yes);
Store(x => x.DisplayName, FieldStorage.Yes);
Store(x => x.ReferenceType, FieldStorage.Yes);
}
}
public class Reference
{
public ReferenceType ReferenceType { get; set; }
public required string Id { get; set; }
}
public class MyThing
{
public string Id { get; set; }
public string Name { get; set; }
public Reference Reference { get; set; }
}
public class ReferenceDto
{
public ReferenceType ReferenceType { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class MyThingDto
{
public string Id { get; set; }
public string Name { get; set; }
public ReferenceDto Reference { get; set; }
}
[TestClass]
public class UnitTest1
{
private readonly IDocumentStore _store = new TestRavenDb().Store;
public UnitTest1()
{
new Smart_Search().Execute(_store);
}
[TestMethod]
public async Task TestMethod1()
{
// arrange
// so given the data defined here I want to get the result defined in the assert section
var product = new Product()
{
Id = "products/1",
Name = "Product 1"
};
var system = new System()
{
Id = "systems/1",
Name = "System 1"
};
var usage = new SystemUsage()
{
Id = "systemUsages/1",
SystemId = system.Id
};
var thingWithSystemUsage = new MyThing()
{
Id = "myThings/1",
Name = "My Thing 1",
Reference = new Reference()
{
Id = usage.Id,
ReferenceType = ReferenceType.SystemUsage
}
};
var thingWithProductReference = new MyThing()
{
Id = "myThings/2",
Name = "My Thing 2",
Reference = new Reference()
{
Id = product.Id,
ReferenceType = ReferenceType.Product
}
};
using var session = _store.OpenAsyncSession();
await session.StoreAsync(product);
await session.StoreAsync(system);
await session.StoreAsync(usage);
await session.StoreAsync(thingWithSystemUsage);
await session.StoreAsync(thingWithProductReference);
await session.SaveChangesAsync();
// act
// to get all the data I need - I have to make 2 database calls
var projections = await session
.Query<Smart_Search.Result, Smart_Search>()
.ProjectInto<Smart_Search.Result>()
.ToListAsync();
var myThings = await session
.Query<MyThing>()
.ToListAsync();
var result = myThings
.Select(x =>
{
// then join the data in memory here
var reference = projections.Find(p => p.Id == x.Reference.Id);
return new MyThingDto()
{
Id = x.Id,
Name = x.Name,
Reference = new ReferenceDto()
{
Id = reference.Id,
Name = reference.DisplayName,
ReferenceType = reference.ReferenceType
}
};
})
.ToList();
// assert
// this is the result I want
var expected = new[]
{
new MyThingDto()
{
Id = "myThings/1",
Name = "My Thing 1",
Reference = new ReferenceDto()
{
Id = "systemUsages/1",
Name = "System 1",
ReferenceType = ReferenceType.SystemUsage
}
},
new MyThingDto()
{
Id = "myThings/2",
Name = "My Thing 2",
Reference = new ReferenceDto()
{
Id = "products/1",
Name = "Product 1",
ReferenceType = ReferenceType.Product
}
}
};
result.Should().BeEquivalentTo(expected);
}
} |
Beta Was this translation helpful? Give feedback.
-
What are you Your issue is basically that for Product, you need the name directly from the document, but for SystemUsage, you need it from the assoicated document? If you run this query, isn't that good enough?
|
Beta Was this translation helpful? Give feedback.
The index you are using has nothing to do with this.
What you want to do is something like:
Basically, your issue is that you have
DisplayName
being different across collections, you can add a interface type that would manage that for you, and it will work.