Sorting on nested Id property - ravendb

Let's say we have a document like this
public class Event
{
public string Id { get; set; }
public EntityDescriptor Venue { get; set; }
// Other properties omitted for simplicity
}
public class EntityDescriptor
{
public string Id { get; set; }
public string Name { get; set; }
}
And an index like this
public class Events : AbstractIndexCreationTask<Event>
{
public Events()
{
Map = items => from e in items
select new
{
Venue_Id = e.Venue.Id,
Venue_Name = e.Venue.Name
};
}
}
When trying to sort on Event.Venue.Id
session.Query<Event, Events>().Take(10).OrderBy(e => e.Venue.Id).ToArray();
the sent request is
/indexes/Events?&pageSize=10&sort=__document_id&SortHint-__document_id=String
Is this by design or a bug?
PS: OrderBy(e => e.Venue.Name) works as expected (sort=Venue_Name).

It's not a bug. __document_id is the special known field containing the ID of the document. It's there regardless of whether you have an .Id property.
edit
I misread your question. This indeed appears to be a bug. I recommend you send a simple repro case to the Raven forum and let them know which RavenDB version you're using.

Related

DDD shared entity between two aggregate roots

I'm working with two different aggregate roots: Post and Question. Both of them have a Category.
So far I have implemented it as a shared entity (which I'm not sure if is a correct design in DDD).
public class Post
{
public Guid Id { get; private set; }
public Category Category { get; private set; }
public string Title { get; private set; }
public string Body { get; private set; }
}
public class Question
{
public Guid Id { get; private set; }
public Category Category { get; private set; }
public string Title { get; private set; }
public string Body { get; private set; }
}
public class Category
{
public int Id { get; private set; }
public string Name { get; private set; }
public string Key { get; private set; }
}
Note: I'm aware I'm falling into primitive obsession anti-pattern, and I have plans on refactor the primitives into ValueObjects.
After read this post DDD: Share entity with multiple aggregate roots I'm thinking that maybe I should convert the Category in a ValueObject (with multiple fields).
In theory Category could be an Entity with its own lifecycle, but reality is that I don't really add/remove/update categories.
Is it possible to use a shared Entity on DDD? Or I better rather use a ValueObject?
Lets deal with one aggregate first: Post
Now to answer your question:
Is it possible to use a shared Entity on DDD? Or I better rather use a ValueObject?
It depends on what you will do with Category.
Scenario 1:
You have a feature(or page) in your application to show all posts of a category. I would go with the following design:
public class Category
{
public int Id { get; set; }
//this is my in-memory database. Use repository and service to adjust yours
public static List<Post> Posts;
public Category()
{
Posts = new List<Post>();
}
public void AddPost(Guid id, string title, string body)
{
var post = new Post(id, title, body, this.Id);
//saving the post into in-memory. Perhaps you can check some business logic inside Post entity
Posts.Add(post);
}
// You can retrieve all posts of a single category
public IEnumerable<Post> GetAllPosts()
{
return Posts.Where(x => x.CategoryId == this.Id);
}
}
public class Post
{
public Guid Id { get; private set; }
public string Title { get; private set; }
public string Body { get; private set; }
public int CategoryId { get; private set; }
public Post(Guid id)
{
Id = id;
}
public Post(Guid id, string title, string body, int categoryId)
{
//I prefer to pass guid into domain from external services.
//Using this way, your service will have the id to return to upper layers.
//Alternatively you can create new guid here on your own
Id = id;
Title = title;
Body = body;
CategoryId = categoryId;
}
// you can retrieve a post detail
public Post GetPost()
{
return Category.Posts.FirstOrDefault(x => x.Id == this.Id);
}
}
I can see only one aggregate root in this scenario: Category.
Scenario 2:
You have posts page, from there users can view detail post. Additionally, every post has a category which will be shown somewhere on that detailed page. You can have following simple design:
public class Post
{
public Guid Id { get; private set; }
public string Title { get; private set; }
public string Body { get; private set; }
public string CatKey { get; private set; }
public Post(Guid id)
{
Id = id;
}
public Post(Guid id, string title, string body, string catKey)
{
//I prefer to pass guid into domain from external services.
//Using this way, your service will have the id to return to upper layers.
//Alternatively you can create new guid here on your own
Id = id;
Title = title;
Body = body;
//I don't even bother with category id. This is a simple value object, you can store all of your categories
//into a hashtable of key-value
CatKey = catKey;
}
// you can retrieve a post detail
public Post GetPost()
{
//get your post detail from repo
}
}
Hope you can make your decision now.
The main question of Entity vs ValueObject is would two instances of the Category with the same values need to be tracked differently? The classic example is a dollar bill - in most instances, the serial number (ID) doesn't matter, and one dollar is the same as another (ValueObject). If your domain is collecting rare bills, though, that would change.
I'd suspect not in your case, since it appears Category is really just comprised of the name and key. If the Category of a Post changes, do you need to track what the Category previous was?

Query entries by nested collection elements in RavenDB

I'm new to RavenDB and I'm struggling with this simple (i guess) issue.
I have a Subscriber with a collection of Subscriptions. And I want to make search by Subscription's fields, and return related Subscriber.
Here are simplified class examples:
public class Subscriber
{
public string Email { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public List<Subscription> Subscriptions { get; set; }
}
public class Subscription
{
public Guid Id { get; set; }
public string EventType { get; set; }
}
I've tried to make an index, as it is said in RavenDB docs:
public class Subscriber_BySubscription : AbstractIndexCreationTask<Subscriber>
{
public Subscriber_BySubscription()
{
Map = subscribers => from subscriber in subscribers
from subscription in subscriber.Subscriptions
select new
{
subscription.EventType,
subscription.QueueName
};
}
}
But I'm not sure that this is what I need, since query by collection using Select and Contains doesn't work. Moreover, the code looks so ugly that I feel that this is not the way how it should be.
So, I'd like to query Subscriptions by EventType, and have corresponding Subscriber as a result. In LINQ it would look like this: subscribers.Where(x => x.Subscriptions.Select(c => c.EventType).Contains(myEventType))
Managed to do it. Here is the right index:
public class Subscriber_BySubscription : AbstractIndexCreationTask<Subscriber>
{
public class Result
{
public string EventType { get; set; }
}
public Subscriber_BySubscription()
{
Map = subscribers => from subscriber in subscribers
from subscription in subscriber.Subscriptions
select new
{
subscription.EventType
};
}
}
And that's how it should be used:
var results = uow.Session
.Query<Subscriber_BySubscription.Result, Subscriber_BySubscription>()
.Where(x => x.EventType == eventType)
.OfType<Subscriber>()
.ToList();

Conditional Restrictions in Fluent NHibernate using Query Object Pattern

I'm a complete noob to Fluent NHibernate, and I'm using the Query Object Pattern based on a recommendation. Which I'm also new to. I'll try to keep the code samples concise and helpful.
User class:
public class User {
public Guid ID { get; set; }
public string Name { get; set; }
}
Visibility:
public enum VisibilityType {
Anybody,
OwnersOnly,
Nobody
}
Car class:
public class Car {
public Guid ID { get; set; }
public VisibilityType Visibility { get; set; }
public ICollection<User> Owners { get; set; }
}
So I need to write a conditional restriction method for the query object. Return all cars that have VisibilityType.Public, but if a car has Visibility property value of VisibilityType.OwnersOnly, restrict the return to users who belong to that group.
Here's the current restriction method that I have working, but without the condition:
public class CarQueryObject
{
private User user { get; set; }
private const string OwnersProperty = "Owners";
private const string OwnersIDProperty = "Owners.ID";
public CarQueryObject RestrictToOwners()
{
// How do I add a conditional criteria here? Only restrict by owner
// if the QueryObject has VisibilityType.OwnersOnly? Note that it should
// *NOT* restrict VisibilityType.Anybody
CreateOwnersAlias();
Criteria.Add(Restrictions.Eq(OwnersIDProperty, user.Id));
return this;
}
public CarQueryObject JoinFetchOwned()
{
Criteria.SetFetchMode(OwnersProperty, FetchMode.Join);
return this;
}
public void CreateOwnersAlias()
{
Criteria.CreateAlias(OwnersProperty, OwnersProperty, JoinType.LeftOuterJoin);
JoinFetchOwned();
}
}
?_?
an idea to get shown cars
var carsShown = session.CreateCriteria<Car>()
.JoinAlias("Owner", "owner")
.Add(Expressions.Or(
Expression.Eq("Visibility", Visibility.Anybody),
Expression.Eq("Visibility", Visibility.OwnersOnly) && Expression.Eq("owner.Id", currentUser.Id)
))
.List<Car>();

RavenDB Include though collection

I'm struggling with the include function in RavenDB. In my model I have a blog post that holds a list of comments. The comment class that's used in this list holds a reference to a user.
public class BlogPost
{
public string Id { get; set; }
public List<Comment> Comments { get; set; }
public BlogPost()
{
Comments = new List<Comment>();
}
}
public class Comment
{
public string Id { get; set; }
public string Text { get; set; }
public string UserId { get; set; }
}
What I want to do is to get the blogpost and get a list of comments with the details of the user that wrote the comment to display in the UI, without querying the server for every single user (N+1).
I would be happy with some pointers on how to solve this. Thanks!
You can do that using something like:
session.Include<BlogPost>(b=>b.Comments.Select(x=>x.UserId)).Load(1);
I think this page would answer your question.
You can load multiple documents at once:
var blogSpots = session.Include<BlogPost>(x => x.Comments.Select(x=>x.UserId))
.Load("blogspot/1234", "blogspot/4321");
foreach (var blogSpot in blogSpots)
{
foreach (var userId in blogSpot)
// this will not require querying the server!!!
var cust = session.Load<User>(userId);
}

RavenDb Select() downcasts instead of selecting the neccessary fields

public class PersonBrief
{
public int Id { get; set; }
public string Picture { get; set; }
public PersonBrief(Person person)
{
Id = person.Id;
Picture = person.Picture;
}
}
public class Person : PersonBrief
{
public string FullName { get; set; }
}
var results = session.Query<Person>()
.Select(x => new PersonBrief(x))
.ToList();
Assert.IsNull(results[0] as Person); // Fails
Is this a bug? If not, what would be the correct way to select only the fields i'm interested in?
It would work if you move the .ToList before the .Select, but that would be doing the work on the client.
If you want to do it on the server, you need to use As in your query, and you need a static index that does a TransformResults. See these docs.