Creating dynamic range aggregations in Nest - c#

I have a set of POCO facets with ranges, that I have received from a client. I need to convert these ultimately into an AggregationDictionary. I cannot figure out the syntax for creating a dynamic set of aggregations (possilbly of type RangeAggregationDescriptor) and need help with this.
My POCO objects are below:
public class TypedFacets
{
public string Name { get; set; }
public string Field { get; set; }
public IReadOnlyCollection<Range> RangeValues { get; set; } = new List<Range>();
public int Size { get; set; }
}
public class Range
{
public string Name { get; set; }
public double? From { get; set; }
public double? To { get; set; }
}
The Nest generation looks like below:
var facets = new List<TypedFacets>()
{
new TypedFacets()
{
Name = "potatoRange",
Field = "potatoRange",
RangeValues = new List<Range>()
{
new Range()
{
From = 0,
To = null,
Name = "chips"
},
new Range()
{
From = 1,
To = null,
Name = "crisps"
}
}
}
};
var aggregations = new AggregationContainerDescriptor<Template>();
facets.Where(f => f.RangeValues.Any()).ToList().ForEach(f =>
{
var rad = new RangeAggregationDescriptor<Template>();
f.RangeValues.ToList().ForEach(rangeValue =>
{
rad = rad.Ranges(rs => rs.From(rangeValue.From).To(rangeValue.To).Key(rangeValue.Name));
});
// this line doesn't work and needs to change
aggregations.Range(f.Name, r => r
.Field(f.Field).Ranges(rs => rad.Ranges));
});
return ((IAggregationContainer)aggregations).Aggregations;
I'm not sure how to fix the above. Any help would be appreciated.

I eventually found the solution for this. You can create the dynamic ranges as per below
private Func<AggregationRangeDescriptor, IAggregationRange>[] CreateRangeRanges(TypedFacets rangedAgg)
{
var rangeRanges = new List<Func<AggregationRangeDescriptor, IAggregationRange>>();
rangedAgg.RangeValues.ToList().ForEach(rangeValue =>
{
rangeRanges.Add(rs => rs.From(rangeValue.From).To(rangeValue.To).Key(rangeValue.Name));
});
return rangeRanges.ToArray();
}
And then assing them like below
facets.Where(f => f.RangeValues.Any()).ToList().ForEach(f =>
{
aggregations.Range(f.Name, r => r
.Field(f.Field).Ranges(CreateRangeRanges(f)));
});

Related

Filter data from 2 lists with diferent models C#

I have this models
public class RoutingAttributeModel
{
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
public string Notes { get; set; }
}
public class AgentRoutingAttributeModel
{
public int Agent_No { get; set; }
public int Bus_No { get; set; }
public int Attribute_No { get; set; }
public string Attribute_Name { get; set; }
public string Status { get; set; }
}
List<RoutingAttributeModel> lstComplete = new List<RoutingAttributeModel>();
List<AgentRoutingAttributeModel> lstAssigned = new List<AgentRoutingAttributeModel>();
Filled this with some data
Is it possible to filter with Linq? I want to save in a new list the diferent content between lstComplete and lstAssigned
I was trying to join both lists but got stuck there
var results1 = from cl in lstComplete
join al in lstAssigned
on cl.Attribute_No equals al.Attribute_No
select cl;
you can use linq
as my understanding, you try to find linked by attribute_No records and have a list of not matching properties?
lstComplete.Add(new RoutingAttributeModel(){
Attribute_Name = "aaa",
Attribute_No = 1,
Bus_No = 1,
Notes = "",
Status = "status"
});
lstAssigned.Add(new AgentRoutingAttributeModel()
{
Attribute_No = 1,
Agent_No = 10,
Bus_No = 1,
Attribute_Name = "bbb",
Status = "status2"
});
var lst = lstComplete
.Join(lstAssigned,
complete => complete.Attribute_No,
assigned => assigned.Attribute_No,
(complete, assigned) => new { lstComplete = complete, lstAssigned = assigned })
.Select(s => new { s.lstComplete, s.lstAssigned})
.Where(w=>
w.lstAssigned.Attribute_Name != w.lstComplete.Attribute_Name
|| w.lstAssigned.Bus_No != w.lstComplete.Bus_No
)
.ToList()
.Dump();
so result would be
You could try the following query
var filteredList = lstComplete
.Where(x => !lstAssigned.Any(y => y.Attribute_No == x.Attribute_No));

LINQ query to get all data about object which is collection of objects when each item of collection has own collection

Could anyone give me a hint on how to resolve the next task?
I have such a classes structure
public class Test
{
public string Title { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public string Description { get; set; }
public ICollection<AnswerItem> AnswerItems { get; set; }
}
public class AnswerItem
{
public string Defenition { get; set; }
public bool IsCorrect { get; set; }
}
After fetching the [Test] entity from DB I've got IQueryable<Test>. And further, I wonder how to get all Question entities (as a list) with all info where each Question item would have all info about AnswerItem collection.
I have tried the next query, but it returns only a collection of all answers:
var questList = test.SelectMany(t => t.Questions.SelectMany(q => q.AnswerItems)).ToList();
And I need something like this:
Questions = new List<Question>
{
new Question
{
Name = "Question_1",
AnswerItems = new List<AnswerItem>
{
new AnswerItem { Name = "answer_1", IsCorrect = false },
new AnswerItem { Name = "answer_2", IsCorrect = false }
}
},
new Question
{
Name = "Question_2",
AnswerItems = new List<AnswerItem>
{
new AnswerItem { Name = "answer_1", IsCorrect = false },
new AnswerItem { Name = "answer_2", IsCorrect = false }
}
}
}
Your query should looks like the following one:
var query =
from t in ctx.Tests
from q in t.Questions
select new
{
Name = q.Description,
AnswerItems = q.AnswerItems.Select(a => new
{
Name = a.Defenition,
IsCorrect = a.IsCorrect
})
.ToList()
}
};
var result = query.ToList();

C# Flaten a nested list to list of different object for display in a datagrid

I'm having an issue presenting my nested collection in a WPF Datagrid.
bellow is the code that is giving me the desired result, but I wonder if it is possible to make it simpler?
public async Task LoadRecepi(short id)
{
Recepi = await _recepiDataService.Get(id);
var flat = new List<FlatRecepi1>();
foreach (var step in Recepi.Step)
{
flat.Add(new FlatRecepi1 {
RecepiId = step.RecepiId,
StepId = step.SPTagId,
Activity = step.Activity,
PVTagName = step.PVTag.Name
});
foreach (var node in step.Nodes)
{
flat.Add(new FlatRecepi1
{
StepId = node.SPTagId,
SPTagName = node.SPTag.Name,
PVTagName = node.PVTag.Name
});
}
}
}
thankyou so much for your help.
public class FlatRecepi1
{
public short RecepiId { get; set; }
public short StepId { get; set; }
public Activity Activity { get; set; }
public short NodeId { get; set; }
public string StepName { get; set; }
public string PVTagName { get; set; }
public string SPTagName { get; set; }
public Operator Operator { get; set; }
}
You can use a combination of Enumerable.SelectMany and Enumerable.Prepend.
The following code will project out each step's nodes into a collection of FlatRecepi1 and then prepend the FlatRecepi1 corresponding to the step at the start of the collection. Finally, the SelectMany flattens this "collection of collections" into a single list. This should give you the same ordering as the current code.
var flat = Recepi.Step.SelectMany(step =>
step.Nodes.Select(node => new FlatRecepi1 {
StepId = node.SPTagId,
SPTagName = node.SPTag.Name,
PVTagName = node.PVTag.Name
}).Prepend(new FlatRecepi1 {
RecepiId = step.RecepiId,
StepId = step.SPTagId,
Activity = step.Activity,
PVTagName = step.PVTag.Name,
})
).ToList();
If Prepend is not available to you because you are using an older framework, we can achieve the same with Enumerable.Concat:
var flat = Recepi.Step.SelectMany(step =>
new [] {
new FlatRecepi1 {
RecepiId = step.RecepiId,
StepId = step.SPTagId,
Activity = step.Activity,
PVTagName = step.PVTag.Name,
}
}.Concat(
step.Nodes.Select(node => new FlatRecepi1 {
StepId = node.SPTagId,
SPTagName = node.SPTag.Name,
PVTagName = node.PVTag.Name
})
)
).ToList();

How to convert one type of list with objects to another?

I'm trying to convert a list of a particular type of an object to another. Here is the original type;
public class OriginialType
{
public string Module { get; set; }
public string Screen { get; set; }
public string Permission { get; set; }
}
And here is the list of this target type;
List<OriginialType> orignialList = new List<OriginialType>();
orignialList.Add(new OriginialType { Module = "Module1", Screen = "Dashboard", Permission = "Add" });
orignialList.Add(new OriginialType { Module = "Module1", Screen = "Dashboard", Permission = "Edit" });
orignialList.Add(new OriginialType { Module = "Module1", Screen = "Dashboard", Permission = "Delete" });
Here is the target type I want a list of;
public class TargetType
{
public string Module { get; set; }
public List<Screen> Screen { get; set; }
}
public class Screen
{
public string ScreenName { get; set; }
public string Permission { get; set; }
}
So instead of having a list that has items containing the same module repeatedly, I want a list that stores a module once while having all the screens associated with it. How can I convert this OriginalType into my TargetType?
Edit:The solution by #itsme86 worked perfectly. But I realized I needed the name of the screens to show up once too. So modified his solution a bit. First changed my TargetType to;
public class TargetType
{
public string Module { get; set; }
public List<Screen> Screen { get; set; }
}
public class Screen
{
public string ScreenName { get; set; }
public List<Permission> Permission { get; set; }
}
public class Permission
{
public string PermissionName { get; set; }
}
And the whole conversion method to;
foreach (var grouping in roleRightsList.GroupBy(o => new { o.Module, o.Screen }))
{
TargetModel target = new TargetModel { Module = grouping.Key.Module };
targetList.Add(target);
Screen targetScreen = new Screen { ScreenName = grouping.Key.Screen };
targetScreen.Permission = grouping.Select(o => new Permission
{
PermissionName = o.Permission,
}).ToList();
target.Screen = new List<Screen>();
target.Screen.Add(targetScreen);
}
Appreciate all the answers.
You could just do something simple like this:
List<TargetType> targetList = new List<TargetList>();
foreach (var grouping in originalList.GroupBy(o => o.Module))
{
TargetType target = new TargetType { Module = grouping.Key };
targetList.Add(target);
target.Screen = grouping.Select(o => new Screen
{
ScreenName = o.Screen,
Permission = o.Permission
}).ToList();
}
Try this:
var targetTypes = orignialList.GroupBy(o => o.Module)
.Select(
g =>
new TargetType
{
Module = g.Key,
Screen =
g.Select(t => new Screen {Permission = t.Permission, ScreenName = t.Screen}).ToList()
}).ToList();
..a bit more explicit here:
List<TargetType> targetTypeList = new List<TargetType>();
List<IGrouping<string, OriginialType>> grouping = orignialList.GroupBy(o => o.Module).ToList();
foreach(IGrouping<string, OriginialType> group in grouping)
{
TargetType newItem = new TargetType() { Module = group.Key, Screen = new List<Screen>() };
foreach(OriginialType o in group)
newItem.Screen.Add(new Screen() { ScreenName = o.Screen, Permission = o.Permission });
targetTypeList.Add(newItem);
}

Can I put a conditional check on a string to see if it ends in "00" inside a LINQ expression?

I am trying to populate an Objective and ObjectiveDetail objects. Here are the classes I have:
partial class Objective
{
public Objective() {
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int ObjectiveId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
public partial class ObjectiveDetail
{
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public string Text { get; set; }
public virtual Objective Objective { get; set; }
}
I'm currently populating the only the Objective object from this call:
var objectiveData = GetContent.GetType5();
var objectives = objectiveData.Select(o => new Objective {
Name = o.Name,
Text = o.Text}
);
The data looks like this:
Name Text
0600 header 1
0601 detail abc
0602 detail def
0603 detail ghi
0700 header 2
0701 detail xyz
Is there a way I could modify my LINQ so that only the data where the name field contents end in "00" goes into the Objective object (as it does now) and when the data where the name field contents end in "01" then it creates a new ObjectiveDetail object with "detail abc" etc going into the text field.
This is a picture of what the end result should look like:
A collection of Objectives
new Objective { name = "header 1",
ObjectiveDetails = A collection of ObjectiveDetails
name = "detail abc"
name = "detail def" etc.
Sure you can do that, using [string.EndsWith] method like:1
.Where(r=> r.Name.EndsWith("00"))
Modify your query as:
var objectives = objectiveData
.Where(r => r.Name.EndsWith("00"))
.Select(o => new Objective {
Name = o.Name,
Text = o.Text}
);
It's somewhat unclear what you are asking, but you can put complex logic inside the Select() if you need to:
var objectives = objectiveData.Select(o =>
{
var result = new Objective
{
Name = o.Name,
Text = o.Text
};
if (o.Name != null && o.Name.EndsWith("01"))
{
result.ObjectiveDetails.Add
(
new ObjectiveDetail
{
ObjectiveDetailId = o.ObjectiveId,
Name = o.Name,
Text = o.Text,
Objective = result
}
);
}
return result;
});
(Note that I'm guessing at what you need; you will need to correct the logic to do what you really want.)
Looks like you want to do some sort of conditional mapping. I like Matthew Watson's answer, but it's a bit unclear why he's always creating an Objective instance every time. Here's some LINQ-less code which I believe is more readable, and maps the way I think you'd want:
public class Mapper
{
public List<Objective> Objectives = new List<Objective>();
public class Objective
{
public int ObjectiveId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
public Objective()
{
ObjectiveDetails = new List<ObjectiveDetail>();
}
}
public class ObjectiveDetail
{
public int ObjectiveDetailId { get; set; }
public int ObjectiveId { get; set; }
public string Text { get; set; }
public virtual Objective Objective { get; set; }
}
public void Assign()
{
var objectiveData = new[] // Hard-coded test data. We don't know what the type of each item in this list is, so I use an anonymous type
{
new {Name = "0600", Text = "Header 06"},
new {Name = "0601", Text = "06 Detail 01"},
new {Name = "0602", Text = "06 Detail 02"},
new {Name = "0603", Text = "06 Detail 03"},
new {Name = "0700", Text = "Header 07"},
new {Name = "0701", Text = "07 Detail 01"},
new {Name = "0702", Text = "07 Detail 02"}
};
// Create Objectives first
var id = 1;
foreach (var item in objectiveData.Where(i => i.Name.EndsWith("00")))
{
Objectives.Add(new Objective { ObjectiveId = id, Name = item.Name, Text = item.Text });
id++;
}
// Create ObjectiveDetails
id = 1;
foreach (var item in objectiveData.Where(i => !i.Name.EndsWith("00")))
{
var itemLocal = item;
var matchingObjective = Objectives.FirstOrDefault(o => o.Name.StartsWith(itemLocal.Name.Substring(0, 2)));
var objectiveDetail = new ObjectiveDetail
{
ObjectiveDetailId = id,
Text = item.Text,
ObjectiveId = matchingObjective != null ? matchingObjective.ObjectiveId : 0,
Objective = matchingObjective
};
if (matchingObjective != null)
{
matchingObjective.ObjectiveDetails.Add(objectiveDetail);
}
id++;
}
// At the end of this method you should have a list of Objectives, each with their ObjectiveDetails children
}
}
Output:
Hope this helps.

Categories