Index with two collections - ravendb

I am struggling creating an simple index with ravendb.
Given are two document collections:
User (34000 docs) and
BlogEntries (1.5 million docs)
1) How can I create an index that shows the count of blog entries for each user?
The collections relationship is as following:
User.LastName + "," + User.FirstName = Blog.CreatedBy
Important is the fact that BlogEntries contains old entries that are not related to the user collection. I want to filter those entries out, so that they are not appear in the index. That's why I need the user collection here.
Sample Data:
User Collection:
User U1
User U2
BlogEntry Collection:
BlogEntry B1 -> U1
BlogEntry B2 -> U1
BlogEntry B3 -> U2
BlogEntry B4 -> XYZ1
BlogEntry B5 -> XYZ2
BlogEntry B6 -> U1
I want to filter out the B4 and B5 entries, cause they are not related to a user in the user collection.
2) Do I have to use a multimap index for that?
3) I already tried the following via the management studio, but the index does not work.
Seems I cannot use two document collections in a single map block.
Map:
from user in docs.Users
from blog in docs.Blogs
where blog.CreatedBy = user.LastName + "," + user.FirstName
select new { UserName = user.LastName ..., Count = 1 }
Reduce:
from result in results group by result.UserName
into g
select new { User = g.Key, g.Sum( x => x.Count) }
Thanks,
Marius

With the changed requirement I guess you need a multi map index:
AddMap<User>(users => from user in users
select new
{
UserName = user.LastName + "," + user.FirstName,
HasUser = true,
Count = 0
});
AddMap<BlogEntry>(blogEntries => from blogEntry in blogEntries
select new
{
UserName = blogEntry.CreatedBy,
HasUser = false,
Count = 1
});
Reduce = results => from result in results
group result by result.UserName
into g
select new
{
UserName = g.Key,
HasUser = g.Any(x => x.HasUser),
Count = g.Sum(x => x.Count)
};
You can filter the index by the HasUser property.

Related

LinqToSql OrderBy has no Effect

I am using a LinqToSql-DataSource for a GridView in this way:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
vorname = person.vorname,
nachname = person.nachname,
nameVerein = institution.name,
vereinid = mitgliedschaft.verein_id,
id = mitgliedschaft.id,
verbandsMitgliedsNummer = person.verbandsMitgliedsNummer,
strasse = person.strasse,
plz = person.plz,
ort = person.ort,
geburtsdatum = person.geburtsdatum,
geschlechtid = person.geschlechtid,
statusid = mitgliedschaft.statusid,
bezirk_id = mitgliedschaft.bezirk_id,
kreis_id = mitgliedschaft.kreis_id,
person_id = mitgliedschaft.person_id.Value,
deletedFlag = mitgliedschaft.deletedFlag,
stammverein = mitgliedschaft.stammVerein,
eintrittsdatum = mitgliedschaft.eintritt
}).GroupBy(p => p.person_id).Select(p => p.First());
}
Now i want to order the Selection. At first the "stammVerein"-Column of Table "mitgliedschaft" descending AND the Column "eintritt" of Table "mitgliedschaft". I have tried several ways:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
orderby mitgliedschaft.stammVerein descending, mitgliedschaft.eintritt
select new
{
...
}).GroupBy(p => p.person_id).Select(p => p.First());
}
AND:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
...
}).GroupBy(p => p.person_id).Select(p => p.First()).OrderByDescending(stamm => stamm.stammverein).ThenBy(eintritt => eintritt.eintrittsdatum);
}
AND:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
....
}).OrderByDescending(stamm => stamm.stammverein).ThenBy(eintritt => eintritt.eintrittsdatum).GroupBy(p => p.person_id).Select(p => p.First());
But nothing of this has any Effects ! I am very new in this kind of DataSource and Linq.
Can anyone help me achieving this order ?
Items within a grouped result will not retain their order. Depending on how you want to factor in the ordering, you will need to do it after the group by, and before, and/or after your First...
To accomplish this, it will be easiest if you map the relationships in EF with navigation properties rather than substituting SQL with Linq QL (joins and such)
Using the following base query:
var query = db.mitgliedschaft
.GroupBy(m => m.Person); // Group by related entity, not ID
For instance, after the group by, you will have sets of records grouped by Person. If you want the first Person with an earliest related record:
var result = query.OrderByDescending(g => g.Key.mitgliedschafts.Max(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.First();
This is taking a wild guess at your schema & entity relationships, but hopefully it will help you work out something that fits. I can only guess at what eintritt is and how it relates to your entity model.
The initial query takes just your base entities that you want to group, and groups them by the related entity. The result of that grouping will be a set of Grouped mitgliedschafts with a key being the Person. To Order those groups by the person with the most recent mitgliedschafts we use an orderby on the Key's associated mitgliedschafts using the Max value for the collection given a descending order request.
The First then gives us the first grouped collection of mitgliedschafts.
Then if you want to sort the resulting list of mitgliedschafts after getting the person with the most recent one:
var result = query.OrderByDescending(g => g.Key.mitgliedschafts.Max(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.First().OrderByDescending(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.ToList();
The 2nd set of OrderBy clauses apply to the selected group, or the mitgliedschafts.
To compose the desired view model, Insert a Select() to build the view model from the mitgliedschafts before the ToList().
With the navigation properties this can probably be done without resorting to a group by. On a hunch, something like this should return something similar:
var query = db.Person
.OrderByDescending(p => p.mitgliedschafts.Max(stamm => stamm.stammverien))
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.SelectMany(p => p.mitgliedschafts)
.OrderByDescending(stamm => stamm.stammverien)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.Select(stamm => new { ... })
.ToList();
Anyhow, hopefully that gives you some ideas on things to try if you have the navigation properties mapped or can set those up.

Ravendb query using a list as criteria

Please could someone help with this query. I'm trying to get a list returned from an index where the id matches any id in a given list. I've tried:
var members = session.Query<MembersNameIdIndex.Result, MembersNameIdIndex>()
.Where(x => list.Any(y => y == x.Id))
.AsProjection<MembersNameIdIndex.Result>()
.ToList();
and
var members = from m in session.Query<MembersNameIdIndex.Result, MembersNameIdIndex>()
where list.Any(y => y == m.Id)
select m;
var projection = members.AsProjection<MembersNameIdIndex.Result>()
.ToList();
and
var members = from m in session.Query<MembersNameIdIndex.Result, MembersNameIdIndex>()
where list.Contains(m.Id)
select m;
var projection = members.AsProjection<MembersNameIdIndex.Result>()
.ToList();
The error is always the same: Expression type not supported: System.Linq.Expressions.TypedParameterExpression
(For the last 2, it fails on the projection part.)
This should work:
var members = from m in session.Query<MembersNameIdIndex.Result, MembersNameIdIndex>()
where m.Id.In(list)
select m;
var projection = members.AsProjection<MembersNameIdIndex.Result>()
.ToList();

Combine two queries in linq

I have two linq queries as follows:
GroupNamesWithCorrespondingEffects
= new ObservableCollection<GroupNameWithCorrespondingEffect>(
from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}
);
GroupNamesWithCorrespondingEffects
= new ObservableCollection<GroupNameWithCorrespondingEffect>
(GroupNamesWithCorrespondingEffects.
Where(u => !GetAllChildren(25).
Select(x => x.GroupID).
Contains(u.GroupID)).ToList());
Now how can I combine these two queries?
You can pass directly this to the constructor of the ObservableCollection:
from g in groups
let g = select new GroupNameWithCorrespondingEffect
{
GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}
where !GetAllChildren(25)
.Select(x => x.GroupID)
.Contains(g.GroupID)
select g
I'm not sure if EF is able to compose the first and the second part (I can't remember from the top of my head if Contains is resolved in an IN clause, my EF is a bit rusty), but you were not doing that anyway, so the effect is the same as yours. If it is able to compose, then this way you are getting a more efficient execution.
If you don't mind mixing SQL-style and extension method syntax, you can do this:
GroupNamesWithCorrespondingEffects
= new ObservableCollection<GroupNameWithCorrespondingEffect>(
(from g in groups
select new GroupNameWithCorrespondingEffect
{ GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
})
.Where(u => !GetAllChildren(25)
.Select(x => x.GroupID)
.Contains(u.GroupID))
.ToList());

How to use sql query in nhibernate ConfORM

How can I join two tables from different dataases using nhibernate ConfORM or at least write sql query in nhibernate ConfORM?
This is the query which I need to run:
select RTRIM(l.descr) as affiliation, a.LocationId
from Facilities a
join [tmt-sam2].sammi.dbo.location l ON a.LocationId = l.off_code+'-'+l.location
Thanks,
Alexey
if you dont have that many locations you can strait load all
using (var session1 = sessionfactoryDataBase1.OpenSession())
using (var session2 = sessionfactory_tmt_sam2.OpenSession())
{
var locations = session2.QueryOver<Location>().List();
var results = session1.QueryOver<Facility>()
.Where(f => f.LocationId.IsIn(locations.Select(l => l.OffCode + '-' + l.location)))
.AsEnumerable()
.Join(locations, f => f.LocationId, l => l.OffCode + '-' + l.location, (f, l) => new { Description = l.descr.TrimEnd(), LocationId = f.LocationId });
}
otherwise batches in the code

Can LINQ2SQL return a few columns or is it entity or single column only?

From what I understand, using something like nHibernate will return either a single result like int (say returning a count) but it can't return say 2 columns or 3 columns from the users table.
Is linq the same way?
e.g. say I have the Users table with columns: UserID, username, password, email, datecreated)
Could it return UserID, password ONLY, or it can only return the entire row/entity/class?
thanks for clearing this up.
You can retrieve only some columns by returning the result as an anonymous type. That can look like this:
var selectedUserId = 19;
var userInfo = from u in context.Users
where u.UserId == selectedUserId
select new { u.UserId, u.Password };
It can also look like this, if you don't use query syntax:
var selectedUserId = 19;
var userInfo = context.Users
.Where(u => u.UserId == selectedUserId)
.Select(u => new { u.UserId, u.Password });
That will return an IQueryable instance, you can add a call to .SingleOrDefault() or .FirstOrDefault() at the end to get back a single object of your anonymous type.
Yes it can be done with Linq to SQL, as bdukes and pcampbell has shown, but it can be done with NHibernate as well.
For example you could execute the following query, which would return a list of object[]:
var userData = session.CreateQuery(
"select u.Username, u.Password from User u").List();
If you want to you could select only some of the fields, but still get back an entity, so that you don't have to mess with object arrays. This can be done with HQL and Critera as well.
For example, if the user class has a constructor that takes a username and password:
var users = session.CreateQuery(
"select new User(u.Username, u.Password) from User u").List<User>();
NHibernate can do this too:
var selectedUserId = 19;
var userInfo = from u in session.Linq<Users>()
where u.UserId == selectedUserId
select new { Id = u.UserId, Password = u.Password };
What it can't do is return a User entity with lazily loaded columns. I have no idea if Linq to SQL can lazy load individual columns.
What this is doing is a projection.
Absolutely it can do what you like. Try and return those fields into an anonymous type. For more info, check out this article by Scott Guthrie on Anonymous Types. Here's an example:
var twoFields = from c in db.Customers
where c.ID == someCustomerID
select new { ID = c.ID, Name = c.Name}
//use your new object called twoFields
Console.WriteLine(twoFields.Name);