How to search with sub collections properties in RavenDB client api - ravendb

As the title says I would like to know how can use the Session.Query.Search(...) from RavenDB C# client API to do a full text search in property of a collection which is a property of an entity.
public class Order
{
public List<Product> Products { get; set; }
}
public class Product
{
public string Name { get; set; }
}
In pure RQL (raven query language) the query would be like this:
from Orders where search(Products.Name, '*alice*')

From the client api,
use a static index to define which document field will be searchable.
After that, you can query this indexed field and get the documents that contain the term you search for in that indexed field.
See example in:
https://demo.ravendb.net/demos/csharp/text-search/fts-with-static-index-single-field

Related

Returning a document and its path in a hierarchy using RavenDB

I am using RavenDB for the first time as a database for a website. I am just starting out and thinking about how to represent the page website hierarchy in the database. I read this article Modelling hierarchical data with RavenDB and it shows a really neat way of storing a hierarchy in a document database and hence I am running with this design.
So I have my Page document
public class Page
{
public string Id { get; set; }
public string Slug { get; set; }
}
and my PagesHierarchy document.
public class PagesHierarchyTree
{
public class Node
{
public string PageId { get; set; }
public List<Node> Children { get; set; }
}
public List<Node> RootPages { get; set; }
}
The idea is to have the PagesHierarchyTree represent the tree and this document has reference id's to the actual documents.
So, now to my question. I want to create an index where I can find a document (page) based on the slug but also return the slug path i.e a/b/c based on where the document lives in the tree.
I read about Indexing Hierarchical Data and Indexing Related Documents but i`m struggling to bring them together.
Can someone help me with this or point me in the right direction?
I got my answer from the ravendb google groups forum found here.

Inheriting data from ancestor documents in RavenDB

I'm using RavenDB to store three types of curriculum: Units, Lessons, and Activities. These three types all inherit from CurriculumBase:
public abstract class CurriculumBase
{
public string Id { get; set; }
public string Title { get; set; }
public List<string> SubjectAreaIds { get; set; }
// non-relevant properties removed
}
These documents have a hierarchical relationship, so I've modeled the hierarchy as a separate single document as recommended here: Modelling Hierarchical Data with RavenDB
public class CurriculumHierarchy
{
public class Node
{
public string CurriculumId { get; set; }
public string Title { get; set; }
public List<Node> Children { get; set; }
public Node()
{
Children = new List<Node>();
}
}
public List<Node> RootCurriculum { get; set; }
public CurriculumHierarchy()
{
RootCurriculum = new List<Node>();
}
}
I need to be able to do searches across all curriculum documents. For simple properties, that seems easy enough to do with a multi-map index.
However one of the properties I need to be able to search by (in combination with the other search criteria) is SubjectAreaId. I need to be able to get curriculum for which it or any of its ancestors have the specified subject area id(s). In other words, for search purposes, documents should inherit the subjectAreaIds of their ancestors.
I've considered de-normalizing subjectAreaIds, and storing the full calculated set of subjectAreaIds in each document, but that will require updates whenever the hierarchy itself or the subjectAreaIds of any of a given document's ancestors change. I'm hoping this is something I can accomplish with an index, or perhaps an entirely different approach is needed.
You can use LoadDocument to load the parents during indexing.
http://ravendb.net/docs/article-page/3.0/csharp/indexes/indexing-related-documents
The main challenge I encountered was that I had written code in CurriculumHierarchy to get a document's ancestors, but this code isn't executable during indexing.
To solve this, I added a read-only property to CurriculumHierarchy which generates a dictionary of ancestors for each document:
public Dictionary<string, IEnumerable<string>> AncestorLookup
{
get
{
// Not shown: build a dictionary where the key is an
// ID and the value is a list of the IDs for
// that item's ancestors
}
}
This dictionary is serialized by Raven and therefore available for indexing.
Then my index ended up looking like this:
public class Curriculum_Search : AbstractMultiMapIndexCreationTask
{
public Curriculum_Search()
{
AddMap<Activity>(
activities =>
from activity in activities
let hierarchy =
LoadDocument<CurriculumHierarchy>("curriculum_hierarchy")
let ancestors =
LoadDocument<CurriculumBase>(hierarchy.AncestorLookup[activity.Id])
select new
{
subjectAreaIds = ancestors.SelectMany(x => x.SubjectAreaIds).Distinct().Union(activity.SubjectAreaIds),
});
// Not shown: Similar AddMap statements for Lessons and Units
}
}
I was a bit concerned about performance, but since there are less than 2000 total curriculum documents, this seems to perform acceptably.

asp.net web api controllers accept POCO or generic data

I am new with ASP.NET Web API and have been researching this for some time now. Admittedly, I have decision paralysis. I want to make a REST-like API for a system with about 250 tables in the database. It's basically a 2 tier system with a UI and a data access layer, not using business objects or ORM.
I cannot decide if my Web API Controllers should accept/return:
a) IDictionary of name/value pairs, which I would package into sql parameters and pass to the data access layer and return a serialized ado.net data table
b) strongly typed complex object (POCO objects). For example: Account class with all properties matching up with fields in the database.
If I have to create POCO classes for every table in the system, there would be 250+ classes that essentially do nothing except package the data and pass it to our data access layer.
Further, it seems as if I need to create an ApiController for basically every table in the database that I want to expose via the Web Api because you only have GET, POST, PUT, DELETE per route? Please help, banging head on desk.
Please see answers below:
1.**Using **"IDictionary of name/value pairs" is fine if your resource supports GET methods only. If you want users to post or update data, how will you validate the data? In addition, if you want to add HATEOAS, how would you do that? In terms of extension, how would you support nested object hierarchy like the one below:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
public IList<PurchaseDetail> PurchaseHistory { get; set; }
}
public class PurchaseDetail
{
public int Id { get; set; }
public DateTime PurchaseDate { get; set; }
public decimal Cost { get; set; }
}
2. You can have more than one GET, POST,etc per resources by defining different routes. More from this link http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

What are the steps for building a faceted search solution with RavenDB?

I need to build a new search solution for our website that will allow users to quickly locate products that match their needs. We want to use a faceted "Amazon.com" type of search and I want to implement using RavenDB. Can anyone help me with defining at a high-level what the steps are to accomplish this from beginning to end. I have already figured out a few of them and I have also determined that I am going to have to make use of Dynamic Fields. So here are the steps that I know of. Any advice/direction/clarification would be greatly appreciated.
Download and install RavenDB
Setup the RavenDB Server (I have chosen to run under IIS)
Create object(s) to store data. I know I am going to need to make use of Dynamic Fields since products can have different attributes. My current objects look like this:
public class ProductSeries
{
public Guid UID { get; set; }
public String SeriesName { get; set; }
public String SeriesDescription { get; set; }
public String ProductIDInCMS { get; set; }
public List<ProductAttribute> Attributes { get; set; }
}
public class ProductAttribute
{
public string Attribute { get; set; }
public string Value { get; set; }
}
Store documents in database.
Create indexes??? Here's where I get lost. Is this the next step?
...
???
Ultimately, I know I will conclude with creating some sort of AJAX enabled control that will display facets with their values and counts and allow users to make selections that will filter our thousands of products down to a select few that meet their criteria. I think I know where I start and where I finish; it's what happens in between that has me stumped.
Did you read the docs about this?
They explain all of it: http://ravendb.net/docs/2.5/client-api/faceted-search

RavenDB Modeling - a single document vs multiple documents?

Given a simple example as follows, I'd like some guidance on whether to store as a single document vs multiple documents.
class User
{
public string Id;
public string UserName;
public List<Post> Posts;
}
class Post
{
public string Id;
public string Content;
}
Once the data is stored, there are times when I will want all the posts for a given user. Sometimes I might want posts across multiple users that meet a particular criteria.
Should I store each User as a document (with Posts embedded), or does it make more sense to store Users and Posts as seperate documents, and have some sort of ID in my post to link it back to a User?
Now, what if each user belonged to an Organization (there will be hundreds of organizations in my application)?
class Organization
{
public string Id;
public List<User> users;
}
Should I then stay with the single document approach? In this case I would store one giant document for each organization, which will contain embedded users, which in turn contain embedded posts?
You should keep them as separate documents. User, Organization, and Post are great examples of aggregate entities, and in Raven, each aggregate is usually its own document.
Only entities which are not aggregates should be nested in the same document. For example, in Post you might have a List<Comment>. Comment and Post are both entities, but only Post is an aggregate.
You should instead model them with references:
public class User
{
public string Id { get; set; }
public string Name { get; set; }
public List<string> PostIds { get; set; }
}
public class Post
{
public string Id { get; set; }
public string Content { get; set; }
}
public class Organization
{
public string Id { get; set; }
public List<string> UserIds { get; set; }
}
Optionally, you can denormalize some of the data into your references where appropriate:
public class UserRef
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Organization
{
public string Id { get; set; }
public List<UserRef> Users { get; set; }
}
Denormalizing the user's name into the organization document has the benefit of not needing to fetch each user document when displaying the organization. However, it has the drawback of having to update the organization document any time a user's name is changed. You should weigh the pros and cons of this each time you consider a relationship. There is no one right answer for all cases.
Also, you should be considering how data will be really used. In practice, you will probably find that your Organization class may not need a user list at all. Instead, you could put a string OrganizationId property on the User class. That would be easier to maintain, and if you wanted a list of users in an organization, you could query for that information using an index.
You should read more in the raven documentation on Document Structure Design and Handling Document Relationships.