I'm trying to create a composite index using RavenDB. Basically, I have 2 models that I need "joined" and able to perform searches on the result.
Here's an example of my models:
Model 1:
public class UserProfile {
public string ProfileId { get; set }
public string FirstName { get; set }
public string LastName { get; set }
public string EmailAddress { get; set }
}
Model 2:
public class UserProjects {
public string UserProjectId { get; set }
public List<Project> Projects { get; set }
}
Here's the Project Model, in case you're wondering:
public class Project {
public string ProjectId { get; set; }
public string Name { get; set; }
}
The UserProjectId property is as follows: users/<Email Address>/projects.
I was able to create an index that returns all the fields, but I'm not able to search on certain fields.
Here's my index:
public class ProfileProjectIndex : AbstractIndexCreationTask<UserProfile> {
Map = profiles =>
from profile in profiles
select new {
profile.ProfileId,
profile.FirstName,
profile.LastName,
profile.EmailAddress
};
TransformResults = (db, results) =>
from result in results
let project = db.Load<UserProjects>("users/" + profile.EmailAddress + "/projects")
select new {
result.ProfileId,
result.EmailAddress,
result.FirstName,
result.LastName,
project.UserProjectId,
project.Projects
};
}
Now this index, when querying Raven from the web interface, returns the complete list of data I could want, but I need to be able to filter the results based on some values included in the UserProjects.Projects list, for example:
return all user profiles with associated projects when Project.Projects.Contains(x => x.ProjectId == "projects/1234")
Any RavenDB gurus can enlighten me? Oh, and I'm using RavenDB 2.5.
You are probably looking at multi map indexes, see:
http://ravendb.net/docs/article-page/3.0/csharp/indexes/multi-map-indexes
Related
I'm currently using MVC with EF to have a small server with API querying a SQL database. But in the API reply I'm not able to hide some parameters.
The main object
public class AssetItem
{
[Key]
public Int32 AssetId { get; set; }
public String AssetName { get; set; }
public int OdForeignKey { get; set; }
[ForeignKey("OdForeignKey")]
public OperationalDataItem OperationalDataItem { get; set; }
}
The other one:
public class OperationalDataItem
{
[Key]
public Int32 OperationalDataId { get; set; }
public String Comunity { get; set; }
public List<AssetItem> AssetItems { get; set; }
}
From what I have read, this should be ok, I have also set the context:
public AssetContext(DbContextOptions<AssetContext> options) : base(options)
{}
public DbSet<AssetItem> AssetItems { get; set; }
public DbSet<OperationalDataItem> OperationalDataItems { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AssetItem>().HasOne(p =>
p.OperationalDataItem).WithMany(b => b.AssetItems).HasForeignKey(p =>
p.OdForeignKey);
}
And the seeding in program.cs
context.AssetItems.Add(
new AssetItem { AssetName = "Test test", OdForeignKey = 1,
OperationalDataItem =
new OperationalDataItem {Comunity = "Comunity1" }});
So calling the API this results in:
{ "assetId":3,
"assetName":"Test test",
"odForeignKey":1,
"operationalDataItem":null }
From what I read this is because of the lazy loading, how can I hide the result operationalDataItem?
In case is not possible i have of course try to query for it and give it back and it give something like:
{ "assetId":3,
"assetName":"Test test",
"odForeignKey":1,
"operationalDataItem":
{ "operationalDataId":1,
"comunity":"Comunity1",
"assetItems":[
But in this case I would like to hide "assetsItems" in the reply to the FE.
How can I hide those parameters?
The API is quite simple, just an example code:
var todoItem = await _context.AssetItems.FindAsync((Int32)id);
var item = _context.OperationalDataItems.Find((Int32)todoItem.OdForeignKey);
todoItem.OperationalDataItem = item;
return todoItem
If you want to fetch data from the database, but you only want to fetch some properties, use Select. Usually this is more efficient than using Find, because you'll only transfer the data that you actually plan to use.
To fetch some properties of the assetItem that has primary key assetItemId:
var result = dbContext.AssetItems
.Where(assetItem => assetItem.AssetItmId = assetItemId)
.Select(assetItem => new
{
// Select only the properties that you plan to use
Id = assetItem.AssertItemId,
Name = assetItem.Name,
OperationalData = new
{
// again, select only the properties that you plan to use
Id = assetItem.OperationalData.OperationalDataId,
Community = assetItem.OperationalData.Community,
},
})
.FirstOrDefault();
Or the other way round:
Fetch several properties of all (or some) OperationalDataItems, each with some properties of all (or some) of its AssetItems:
var result = dbContext.OperqationalDataItems
.Where(operationalDataItem => ...) // only if you don't want all
.Select(operationalDataItem => new
{
Id = operationalDataItem.Id,
Community = operationalDataItem.Community
AssetItems = operationalDataItem.AssetItems
.Where(assetItem => ...) // only if you don't want all its assetItems
.Select(assetItem => new
{
// Select only the properties you plan to use:
Id = assetItem.Id,
...
// not useful: you know the value of the foreign key:
// OperationalDataId = assetItem.OperationalDataId,
})
.ToList();
})
.ToList(); // or: FirstOrDefault if you expect only one element
Entity framework knows your one-to-many relation and is smart enough to know which (group-)join is needed for your query.
Some side remarks
You've declare your many-relation a List<AssetItem>. Are you sure that operationalDataItem.AssetItems[4] has a defined meaning? Wouldn't it be better to stick to the entity framework code first conventions? This would also eliminate the need for most attributes and / or fluent API
public class OperationalDataItem
{
public int Id { get; set; }
public String Comunity { get; set; }
...
// Every OperationalDataItem has zero or more AssetItems (one-to-many)
public virtual ICollection<AssetItem> AssetItems { get; set; }
}
public class AssetItem
{
public int Id { get; set; }
public String Name { get; set; }
...
// every AssetItem belongs to exactly one OperationalDataItem, using foreign key
public int OperationDataItemId { get; set; }
public virtual OperationalDataItem OperationalDataItem { get; set; }
}
In entity framework the columns of a table are represented by the non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many)
Because I stuck to the conventions, no attributes nor fluent API is needed. Entity framework is able to detect the one-to-many relation and the primary and foreign keys. Only if I am not satisfied with the names or the types of the columns I would need fluent API.
I have a model in Entity Framework Core that goes something like this:
public class Anime
{
public int EpisodeCount { get { return Episodes.Count() } }
public virtual ICollection<Episode> Episodes { get; set; }
}
I'm having the issue of EpisodeCount being 0. The solution currently is to run a .Include(x => x.Episodes) within my EF query, but that loads the entire collection of episodes where it's not needed. This also increases my HTTP request time, from 100ms to 700ms which is just not good.
I'm not willing to sacrifice time for simple details, so is there a solution where I can have EF only query the COUNT of the episodes, without loading the entire collection in?
I was suggested to do this
var animeList = context.Anime.ToPagedList(1, 20);
animeList.ForEach(x => x.EpisodeCount = x.Episodes.Count());
return Json(animeList);
but this also returns 0 in EpisodeCount, so it's not a feasible solution.
You need to project the desired data into a special class (a.k.a. ViewModel, DTO etc.). Unfortunately (or not?), in order to avoid N + 1 queries the projection must not only include the count, but all other fields as well.
For instance:
Model:
public class Anime
{
public int Id { get; set; }
public string Name { get; set; }
// other properties...
public virtual ICollection<Episode> Episodes { get; set; }
}
ViewModel / DTO:
public class AnimeInfo
{
public int Id { get; set; }
public string Name { get; set; }
// other properties...
public int EpisodeCount { get; set; }
}
Then the following code:
var animeList = db.Anime.Select(a => new AnimeInfo
{
Id = a.Id,
Name = a.Name,
EpisodeCount = a.Episodes.Count()
})
.ToList();
produces the following single SQL query:
SELECT [a].[Id], [a].[Name], (
SELECT COUNT(*)
FROM [Episode] AS [e]
WHERE [a].[Id] = [e].[AnimeId]
) AS [EpisodeCount]
FROM [Anime] AS [a]
Is it possible to load only a part of a Ravendb document.
I want to fetch a document by id, but only some fields should be fetched.
I know I can use session.Query with a Selectcall, but then I can't query on the id of the document so I have to use session.Loadinstead, but this fetches the whole document.
Do I need to create an index for this?
You can use something called Results Transformers to achieve this.
Say you have an entity "Customer"
public class Customer
{
public string Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
}
and you want to load only the Id and the Name property, you define a Results Transformer:
public class CustomerNameTransformer : AbstractTransformerCreationTask<Customer>
{
public CustomerNameTransformer()
{
TransformResults = results => from customer in results
select new CustomerNameViewModel
{
Id = customer.Id,
Name = customer.Name
};
}
}
and your "view model":
public class CustomerNameViewModel
{
public string Id { get; set; }
public string Name { get; set; }
}
With this, you can access the Customer entity as a "Customer Name ViewModel" in several ways:
//Load and transform by one id
CustomerNameViewModel viewModel = session.Load<CustomerNameTransformer, CustomerNameViewModel>("customers/1");
//Load and transform by several id's
CustomerNameViewModel[] viewModels = session.Load<CustomerNameTransformer, CustomerNameViewModel>(new[]{ "customers/1", "customers/2"});
//Query and transform
List<CustomerNameViewModel> viewModels = session.Query<Customer>()
.TransformWith<CustomerNameTransformer, CustomerNameViewModel>()
.ToList();
Results Transformers are executed server side before the data is returned to the client. They are created by the same IndexCreation task that creates the index definitions on the server.
You can read more about Results Transformers in the documentation:
http://ravendb.net/docs/article-page/2.5/csharp/client-api/querying/results-transformation/result-transformers
Hope this helps!
I have a table with these columns(type) as described below.
TABLE
------------------------------------------------------------------
Dir(str) | Twnshp(int) | Rng(int) | Section(int) | Xcell(int) | Ycell(int)
------------------------------------------------------------------
I am trying to do this query using EF.
SELECT Xcell,Ycell FROM [CIR].[dbo].[TRS2Cell] where Twnshp = 1 and Rng = 4 and Section =31
After some study, I created a DAL Context and class as below.
PlotXYContext.cs
public class PlotXYContext :DbContext
{
public DbSet<PlotXY> XYCells { get; set; }
}
PlotXY.cs
[Table("TRS2Cell")]
public class PlotXY
{
public string Dir { get; set; }
[Key]
public int Twnshp { get; set; }
public int Rng { get; set; }
public int Section { get; set; }
public int Xcell { get; set; }
public int Ycell { get; set; }
}
Here is the code in my controller where I pass the three parameters.
PlotXYContext plotXYContext = new PlotXYContext();
var query = from TRS2Cell in plotXYContext.XYCells
where TRS2Cell.Twnshp == 1
&& TRS2Cell.Rng == 4
&& TRS2Cell.Section == 31
select TRS2Cell.Xcell;
I need help with EF as I am new to it and also is this the right query?
If so how do I retrieve the Xcell and Ycell values from the query.
Also the table has no unique column, no nulls, nothing needs to be updated here. All I want is to do a select.
Normally your not gonna want to do any data access code in your controller. You want to keep those separated.
Also when I first started using EF i got hung up on DB Context as well when I started with MVC. If you added your Ado.Net Entity Data Model correctly the db context should be automatically created for you. If you look at "YourEntity".cs file under "Entity".edmx => "Entity".Context.tt it will look something like
public partial class VuittonEntities : DbContext
{
public VuittonEntities()
: base("name=VuittonEntities")
{
}
To help you out with EF I'm gonna post all my code for a query.
So your Model class in the models folder will look like.
public class RoleGridViewModel
{
public int UserID { get; set; }
public string UserFirst { get; set; }
public string UserLast { get; set; }
public string UserRole { get; set; }
public string UserRoleDesc { get; set; }
}
This is your Data Access layer function: Here I'm creating a list of my model class because I'm gonna populate it in a gridview later on.
public List<RoleGridViewModel> GridRoles()
{
using (VuittonEntities db = new VuittonEntities())
{
return (from users in db.User
join roles in db.UserRole on users.RoleID equals roles.RoleID
select new RoleGridViewModel
{
UserID = users.UserID,
UserFirst = users.FirstName,
UserLast = users.LastName,
UserRole = roles.Role,
UserRoleDesc = roles.Role_Desc
}).ToList();
}
}
Here in your Controller you can call it like this. Normally you would call a businezz layer from your controller I'm going straight to the Data layer to show you how its done. Here var roles holds your query. I'm using Json result here but this can also be done in an action result
public JsonResult RolesGrid()
{
var roles = new UserDAL().GridRoles();
return Json(roles, JsonRequestBehavior.AllowGet);
}
If you just want to select a single item you have to use .First() at the end of the query like this...
public string currentRole(UserViewModel uvm)
{
using (VuittonEntities db = new VuittonEntities())
{
return (from us in db.User
join usRole in db.UserRole on us.RoleID equals usRole.RoleID
where (us.RoleID == uvm.RoleID) && (us.UserID == uvm.UserID)
select usRole.Role).First();
}
}
I found that I wasn't using the similar datatype as in my table to declare the class for it.Thant is the only issue I came across that resolved it and hence the error.
Thank you for all the replies.
I'm new to RaveDB (Actually I've started learn it only yesterday). And try to implement some functionality.
Lets we have next class hierarchy:
public abstract class Transaction
{
public string Id { get; set; }
}
public class CategoryTransaction : Transaction
{
public string AccountId { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public decimal Amount { get; set; }
}
public class ExchangeTransaction : Transaction
{
public string DebitAccountId { get; set; }
public string CreditAccountId { get; set; }
public decimal DebitAmount { get; set; }
public decimal CreditAmount { get; set; }
}
Everything storing excelent int db. I event add Conventions = FindTypeTagName ... for store documents as 'transactions/*' document id.
But I want to create index for select all transactions for specific Account (by field AccountId). I've create index:
public class AccountTransactionResult
{
public string AccId { get; set; }
}
public class Account_Transactions : AbstractMultiMapIndexCreationTask<AccountTransactionResult>
{
public Account_Transactions()
{
this.AddMap<CategoryTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.AccountId
} );
this.AddMap<ExchangeTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.DebitAccountId
} );
this.AddMap<ExchangeTransaction>( transactions => from transaction in transactions
select new
{
AccId = transaction.CreditAccountId
} );
}
}
This index works well I can't get all type of transactions from DB (Exchange and Category) as single IEnumerable<Transaction>. That is great.
But I want to use the index to return transactions only for particular account. Because ExchangeTransaction can belong to two Accounts. I want to see ExchangeTransaction for both Accounts. I can make query in Raven Studio by AccId (index field) and it works greate! But I can't create the same request in the code :(.
Can someone help me? How can I use index field AccId in C# LINQ query?
This code
var query = session.Query<Transaction, Account_Transactions>()
return all type of transaction, but I don't how to filter the transactions by index field in the DB.
Thanks in advance. And please sorry me my English, it is not my native language.
My fault, in documentation everythings is clear. I missed some useful paragraph.
The easiest way to do this is by writing a multi-map index like this
one:
public class AnimalsIndex : AbstractMultiMapIndexCreationTask
{
public AnimalsIndex()
{
AddMap<Cat>(cats => from c in cats
select new { c.Name });
AddMap<Dog>(dogs => from d in dogs
select new { d.Name });
}
}
And query it like this:
var results = session.Advanced.LuceneQuery<object>("AnimalsIndex")
.WhereEquals("Name", "Mitzy");
>
In my case I should use next construction:
var transactions = session.Advanced.LuceneQuery<Transaction>( "Account/Transactions" )
.WhereEquals( "AccId", account2.Id )
And everything is working now.