Query inner records in ElasticSearch Nest C# - c#

I have structure like this:
public class OuterResource
{
public int Id { get; set; }
[Nested]
public List<InnerResource> InnerResources { get; set; }
}
public class InnerResource
{
public int Id { get; set; }
public int OuterResourceId { get; set; }
public int Value { get; set; }
}
Inner records are stored as lists inside outer records. I want to query inner records, giving specified outer resource id and value to filter inner records.
How to do it in C# Nest? Can't figure it out from documentation.

Please try this.
objforOuterResource.InnerResources.Where(x => x.Id == 5);

.Query<OuterResource>.Nested(n => n
.Path(p => p.InnerResources)
.Query(qq => qq
.Terms(t => t.Fields(fi => fi.InnerResources.First().OuterResourceId ).Terms(value))
)
)
Should work. Note the .First is applied to all the array (=list)

Related

Linq method - Return rows only found in another table

I want to return the Tags from the Tag table that are only found in the TagRecipe table. How can I do this?
var dataTags = await _context.Tags
.Include(tc => tc.TagCategory)
.ToListAsync();
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<TagRecipe> TagRecipes { get; set; }
public int TagCategoryID { get; set; }
public TagCategory TagCategory { get; set; }
}
public class TagRecipe
{
public int TagId { get; set; }
public int RecipeId { get; set; }
public Tag Tag { get; set; }
public Recipe Recipe { get; set; }
}
Thank you
Try this
var dataTags = await _context.TagRecipe
.Include(tc => tc.Tag.TagCategory)
.Select(i=> i.Tag)
.ToListAsync();
or you can use this syntax if you like it more
var dataTags = await _context.TagRecipe
.Include(t => t.Tag)
.ThenInclude(tc => tc.TagCategory)
.Select(i=> i.Tag)
.ToListAsync();
An alternative starting at table Tags using Join that will return a result without duplicates.
var dataTags = db.Tags
.Join(db.TagRecipes, tag => tag.Id, tagRecipe => tagRecipe.TagId, (tag, tagRecipe) => tag)
.Include(tag => tag.TagCategory)
.ToLookup(tag => tag.Id) // client-side from here
.Select(grouping => grouping.First()) // to make distinct
.ToList();
Will generate a straight-forward SQL
SELECT "t"."Id", "t"."Name", "t"."TagCategoryId", "t1"."Id", "t1"."Name"
FROM "Tags" AS "t"
INNER JOIN "TagRecipes" AS "t0" ON "t"."Id" = "t0"."TagId"
INNER JOIN "TagCategories" AS "t1" ON "t"."TagCategoryId" = "t1"."Id"
It is possible to use .Distinct in the above expression for removing duplicates instead of using grouping, but that will create a more complex SQL.
Table TagRecipes seems to be a join table in a many-to-many between table Tags and table Recipes. The latter is not included in the question, but I added it during my tests.
Please note that in EF Core 5, many-to-many relations may be created without an entity class for the join table.

Inner join to LINQ

I am new to Linq. For the learning perspective, I am trying to convert this following sql query to linq query. However, this gives me nothing. No error message or anything. The sql query gives me the table but linq doesn't give me values.
The SQL query:
SELECT
Members.FirstName,
Members.LastName,
PhoneScreens.BaselineEligibility
FROM
Members INNER JOIN PhoneScreens ON Members.Id = PhoneScreens.MemberId
WHERE PhoneScreens.BaselineEligibility = 'eligible'
And the Linq query is:
context.Members
.Include(p => p.PhoneScreens)
.Where(y => y.PhoneScreens.BaselineEligibility == "eligible")
.ToListAsync();
EDIT:
Here are the classes:
public class Member
{
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PhoneScreen> PhoneScreens { get; set; }
public Member()
{
PhoneScreens = new Collection<PhoneScreen>();
}
}
public class PhoneScreen
{
public string BaselineEligibility { get; set; }
public Member Member { get; set; }
public int MemberId { get; set; }
}
I would appreciate any help.
This will do the work:
context.Members
.Include(c => c.PhoneScreens)
.Where(m => m.PhoneScreens.Any(i => i.BaselineEligibility == "eligible"))

Get columns from Tables (Using Inner Join) with DetachedCriteria (prefereable) or QueryOver

I'm having a problem with my query in NHibernate With DetachedCriteria (or using Session QueryOver). I need to get only a few columns from each table, like the classes as follow:
public class PanelaCorrida : BaseEntity
{
public virtual Int64? CodCorrida { get; set; }
public virtual Int64 NumLocal { get; set; }
public virtual Boolean IdcInspecionada { get; set; }
public virtual String Sigla { get; set; }
public virtual String Rota { get; set; }
public virtual Local Local { get; set; }
public virtual Panela Panela { get; set; }
}
public class Panela : BaseEntity
{
public Panela()
{
this.Local = new Local();
this.PanelaCorridas = new List<PanelaCorrida>();
}
public virtual Int32 Numero { get; set; }
public virtual Boolean IdcAtiva { get; set; }
public virtual Int16 Status { get; set; }
public virtual ICollection<PanelaCorrida> PanelaCorridas { get; set; }
public virtual Local Local { get; set; }
#endregion
}
public class Local : BaseEntity
{
public Local()
{
this.PanelaCorridas = new List<PanelaCorrida>();
this.Panelas = new List<Panela>();
}
#region Property
public virtual Int32 IdLocal { get; set; }
public virtual Int32 AreaLocal { get; set; }
public virtual String Descricao { get; set; }
public virtual String Codigo { get; set; }
#endregion
}
Basically, the Entity 'Panela' is the Mother of 'PanelaCorrida' (Each Panela can have multiple PanelaCorrida's) but a 'PanelaCorrida' can be in one Local. But one local can have multiple PanelaCorrida's as well. It's basically, this relationship:
Panela 1 - N PanelaCorrida
Local 1 - N Panela
Local 1 - N PanelaCorrida
For this query, i need to get the Last PanelaCorrida of the db, but i need the info of Panela and Local as well.
So far, i can get all data using this NHibernate query:
To get all id's of 'panela' which is active:
var panelaIdList = Session.QueryOver<Panela>()
.Select(c => c.Id)
.Where(c => c.IdcAtiva == true)
.List<Int64>();
To get all last id's of 'PanelaCorrida' which is active (and the last PanelaCorrida generated):
var corridaPanelaIdList = Session.QueryOver<PanelaCorrida>()
.Select(
Projections.Max<PanelaCorrida>(x => x.Id),
Projections.Group<PanelaCorrida>(x => x.Panela)
)
.Where(p => p.Panela.IsIn(panelaIdList.ToArray()))
.List<Object[]>();
Now, to get the result with all info of all those tables:
With DetachedCriteria:
criteria = DetachedCriteria.For<PanelaCorrida>()
.CreateAlias("Local", "L")
.CreateAlias("Panela", "P")
.Add(Restrictions.In("Id", corridaPanelaIdList.Select(x => x[0]).ToArray()));
With Session QueryOver:
var corridas = Session.QueryOver<PanelaCorrida>()
.Where(p => p.Id.IsIn(corridaPanelaIdList.Select(x => x[0]).ToArray()))
.List<PanelaCorrida>();
But the problem is, i need only a few columns of each Table. With NHibernate i've tried with Projections, and with QueryOver, i've tried with SelectList, but each time they generate a error (could not found the property of ...) or they do not populate the entities in result.
How i could achieve this?
Note: This is my query in first place (in SQL):
select cd.num_panela_corrida, cd.num_panela, p.numero, l.num_local from scp_panela_corrida cd
inner join scp_panela p on p.num_panela = cd.num_panela
inner join scp_local l on l.num_local = cd.num_local and cd.num_panela_corrida
in (
select
max( c.num_panela_corrida) as num_panela_corrida
from
scp_panela_corrida c
inner join
scp_panela p on p.num_panela = c.num_panela
and p.num_panela in (
select
num_panela
from
scp_panela
where
idc_ativa = 1
) group by c.num_panela ) order by cd.num_panela_corrida desc
But the client don't want to use a Stored Procedure or HQL.
Any help is welcome.
Resolved with the following code (provided with the link of Radim Kohler).
Radim, if you want, please answer this question with the provided link and i'll accept it as the answer. Thanks for your help.
Panela panela = null;
Local local = null;
var query = session.QueryOver<PanelaCorrida>()
.JoinAlias(c => c.Panela, () => panela)
.Where (c => c.Id.IsIn(corridaPanelaIdList.ToArray()))
.SelectList(list => list
.Select(c => c.Id))
.Select(c => c.CodCorrida)
.Select(Projections.Property(() => panela.Id).As("Panela.Id"))
.Select(Projections.Property(() => panela.IdcAtiva).As("Panela.IdcAtiva"))
.TransformUsing(Transformers.AliasToBean(typeof(PanelaCorrida)));
Don't know if is the best approach, but it worked. We will analyze best approaches using QueryOver/DetachedCriteria, but for now, this works great.
Note: I've removed some columns, just to explain how it worked.
Thanks again.

Assign aggregate result to the entity property not pulling all subquery rows

I have a Comment and Votes related to the comment.
[Table("QAComment")]
public class QaComment : IEntity
{
[Key, Column("QACommentID")]
public int Id { get; set; }
// ...
public virtual ICollection<QaCommentVote> Votes { get; set; }
[NotMapped]
public int OverallVote { get; set; }
}
[Table("QACommentVote")]
public class QaCommentVote : IEntity
{
[Key, Column("QACommentVoteID")]
public int Id { get; set; }
[ForeignKey("QAComment")]
public int QaCommentId { get; set; }
public int Value { get; set; }
public virtual QaComment QaComment { get; set; }
}
I need to get comments with the sum of their votes, not pulling all votes to the application.
The ways I can see to achive this:
1. Make a database view for Commment and calc votes sum in there.
Cons: dont wanna make extra-views
2. Via LINQ:
var comments =
Set<QaComment>()
.Select(c => new QaComment() {/* assign every property once again and calc OverallVote */});
Cons: don't like to assign allproperties once again.
Is there a better way devoid of that cons?
UPDATE
This is what I want as a result of LINQ:
SELECT
qac.*,
(SELECT SUM(v.Value)
FROM QACommentVote v
WHERE v.QACommentID = qac.QACommentID) as OverallVote
FROM QAComment qac
You can fetch QaComment and the sum you're looking for separately as anonymous type and merge them into one object using LINQ to Objects:
var comments
= Set<QaComment>()
.Select(c => new { c, sum = c.Votes.Sum(v => v.Value))
.AsEnumerable() // to make next query execute as LINQ to Objects query
.Select(x => { x.c.OverallVote = x.sum; return x.c; })
.ToList();
But to make point clear: I haven't tested that :)

Convert IGrouping from one class to another using linq

I have two classes Teams and PlayerTeams
public class PlayerTeams
{
public int ID { get; set; }
public string PlayerName { get; set; }
public string PlayerCountry { get; set; }
public bool IsActive { get; set; }
public string PlayerTeam { get; set; }
}
public class Players
{
public int ID { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public bool? Status { get; set; }
}
I have a list of PlayerTeams which is grouped by PlayerTeam like this.
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy( x => x.PlayerTeam)
.ToList();
Its of type List<IGrouping<string, PlayerTeams>> but I want it to be of type List<IGrouping<string, Players>> as I do not want the redundant key information on every row.
How could I possibly achieve that? I could only think of something like .ConvertAll() on the IGrouping. I am not able to make it also.
Is there an efiicient way to do this?
If you can change the grouping, I'd just use:
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.ToList();
Where Players.FromPlayerTeam is a static method in Players which takes a PlayerTeam and returns a Players.
Additionally, I'd suggest using ToLookup instead of GroupBy - a Lookup is exactly what you want here, without bothering with the ToList call.
This not testet, just an idea.
If you have trouble converting your linq statement, which is expecting the IGrouping type, to a string list, then you might have to select it before.
var groupedPlayers = new List<string>();
groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.Select(x => x.Key) // << added select
.ToList();

Categories

Resources