I have object Order with OrderItems.
I need grouped the data by field ProductId (in orderItems) and show sum for each product.
This solution works well:
var collection = database.GetCollection<Order>("Order");
var result = collection.Aggregate().Unwind(x=>x.OrderItems)
.Group(new BsonDocument
{
{"_id", "$OrderItems.ProductId"},
{"suma", new BsonDocument
{
{ "$sum" , "$OrderItems.UnitPriceExclTax"}
}
}
}).ToListAsync().Result;
But I don't want use pipeline, I need prepare full sample in c#.
And this solution doesn't work.
var collection = database.GetCollection<Order>("Order");
var result = collection.Aggregate()
.Unwind(x=>x.OrderItems)
.Group(i => i.ProductId, g => new { ProductId = g.Key, Count = g.Sum(i.UnitPriceExclTax) })
Thanks for any help,
I found solution,
I've prepared extra class:
[BsonIgnoreExtraElements]
public class UnwindedOrderItem
{
public OrderItem OrderItems { get; set; }
}
var agg = database.GetCollection<Order>("Order")
.Aggregate()
.Unwind<Order, UnwindedOrderItem>(x => x.OrderItems)
.Group(x=>x.OrderItems.ProductId, g => new
{
Id = g.Key,
Suma = g.Sum(x=>x.OrderItems.PriceExclTax)
})
.ToListAsync().Result;
It seems to me that you're trying to use linq syntax instead of mongodb. I don't know of any solution that would allow similar syntax to what you've described above in the aggregation-pipeline.
Related
I have a list with people with integerIds who share Tokens I am trying to get the products of each pair of friends id numbers. so friend id 2 and 4 would result in 8 etc.
var FriendsWhoShareTheMostTokens = FriendsWithTheirSharedTokensAmount.Select(x => new
{
FriendIdA = x.FriendTo.FriendID,
FriendIdB = x.FriendFor.FriendID,
SharedCountNumber = x.SharedCount
})
.Where(x => x.SharedCountNumber == FriendsWithTheirSharedTokensAmount.Max(y => y.SharedCount))
.ToList();
// need some code here**
foreach (var pairOffriendsWhoSharetheMostTokens in FriendsWhoShareTheMostTokens)
{
}
Can I accomplish this with Linq or what is the best way to accomplish this?
Edit
The answer is simple
var ProductofGreatestTokenSharers = FriendsWhoShareTheMostTokens.Select(x => new
{
ProductOfIds = x.FriendIdA * x.FriendIdB
}
);
It's really hard to understand what you are trying to achieve with your example.
However, if you want to have the id's and the product, and also obtain the highest SharedCountNumber.
You can probably just do the following
var someResult = someList.Select(x => new
{
FriendIdA = x.FriendTo.FriendID,
FriendIdB = x.FriendFor.FriendID,
SharedCountNumber = x.FriendTo.FriendID * x.FriendFor.FriendID
}) // Projection
.OrderByDescending(x => x.SharedCountNumber) // order the list
.FirstOrDefault(); // get the first
if (someResult != null)
{
Debug.WriteLine($"A : {someResult.FriendIdA}, B : {someResult.FriendIdB}, Product : {someResult.SharedCountNumber}");
}
In order to explain the problem I've created a simplified example. In real life the data class is somewhat more complicated. Consider the following data class:
public class Data
{
public Data(string source, string path, string information)
{
this.Source = source;
this.Path = path;
this.Information = information;
}
public string Source { get; set; }
public string Path { get; set; }
public string Information { get; set; }
}
Now consider the following array:
var array = new Data[] {
new Data("MS", #"c:\temp\img1.jpg", "{a}"),
new Data("IBM", #"c:\temp\img3.jpg", "{b}"),
new Data("Google", #"c:\temp\img1.jpg", "{c}"),
new Data("MS", #"c:\temp\img2.jpg", "{d}"),
new Data("MS", #"c:\temp\img3.jpg", "{e}"),
new Data("Google", #"c:\temp\img1.jpg", "{f}"),
new Data("IBM", #"c:\temp\img2.jpg", "{g}")
};
I would like to process the data by partitioning it on the Path and sorting each partition on Source. The output needs to be like:
c:\temp\img1.jpg
"Google": "{c}"
"IBM": "{f}"
"MS": "{a}"
c:\temp\img2.jpg
"IBM": "{g}"
"MS": "{d}"
c:\temp\img3.jpg
"IBM": "{b}"
"MS": "{e}
How can I create these partitions with LINQ?
Here you can play with the code: https://dotnetfiddle.net/EbKluE
You can use LINQ's OrderBy and GroupBy to sort your items by Source and group your ordered items by Path:
var partitioned = array
.OrderBy(data => data.Source)
.GroupBy(data => data.Path);
See this fiddle for a demo.
You can use GroupBy and OrderBy like this:
Dictionary<string, Data[]> result =
array.GroupBy(d => d.Path)
.ToDictionary(g => g.Key, g => g.OrderBy(d => d.Source).ToArray());
This gives you a dictionary with Path as keys. Each value is an array of Data that have this Path and are sorted by their Source.
I would recommend the Group-by function of lync.
For your case:
var queryImageNames =
from image in array // <-- Array is your name for the datasource
group image by image.Path into newGroup
orderby newGroup.Key
select newGroup;
foreach (var ImageGroup in queryImageNames)
{
Console.WriteLine("Key: {0}", nameGroup.Key);
foreach (var image in ImageGroup )
{
Console.WriteLine("\t{0}, {1}", image.Source, image.Information);
}
}
You could use GroupBy and do this.
var results = array
.GroupBy(x=>x.Path)
.Select(x=>
new
{
Path =x.Key,
values=x.Select(s=> string.Format("{0,-8}:{1}", s.Source, s.Information))
.OrderBy(o=>o)
})
.ToList();
Output:
c:\temp\img1.jpg
Google :{c}
Google :{f}
MS :{a}
c:\temp\img3.jpg
IBM :{b}
MS :{e}
c:\temp\img2.jpg
IBM :{g}
MS :{d}
Check this fiddle
You can use Enumerable.GroupBy to group by the Path property:
var pathPartitions = array.GroupBy(x => x.Path);
foreach(var grp in pathPartitions)
{
Console.WriteLine(grp.Key);
var orderedPartition = grp.OrderBy(x => x.Source);
foreach(var x in orderedPartition )
Console.WriteLine($"\"{x.Source}\": \"{x.Information}\"");
}
If you want to create a collection you could create a Tuple<string, Data[]>[]:
Tuple<string, Data[]>[] pathPartitions = array
.GroupBy(x => x.Path)
.Select(g => Tuple.Create(g.Key, g.OrderBy(x => x.Source).ToArray()))
.ToArray();
or a Dictionary<string, Data[]>:
Dictionary<string, Data[]> pathPartitions = array
.GroupBy(x => x.Path)
.ToDictionary(g => g.Key, g => g.OrderBy(x => x.Source).ToArray());
In my application I have Movements associated with a category.
I want a list of the most frequent category.
My objects are:
Category: catId, catName
Movement: Movid, movDate, movMount, catId
I think it would have to raise it with a "Group By" query (grouping by catId and getting those more)
(Im using Entity Framework 6 in c#)
From already thank you very much!
IMPORTANT: Entity Framework 7 (now renamed to Entity Framework Core 1.0) does not yet support GroupBy() for translation to GROUP BY in generated SQL. Any grouping logic will run on the client side, which could cause a lot of data to be loaded.
https://blogs.msdn.microsoft.com/dotnet/2016/05/16/announcing-entity-framework-core-rc2
group the movements by category and select catid and count.
join this result with category to get the name and then descending sort the results on count.
var groupedCategories = context.Movements.GroupBy(m=>m.catId).Select(g=>new {CatId = g.Key, Count = g.Count()});
var frequentCategories = groupedCategories.Join(context.Categories, g => g.CatId, c => c.catId, (g,c) => new { catId = c.catId, catName = c.catName, count = g.Count }).OrderByDescending(r => r.Count);
foreach (var category in frequentCategories)
{
// category.catId, category.catName and category.Count
}
i hope this help:
var query = dbContext.Category.Select(u => new
{
Cat = u,
MovementCount = u.Movement.Count()
})
.ToList()
.OrderByDescending(u => u.MovementCount)
.Select(u => u.Cat)
.ToList();
I resolved the problem!
I used the proposal by "Raja" solution (Thanks a lot!).
This return a collection composed of "Category" and "Count". I Change it a bit to return a list of Categories.
var groupedCategories = model.Movement.GroupBy(m => m.catId).Select(
g => new {catId= g.Key, Count = g.Count() });
var freqCategories= groupedCategories.Join(model.Category,
g => g.catId,
c => c.catId,
(g, c) => new {category = c, count = g.Count}).OrderByDescending(ca => ca.count).Select(fc => fc.category).ToList ();
you just need to use navigation property on category simply, you have a navigation property on category contains all related Movement, i call it Movements in following query. you can write your query like this, with minimum of connection with DB.
class Cat
{
public Guid catId { get; set; }
public string catName { get; set; }
public IEnumerable<Movement> Movements { get; set; }
public int MovementsCount { get { return Movements.Count(); } }
}
var Categories = category.Select(u => new Cat()
{
u.catId,
u.catName,
Movements = u.Movements.AsEnumerable()
}).ToList();
var CategoriesIncludeCount = Categories.OrderBy(u => u.MovementsCount).ToList();
we have set.All(value) in linq, when i use list of long in set parameter everything is ok.
List<long> searchIds = new List<long>();
searchIds.Add(1);
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var adsWithRelevantadFields =
from adField in cwContext.tblAdFields
join ads in cwContext.tblAds on adField.adId equals ads.id
group adField by adField.adId into adAdFields
where searchIds.All(i => adAdFields.Select(co => co.listId).Contains(i))
select adAdFields.Key;
MessageBox.Show(adsWithRelevantadFields.Count().ToString());
}
but when i use list of class i get error:
var lstId = new ListIds[]
{
new ListIds { listId = 1 },
};
class ListIds
{
public long listId { get; set; }
}
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var adsWithRelevantadFields =
from adField in cwContext.tblAdFields
join ads in cwContext.tblAds on adField.adId equals ads.id
group adField by adField.adId into adAdFields
where lstId.All(i => adAdFields.Select(co => co.listId).Contains(i.listId))
select adAdFields.Key;
MessageBox.Show(adsWithRelevantadFields.Count().ToString());
}
problem is in where line,
and error is:
unable to create a constant value of type 'ClearWhite.Handlers.ListIds. Only primitive types('such as Int32, String, and Guid') are supported in this context.
Error message is quite simple. Entity Framework is not able to produce correct SQL from your query.
You can either get List<long> before making a query:
var ids = lstId.Select(i => i.listId).ToList();
and then use it within the query:
where ids.All(i => adAdFields.Select(co => co.listId).Contains(i))
or try changing your query a little bit (but I'm not sure it will do the trick):
where lstId.Select(i => i.listId).All(i => adAdFields.Select(co => co.listId).Contains(i))
I have simple type Question:
public class Question
{
public string[] Tags { get; set; }
public DateTime Created { get; set; }
}
While I have a list of questions, I need to filter them along list of tags (called filters). The questions which have the most tags matched by the filters list, should be placed higher in the result collection. I wrote expression for that:
public IList<Question> GetSimiliar(IList<Questions> all, string[] filters)
{
var questions = all.Select(
x => new
{
MatchedTags = x.Tags
.Count(tag => filters.Contains(tag)),
Question = x
})
.Where(x => x.MatchedTags > 0)
.OrderByDescending(x => x.MatchedTags)
.Select(x => x.Question);
return questions.ToList();
}
Now I need a support for such situation, where I have more than one question with the same quantity of matched tags. Such questions should be further sorted by creation date (from newest to oldest).
Example of what I want:
filter: tags = [a,b,c]
collection of questions to be filtered:
q1 { tags = [a], created = 1939 }
q2 { tags = [b], created = 1945 }
q3 { tags = [a,b,c], created = 1800 }
q4 { tags = [a,b], created = 2012 }
q5 { tags = [z], created = 1999 }
result - the sorted collection:
q3
q4
q2
q1
How to do that using linq ?
Now I need a support for such situation, where I have more than one question with the same quantity of matched tags. Such questions should be further sorted by creation date (from newest to oldest).
Use ThenBy or ThenByDescending to further sort your query. Use these methods to break ties in prior ordering.
.OrderByDescending(x => x.MatchedTags)
.ThenByDescending(x => x.Question.Created)
.Select(x => x.Question);
The 101 Linq Samples page has a nested grouping example. This sample uses group by to partition a list of each customer's orders, first by year, and then by month:
public void Linq43()
{
List<Customer> customers = GetCustomerList();
var customerOrderGroups =
from c in customers
select
new
{
c.CompanyName,
YearGroups =
from o in c.Orders
group o by o.OrderDate.Year into yg
select
new
{
Year = yg.Key,
MonthGroups =
from o in yg
group o by o.OrderDate.Month into mg
select new { Month = mg.Key, Orders = mg }
}
};
ObjectDumper.Write(customerOrderGroups, 3);
}