Reputation: 9959
How can i get the top 5 rated products via via Linq to Sql?
My products class is
public class Product
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ProductID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
and my ProductReviews class is
public class ProductReview
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ProductReviewID { get; set; }
public int ProductID { get; set; }
public int Rating { get; set; }
public virtual Product Product { get; set; }
}
Upvotes: 1
Views: 171
Reputation: 45771
Assuming that you want to take a simple average of the Rating from all reviews for each product, the following code (including sample data) should do the trick (I've tested this against LINQ against objects, but it should work with LINQ to SQL also):
var products = new List<Product>
{
new Product { ProductID = 1, Name= "Product 1", Price = 1.00m },
new Product { ProductID = 2, Name= "Product 2", Price = 1.00m },
new Product { ProductID = 3, Name= "Product 3", Price = 1.00m },
new Product { ProductID = 4, Name= "Product 4", Price = 1.00m },
new Product { ProductID = 5, Name= "Product 5", Price = 1.00m },
new Product { ProductID = 6, Name= "Product 6", Price = 1.00m }
};
var productReviews = new List<ProductReview>
{
new ProductReview { ProductReviewID = 1, ProductID = 1, Rating = 5 },
new ProductReview { ProductReviewID = 2, ProductID = 1, Rating = 3 },
new ProductReview { ProductReviewID = 3, ProductID = 2, Rating = 1 },
new ProductReview { ProductReviewID = 4, ProductID = 3, Rating = 4 },
new ProductReview { ProductReviewID = 5, ProductID = 4, Rating = 2 },
new ProductReview { ProductReviewID = 6, ProductID = 5, Rating = 5 },
new ProductReview { ProductReviewID = 7, ProductID = 6, Rating = 4 },
new ProductReview { ProductReviewID = 8, ProductID = 6, Rating = 3 }
};
var averageProductRatings = from review in productReviews
group review by review.ProductID into product
select new
{
ProductId = product.Key,
AverageRating = product.Average(p => p.Rating)
};
var top5 = averageProductRatings.OrderByDescending(average => average.AverageRating).Take(5);
The first statement is taking the review data, grouping it by ProductID
and calculating the average value of the Rating
for each Product.
The second statement is then taking the averages for each product and giving you the 5 products with the highest average ratings.
If you wanted to do something different (like give a higher weighting to more recent reviews) you could pass "product" into a custom function that works out the rating for that product, for example:
var averageProductRatings = from review in productReviews
group review by review.ProductID into product
select new
{
ProductId = product.Key,
AverageRating = GetProductRatingFromReviews(product)
};
private double GetProductRatingFromReviews(IGrouping<int, ProductReview> productReviews)
{
// Work out the aggregate rating to give the product here and return it
foreach (var item in productReviews)
{
}
return -1;
}
Upvotes: 2
Reputation: 1564
I hope I understood your use case properly. products and productReviews are similar to your relevant collections:
productReviews.GroupBy(q => q.ProductID)
.Select(t => new {ID = t.Key, ReviewAvg = t.Average(q => q.Rating})
.OrderByDescending(q => q.ReviewAvg).Take(5).Select(t => products.FirstOrDefault(q => q.ProductID == t.ID));
I first group by all reviews by the relevant product id (would be better if grouping si done by the product itself) and then for each product the average rating is being calculated and the top 5 products are chosen.
Upvotes: 1
Reputation: 2000
You need to specify criteria to detect top items. Then you can do something like this
var top5Products = database.Products.Where(product => _your condition_).OrderBy(product => product.SomeField).Take(5).ToList();
Upvotes: 0