Joining documents with Raven 2.0 - Could not find index? - ravendb

I'm new to RavenDB and I have a question about joining two documents with Raven 2.0
I found this page http://daniellang.net/joining-documents-in-ravendb-2-0/ that helped me in finding a solution to join two documents.
Please see my code first (it compiles)
internal class Program
{
private static void Main(string[] args)
{
using (var store = new EmbeddableDocumentStore {DataDirectory = #"C:\temp\ravendata"}.Initialize())
{
using (var session = store.OpenSession())
{
var products = session.Query<Product, UserProducts>()
.AsProjection<UserProductProjection>()
.ToList();
}
}
}
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public string UserId { get; set; }
}
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
public class UserProductProjection
{
public string Id { get; set; }
public string UserName { get; set; }
public string ProductName { get; set; }
public string ProductId { get; set; }
}
internal class UserProducts : AbstractIndexCreationTask<Product, UserProductProjection>
{
public UserProducts()
{
Map = products => from product in products
select new
{
UserName = LoadDocument<User>(product.UserId).Name,
ProductName = product.Name,
ProductId = product.Id
};
Index(projection => projection.ProductId, FieldIndexing.Analyzed);
Index(projection => projection.ProductName, FieldIndexing.Analyzed);
Store(projection => projection.UserName, FieldStorage.Yes);
}
}
Unfortunately it doesn't work :(
Raven.Database.Exceptions.IndexDoesNotExistsException was unhandled
HResult=-2146233088
Message=Could not find index named: UserProducts
Source=Raven.Database
StackTrace:
at Raven.Database.DocumentDatabase.<>c__DisplayClass9a.<Query>b__90(IStorageActionsAccessor actions) in c:\Builds\RavenDB-Stable\Raven.Database\DocumentDatabase.cs:line 1100
....
I really have NO clue at all!! Google doesn't help me on this subject as well, because it is still pretty new as I found out.
If someone has a hint or a solution I would be very grateful.

While you have defined the index you haven't created it in ravendb.
See Defining a static index but basically you need....
IndexCreation.CreateIndexes(typeof(UserProducts).Assembly, documentStore);

Related

Net Core 2.1 Controller truncates Json while the json has no reference loops

I'm struggling with net core returning truncated response. I have already defined no reference loop in my startup services, and also tried to set compatibility version for the version i'm currently using 2.1 as follows:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
I also tried to serialize the array using JsonConvert and it did not throw any reference loop exception. Here's the action in the controller and the serializedArray text:
public IActionResult GetProductItems(int productId)
{
try
{
var productItems = _productsMethods.GetProductItems(productId);
// for testing the object for ref loops
string serialized = Newtonsoft.Json.JsonConvert.SerializeObject(productItems);
return Ok(productItems);
}
catch (ClientException ex)
{
return BadRequest(new { message = ex.Message });
}
catch (Exception ex)
{
return StatusCode(500, new { message = ex.Message });
}
}
// serialized string
//[{"ID":2,"ProductId":6,"ItemId":4,"Product":null,"Item":null,"Orders":[]},{"ID":3,"ProductId":":6,"ItemId":1,"Product":null,"Item":null,"Orders":[]},{"ID":5,"ProductId":":6,"ItemId":2,"Product":null,"Item":null,"Orders":[]}]
Here's the actual response
[{"id":2,"productId":6,"itemId":4,"product":null,"item":null,"orders":
Method:
public List<ProductItem> GetProductItems(int productId)
{
IQueryable<DataSets.ProductItem> query = db.ProductItems
.AsNoTracking()
.Include(k => k.Orders)
.Where(k => k.ProductId == productId);
// result truncated (when array orders is empty)
//return query.Select(_mapper.Map<ProductItem>).ToList();
// without automapper, also truncated
//return query.Select(k => new ProductItem()
//{
// ID = k.ID,
// ItemId = k.ItemId,
// ProductId = k.ProductId,
// Orders = k.Orders.Select(a => new Order() { ID = a.ID })
// .ToList()
//}).ToList();
// WORKS, not getting truncated
// order not included
return query.Select(k => new ProductItem()
{
ID = k.ID,
ItemId = k.ItemId,
ProductId = k.ProductId,
}).ToList();
}
Entities (renamed and removed props for simplification):
public class Product
{
public int ID { get; set; }
// some props
public string UserId { get; set; }
public User User { get; set; }
public List<ProductItem> Items { get; set; }
}
public class ProductItem
{
public int ID { get; set; }
// some props
public int ProductId { get; set; }
public int ItemId { get; set; }
public Product Product { get; set; }
public Item Item { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int ID { get; set; }
// some props
public int ItemId { get; set; }
public ProductItemOrder Item { get; set; }
}
Since there is no reference loop in orders and also the reference loop is ignored. Why is this still truncating?
I think the problem is public Product Product { get; set; } part here. You should define your Product entity virtually.
Here is an example below,
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Tags { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
Here is source.
For someone who might have this issue in the future. While I think the API should throw that error instead of just truncating the response.
I had 2 properties with the same letters but different letter case IPAddress and IpAddress.
SerializeObject alone wasn't throwing an exception, then I did this (CamelCase Resolver) to point out the issue:
Newtonsoft.Json.JsonConvert.DefaultSettings = () => new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
string serialized = Newtonsoft.Json.JsonConvert.SerializeObject(productItems);
So it threw: A member with the name 'ipAddress' already exists on ...

New records inserted in foreign key table when inserting in parent table

I am new to Asp.net MVC and working on a simple blog application (Asp.Net MVC5, EF6) for learning.
I am using repository pattern for the solution architecture with EF Code first migration, Ninject for DI. On the client side, I am using jQuery Grid for Admin to manage Posts, Categories and Tags.
- Blog.Model: Post.cs, Category.cs, Tags.cs
public class Post
{
[Required(ErrorMessage = "Id is required")]
public int Id { get; set; }
[Required(ErrorMessage = "Title is required")]
[StringLength(500, ErrorMessage = "Title cannot be more than 500 characters long")]
public string Title { get; set; }
[Required(ErrorMessage = "Short description is required")]
public string ShortDescription { get; set; }
[Required(ErrorMessage = "Description is required")]
public string Description { get; set; }
public bool Published { get; set; }
[Required(ErrorMessage = "PostedOn date is required")]
public DateTime PostedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
[ForeignKey("Category")]
public virtual int CategoryId { get; set; }
public virtual Category Category { get; set; }
public virtual IList<Tag> Tags { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500,ErrorMessage = "Category name length cannot exceed 500")]
public string Name { get; set; }
[Required(ErrorMessage = "Category Name is required")]
[StringLength(500, ErrorMessage = "Category name length cannot exceed 500")]
public string Description { get; set; }
[JsonIgnore]
public virtual IList<Post> Posts { get; set; }
}
public class Tag
{
public int Id { get; set; }
[Required(ErrorMessage = "Name is required")]
[StringLength(500, ErrorMessage = "Name length should not exceed 500 characters")]
public string Name { get; set; }
public string Description { get; set; }
[JsonIgnore]
public IList<Post> Posts { get; set; }
}
- Blog.Repository: BlogRepository, IBlogRepository, BlogContext
public interface IBlogRepository
{
int SavePost(Post post);
//Other methods...
}
public class BlogRepository : BlogContext, IBlogRepository
{
public BlogContext _db;
public BlogRepository(BlogContext db)
{
_db = db;
}
public int SavePost(Post post)
{
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
//Other implementations...
}
public class BlogContext : DbContext, IDisposedTracker
{
public BlogContext() : base("BlogDbConnection") { }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public bool IsDisposed { get; set; }
protected override void Dispose(bool disposing)
{
IsDisposed = true;
base.Dispose(disposing);
}
- Blog.Web: AdminController.cs, NinjectWebCommon.cs
AdminController sends/consumes data in Json format.
public class AdminController : Controller
{
private readonly IBlogRepository _blogRepository;
public AdminController(IBlogRepository blogRepository)
{
_blogRepository = blogRepository;
}
//POST: /Admin/CreatePost
[HttpPost, ValidateInput(false)]
public ContentResult CreatePost([ModelBinder(typeof(PostModelBinder))] Post model)
{
string json;
ModelState.Clear();
if (TryValidateModel(model))
{
var id = _blogRepository.SavePost(model);
json = JsonConvert.SerializeObject(
new
{
id = id,
success = true,
message = "Post saved successfully."
});
}
else
{
json = JsonConvert.SerializeObject(
new
{
id = 0,
success = false,
message = "Post not saved."
});
}
return Content(json, "application/json");
}
}
public static class NinjectWebCommon
{
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<BlogContext>().ToSelf(); //This isn't helping either
kernel.Bind<IBlogRepository>().To<BlogRepository>();
}
}
I am using Custom Model Binding because I was getting validation exception while saving post since list of Categories and Tags received from grid do not map to actual objects in the application model. Therefore in the custom model binding, I am populating Post object with actual objects received from grid. This Post object is Sent to controller which Save to database using DbContext and Repository.
public class PostModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var post = (Post)base.BindModel(controllerContext, bindingContext);
**var blogRepository = new BlogRepository(new BlogContext());**//I think here I need to inject the dependency for BlogContext, but don't know how to do that.
if (post.Category != null)
{
post.Category = blogRepository._db.Categories.AsNoTracking().Single(c => c.CategoryId == post.Category.CategoryId);
}
var tags = bindingContext.ValueProvider.GetValue("Tags").AttemptedValue.Split(',');
if (tags.Length > 0)
{
post.Tags = new List<Tag>();
foreach (var tag in tags)
{
var id = int.Parse(tag.Trim());
post.Tags.Add(blogRepository._db.Tags.AsNoTracking().Single(t => t.Id == id));
}
}
if (bindingContext.ValueProvider.GetValue("oper").AttemptedValue.Equals("edit"))
post.ModifiedOn = DateTime.UtcNow;
else
post.PostedOn = DateTime.UtcNow;
return post;
}
}
Issue: When the Post is saved, data context inserts new rows for Category and Tags in their respective tables. The newly created post refers to new Category (Id:22) under Foreign key column.
Post:
Category:
Tag:
I think the reason for this is that when entity is saved it is attached to a different ObjectContext and I need to attach it to current context but do not know how? I found similar question asked before but there isn't an accepted answer to that. Any help would be greatly appreciated.
I was able to resolve above issue by attaching category and tags value to objectcontext manually, which indicates EF the changes it needs to make. This way it doesn't create new entries in Category and Tag's parent tables.
public int SavePost(Post post)
{
//attach tags to db context for Tags to tell EF
//that these tags already exist in database
foreach (var t in post.Tags)
{
_db.Tags.Attach(t);
}
//tell EF that Category already exists in Category table
_db.Entry(post.Category).State = EntityState.Modified;
_db.Posts.Add(post);
_db.SaveChanges();
return post.Id;
}
public void EditPost(Post post)
{
if (post == null) return;
//get current post from database
var dbPost = _db.Posts.Include(p => p.Tags).SingleOrDefault(p => p.Id == post.Id);
//get new list of tags
var newTags = post.Tags.Select(tag => new Tag() { Id = tag.Id, Name = tag.Name, Description = tag.Description }).ToList();
if (dbPost != null)
{
//get category from its parent table and assign to db post
dbPost.Category = _db.Categories.Find(post.Category.CategoryId); ;
//set scalar properties
_db.Entry(dbPost).CurrentValues.SetValues(post);
//remove tags from post in database
foreach (var t in dbPost.Tags.ToList())
{
if (!newTags.Contains(t))
{
dbPost.Tags.Remove(t);
}
}
//add tags to post in database
foreach (var t in newTags)
{
if (dbPost.Tags.All(p => p.Id != t.Id))
{
var tagInDb = _db.Tags.Find(t.Id);
if (tagInDb != null)
{
dbPost.Tags.Add(tagInDb);
}
}
}
}
//save changes
_db.SaveChanges();
}

NHibernate JoinQueryOver

Calling all NHibernate gurus out there!
If any one of you brainy folks could help me with the following conundrum I'd be most grateful:
I have some entities that describe RSS feeds from various sources that are grouped together in an entity called FeedList.
I am trying to select only the distinct SourceFeed entities that are linked with a given FeedList. (i.e "WHERE FeedList.name = 'feedlist1' ".
I've been playing around with JoinQueryOver for a while now but I just can't seem to work out how to get the required results.
The entities are related like this:
FeedList > Feed > FeedSource
So a FeedList contains many Feeds and each Feed belongs to a FeedSource.
Here is the code for the entities:
public class Feed
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual FeedSource FeedSource { get; set; }
public virtual string URL { get; set; }
}
public class FeedList
{
public virtual int Id{get;set;}
public virtual string Name { get; set; }
public virtual IList<Feed> Feeds { get; set; }
}
public class FeedSource
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I am using Fluent Automapping with the following overrides:
public class FeedOverride : IAutoMappingOverride<Feed>
{
public void Override(AutoMapping<Feed> mapping)
{
mapping.References<FeedSource>(map => map.FeedSource).Cascade.All();
}
}
public class FeedListOverride : IAutoMappingOverride<FeedList>
{
public void Override(AutoMapping<FeedList> mapping)
{
mapping.HasManyToMany<Feed> (map => map.Feeds).Cascade.All().Table("FeedList_Feed");
}
}
Here is some code that creates some sample data:
private static void CreateSampleData()
{
using (var session = HibernateHelper.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
//Create source 1 and feeds
FeedSource source1 = new FeedSource() { Name = "BBC" };
IList<Feed> feedsForSource1 = new List<Feed>(){
CreateFeed("Sample Feed1",source1,"http://feed1.xml"),
CreateFeed("Sample Feed2",source1,"http://feed2.xml")
};
FeedList feedList1 = CreateFeedList("FeedList1", feedsForSource1);
//Create source 2 and feeds
FeedSource source2 = new FeedSource() { Name = "Sky" };
IList<Feed> feedsForSource2 = new List<Feed>(){
CreateFeed("Sample Feed3",source2,"http://feed3.xml"),
CreateFeed("Sample Feed4",source2,"http://feed4.xml"),
CreateFeed("Sample Feed5",source2,"http://feed5.xml")
};
FeedList feedList2 = CreateFeedList("FeedList2", feedsForSource2);
session.SaveOrUpdate(feedList1);
session.SaveOrUpdate(feedList2);
transaction.Commit();
}
}
}
What am I trying to achieve?
If I were to describe it (very poorly) in SQL, I'm looking to do something like this:
select distinct *
from FeedList as list
LEFT JOIN FeedList_Feed as list_feed
ON list.Id = list_feed.FeedList_id
LEFT JOIN Feed as feed
ON feed.FeedSource_id = list_feed.Feed_id
LEFT JOIN FeedSource as src
ON src.Id = list_feed.Feed_id
WHERE list.Name="a feedlist name"
Thanks very much for your time :)
Ok so I've managed to work this out finally for anyone that's interested:
Entities
public class Feed
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
public virtual FeedSource FeedSource { get; set; }
public virtual string URL { get; set; }
public virtual FeedList FeedList { get; set; }
}
public class FeedList
{
public virtual int Id{get;set;}
public virtual string Name { get; set; }
public virtual IList<Feed> Feeds { get; set; }
}
public class FeedSource
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
Overrides
public class FeedOverride : IAutoMappingOverride<Feed>
{
public void Override(AutoMapping<Feed> mapping)
{
mapping.References<FeedList>(map => map.FeedList).Cascade.All();
mapping.References<FeedSource>(map => map.FeedSource).Cascade.All();
}
}
public class FeedListOverride : IAutoMappingOverride<FeedList>
{
public void Override(AutoMapping<FeedList> mapping)
{
mapping.HasManyToMany<Feed>(map => map.Feeds).Cascade.All().Table("FeedList_Feed");
//mapping.HasMany<FeedListFeed>(map => map.Feeds).Cascade.All();
}
}
And finally:
The Query
session.QueryOver<FeedList>()
.Inner.JoinAlias(f => f.Feeds, () => feedAlias)
.Where(fl => fl.Name == name)
.Select(x => feedAlias.FeedSource)
.List<FeedSource>().Distinct().ToList();

How to use Raven LoadDocument

I'm having trouble querying RavenDB with even the simplest of queries, probably I'm doing something wrong, but after a few hours I just can't see it anymore. I've googled almost anything I can think of..
I have these entities:
public class User
{
public string Id { get; set; }
public string DisplayName { get; set; }
public string RealName { get; set; }
public string Email { get; set; }
public string PictureUri { get; set; }
public List<Comment> Comments { get; set; }
public List<Role> Roles { get; set; }
}
public class NewsItem
{
public string Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime Created { get; set; }
public string UserId { get; set; }
public List<Tag> Tags { get; set; }
public List<Comment> Comments { get; set; }
public List<WebImage> Images { get; set; }
}
I want to query these so I get a list of newsItems, but with the user information alongside it. So I read the docs and tried the LoadDocument feature, the index:
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result
{
public string AuthorName { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
select new
{
AuthorName = LoadDocument<User>(newsItem.UserId).DisplayName
};
}
}
Which I try to use like:
var result = _documentSession.Query<NewsItemIndexWithComments.Result, NewsItemIndexWithComments>().AsProjection<NewsItemIndexWithComments.Result>().ToList();
Now I get the number of documents in my list, but the AuthorName is always null. If I don't use the AsProjection method, I won't get any results. Can anyone show me a proper example on which I can experiment further?
Thanks.
_ edit:
That helped a lot, thanks :) Now for step two, I'm sorry if I'm being a bit newbish, but you'll have to start somewhere. In the newsitems there are comments, in these comments there is another reference to the userid. You can probably guess what I want to do: I want the user info for the comments with the comments as well.
new Index:
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result : NewsItem
{
public string AuthorName { get; set; }
public string AuthorId { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
let user = LoadDocument<User>(newsItem.UserId)
select new
{
AuthorName = user.DisplayName,
AuthorId = user.Id,
};
Store(x => x.AuthorName, FieldStorage.Yes);
Store(x => x.AuthorId, FieldStorage.Yes);
}
}
Comment class:
public class Comment
{
public string Id { get; set; }
public string Text { get; set; }
public string UserId { get; set; }
public string User { get; set; }
public DateTime Created { get; set; }
}
How can I query the comments and expand the results for that? Or is it better to create a new index just for the comments and get the user info analog to the solution above?
You're almost there, you just need to store the field you are projecting. Add this to the index constructor, after the map.
Store(x=> x.AuthorName, FieldStorage.Yes);
This is because you want it returned and available for AsProjection to find. If you just wanted to use the author name in a where or orderby, you wouldn't need it.
If you just want to include the comments in your AsProjection, you can simply index the entire object along.
Note that indexing a custom object will mean that you're not able to query on it using a .Where(). RavenDB can only query on flattened results (ints, decimals, strings, dates).
In order to, for instance, query on the title, you will need to create a seperate Property public string Title { get; set; } and map it with Title = newsItem.Title.
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result : NewsItem
{
public string AuthorName { get; set; }
public string AuthorId { get; set; }
public List<Comment> Comments { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
let user = LoadDocument<User>(newsItem.UserId)
select new
{
AuthorName = user.DisplayName,
AuthorId = user.Id,
Comments = newsItem.Comments.
};
Store(x => x.AuthorName, FieldStorage.Yes);
Store(x => x.AuthorId, FieldStorage.Yes);
Store(x => x.Comments, FieldStorage.Yes);
}
}

RavenDB TransformResults

I'm trying to use the TransformResults feature, and I can't get it to work. I'm not totally sure I understand this feature, perhaps there is another way to solve this problem. What I want is just the Id from the Order and the email addesses from the Customer and the Entrepreneur. I am happy for all tips that can take me in the right direction. Here is my code.
Document
public class OrderDocument
public string Id {get; set }
public EntrepreneurInfo EntrepreneurInfo { get; set; }
public CustomerInfo CustomerInfo { get; set; }
public OrderStatus CurrentOrderStatus { get; set; }
}
Info classes
public class EntrepreneurInfo
{
public string EntrepreneurDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
public class CustomerInfo
{
public string CustomerDocumentId { get; set; }
public string Number { get; set; }
public string Name { get; set; }
}
The info classes are just subsets of a Customer and Entrepreneur documents respectively.
The Customer and Entrepreneur documents inherits from a base class ( AbstractOrganizationDocument) that has the EmailAddress property.
My Index
public class OrdersApprovedBroadcastingData :
AbstractIndexCreationTask<OrderDocument, OrdersApprovedBroadcastingData.ReduceResult>
{
public OrdersApprovedBroadcastingData()
{
this.Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
Id = d.Id,
CustomerId = d.CustomerInfo.CustomerDocumentId,
EntrepreneurId = d.EntrepreneurInfo.EntrepreneurDocumentId
};
this.TransformResults = (db, orders) => from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurId)
select
new
{
o.Id,
o.CustomerId,
CustomerEmail = customer.EmailAddress,
o.EntrepreneurId,
EntrepreneurEmail = entrepreneur.EmailAddress
};
}
public class ReduceResult
{
public string Id { get; set; }
public string CustomerId { get; set; }
public string CustomerEmail { get; set; }
public string EntrepreneurId { get; set; }
public string EntrepreneurEmail { get; set; }
}
}
If I look at the result of this Index in Raven Studio I get null values for all fields except the Id. And finally here is my query.
Query
var items =
this.documentSession.Query<OrdersApprovedBroadcastingData.ReduceResult, OrdersApprovedBroadcastingData>()
.Select(x => new OrdersToBroadcastListItem
{
Id = x.Id,
CustomerEmailAddress = x.CustomerEmail,
EntrepreneurEmailAddress = x.EntrepreneurEmail
}).ToList();
Change your index to:
public class OrdersApprovedBroadcastingData : AbstractIndexCreationTask<OrderDocument>
{
public OrdersApprovedBroadcastingData()
{
Map = docs => from d in docs
where d.CurrentOrderStatus == OrderStatus.Approved
select new
{
};
TransformResults = (db, orders) =>
from o in orders
let customer = db.Load<CustomerDocument>(o.CustomerInfo.CustomerDocumentId)
let entrepreneur = db.Load<EntrepreneurDocument>(o.EntrepreneurInfo.EntrepreneurDocumentId)
select new
{
o.Id,
CustomerEmailAddress = customer.EmailAddress,
EntrepreneurEmailAddress = entrepreneur.EmailAddress
};
}
}
Your result class can simply be the final form of the projection, you don't need the intermediate step:
public class Result
{
public string Id { get; set; }
public string CustomerEmailAddress { get; set; }
public string EntrepreneurEmailAddress { get; set; }
}
You don't have to nest this class in the index if you don't want to. It doesn't matter either way. You can query either with:
var items = session.Query<Result, OrdersApprovedBroadcastingData>();
Or with
var items = session.Query<OrderDocument, OrdersApprovedBroadcastingData>().As<Result>();
Though, with the first way, the convention tends to be to nest the result class, so really it would be
var items = session.Query<OrderDocument.Result, OrdersApprovedBroadcastingData>();
Note in the index map, I am not including any properties at all. None are required for what you asked. However, if you want to add a Where or OrderBy clause to your query, any fields you might want to filter or sort on should be put in there.
One last thing - the convention you're using of OrderDocument, CustomerDocument, EntrepreneurDocument, is a bit strange. The usual convention is just Order, Customer, Entrepreneur. Think of your documents as the persisted form of the entities themselves. The convention you are using will work, it's just not the one usually used.