Skip to content

Instantly share code, notes, and snippets.

@jakejscott
Last active June 2, 2016 20:14
Show Gist options
  • Save jakejscott/b42fc3fec9efe173f4e22cbbc41be76a to your computer and use it in GitHub Desktop.
Save jakejscott/b42fc3fec9efe173f4e22cbbc41be76a to your computer and use it in GitHub Desktop.
PredicateBuilder + LINQ + Marten
using System;
using System.Collections.Generic;
namespace Sprightly.Entities
{
public enum CollectionType
{
Manual,
Automatic,
}
public enum CollectionSortBy
{
BestSelling,
AlphabeticallyAZ,
AlphabeticallyZA,
PriceHighestToLowest,
PriceLowestToHighest,
DateNewestToOldest,
DateOldestToNewest,
}
public class Collection
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Slug { get; set; }
public string Description { get; set; }
public CollectionType Type { get; set; }
public CollectionSortBy SortBy { get; set; }
public List<Guid> ProductIds { get; set; }
public List<CollectionRule> Rules { get; set; }
public Collection()
{
Rules = new List<CollectionRule>();
ProductIds = new List<Guid>();
Type = CollectionType.Automatic;
SortBy = CollectionSortBy.AlphabeticallyAZ;
}
}
public enum CollectionRuleColumn
{
ProductTitle, // e.g Nike Air Max
ProductType, // e.g Running Shoes
ProductVendor, // e.g Nike
ProductTag, // e.g running, finess, shoes, nike
ProductPrice,
ProductSalePrice,
ProductWeight,
InventoryStock
}
public enum CollectionRuleOperator
{
IsEqualTo,
IsNotEqualTo,
IsGreaterThan,
IsLessThan,
StartsWith,
EndsWith,
Contains,
DoesNotContain,
}
public enum CollectionRuleMatchConditions
{
AllConditions,
AnyConditions,
}
public class CollectionRule
{
public CollectionRuleColumn Column { get; set; }
public CollectionRuleOperator Operator { get; set; }
public CollectionRuleMatchConditions MatchConditions { get; set; }
public object Value { get; set; }
}
public enum WeightUnit { Grams, Kilograms }
public class Product
{
public Guid Id { get; set; }
public long Sku { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public string Slug { get; set; }
public double Price { get; set; }
public double? SalePrice { get; set; }
public double Weight { get; set; }
public WeightUnit WeightUnit { get; set; }
//
// Collection metadata
//
public string ProductType { get; set; }
public string Vendor { get; set; }
public string[] Tags { get; set; }
public List<Image> Images { get; set; }
public Image FeaturedImage
{
get
{
Image result = null;
if (Images.Any())
{
result = Images.First();
}
return result;
}
}
public Product()
{
Images = new List<Image>();
}
}
}
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Sprightly
{
public List<Product> GetProducts(IQuerySession session, Collection collection, string tag = null, CollectionSortBy? sort = null)
{
List<Product> results;
if (collection.Type == CollectionType.Manual)
{
results = session.LoadMany<Product>(collection.ProductIds.ToArray()).ToList();
}
else
{
Expression<Func<Product, bool>> predicate = p => p.Title != null;
if (collection.Rules.Any())
{
foreach (var _ in collection.Rules)
{
var rule = _;
if (rule.Column == CollectionRuleColumn.ProductPrice)
{
double value = Convert.ToDouble(rule.Value);
switch (rule.Operator)
{
case CollectionRuleOperator.IsEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Price == value)
: predicate.Or(x => x.Price == value);
break;
case CollectionRuleOperator.IsNotEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Price != value)
: predicate.Or(x => x.Price != value);
break;
case CollectionRuleOperator.IsLessThan:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Price < value)
: predicate.Or(x => x.Price < value);
break;
case CollectionRuleOperator.IsGreaterThan:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Price > value)
: predicate.Or(x => x.Price > value);
break;
case CollectionRuleOperator.StartsWith:
case CollectionRuleOperator.EndsWith:
case CollectionRuleOperator.Contains:
case CollectionRuleOperator.DoesNotContain:
throw new NotSupportedException();
}
}
else if (rule.Column == CollectionRuleColumn.ProductSalePrice)
{
double value = Convert.ToDouble(rule.Value);
switch (rule.Operator)
{
case CollectionRuleOperator.IsEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.SalePrice == value)
: predicate.Or(x => x.SalePrice == value);
break;
case CollectionRuleOperator.IsNotEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.SalePrice != value)
: predicate.Or(x => x.SalePrice != value);
break;
case CollectionRuleOperator.IsLessThan:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.SalePrice < value)
: predicate.Or(x => x.SalePrice < value);
break;
case CollectionRuleOperator.IsGreaterThan:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.SalePrice > value)
: predicate.Or(x => x.SalePrice > value);
break;
case CollectionRuleOperator.StartsWith:
case CollectionRuleOperator.EndsWith:
case CollectionRuleOperator.Contains:
case CollectionRuleOperator.DoesNotContain:
throw new NotSupportedException();
}
}
else if (rule.Column == CollectionRuleColumn.ProductTitle)
{
string value = Convert.ToString(rule.Value);
switch (rule.Operator)
{
case CollectionRuleOperator.IsEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Title == value)
: predicate.Or(x => x.Title == value);
break;
case CollectionRuleOperator.IsNotEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Title != value)
: predicate.Or(x => x.Title != value);
break;
case CollectionRuleOperator.StartsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Title.StartsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.Title.StartsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.EndsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Title.EndsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.Title.EndsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.Contains:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Title.Contains(value))
: predicate.Or(x => x.Title.Contains(value));
break;
case CollectionRuleOperator.DoesNotContain:
case CollectionRuleOperator.IsLessThan:
case CollectionRuleOperator.IsGreaterThan:
throw new NotSupportedException();
}
}
else if (rule.Column == CollectionRuleColumn.ProductVendor)
{
string value = Convert.ToString(rule.Value);
switch (rule.Operator)
{
case CollectionRuleOperator.IsEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Vendor == value)
: predicate.Or(x => x.Vendor == value);
break;
case CollectionRuleOperator.IsNotEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Vendor != value)
: predicate.Or(x => x.Vendor != value);
break;
case CollectionRuleOperator.StartsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Vendor.StartsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.Vendor.StartsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.EndsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Vendor.EndsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.Vendor.EndsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.Contains:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.Vendor.Contains(value))
: predicate.Or(x => x.Vendor.Contains(value));
break;
case CollectionRuleOperator.DoesNotContain:
case CollectionRuleOperator.IsLessThan:
case CollectionRuleOperator.IsGreaterThan:
throw new NotSupportedException();
}
}
else if (rule.Column == CollectionRuleColumn.ProductType)
{
string value = Convert.ToString(rule.Value);
switch (rule.Operator)
{
case CollectionRuleOperator.IsEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.ProductType == value)
: predicate.Or(x => x.ProductType == value);
break;
case CollectionRuleOperator.IsNotEqualTo:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.ProductType != value)
: predicate.Or(x => x.ProductType != value);
break;
case CollectionRuleOperator.StartsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.ProductType.StartsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.ProductType.StartsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.EndsWith:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.ProductType.EndsWith(value, StringComparison.InvariantCultureIgnoreCase))
: predicate.Or(x => x.ProductType.EndsWith(value, StringComparison.InvariantCultureIgnoreCase));
break;
case CollectionRuleOperator.Contains:
predicate = rule.MatchConditions == CollectionRuleMatchConditions.AllConditions
? predicate.And(x => x.ProductType.Contains(value))
: predicate.Or(x => x.ProductType.Contains(value));
break;
case CollectionRuleOperator.DoesNotContain:
case CollectionRuleOperator.IsLessThan:
case CollectionRuleOperator.IsGreaterThan:
throw new NotSupportedException();
}
}
}
}
if (tag != null)
{
predicate = predicate.And(x => x.Tags.Contains(tag));
}
IQueryable<Product> query = session.Query<Product>().Where(predicate);
switch (sort)
{
case null:
break;
case CollectionSortBy.BestSelling:
// todo
break;
case CollectionSortBy.AlphabeticallyAZ:
query = query.OrderBy(x => x.Title);
break;
case CollectionSortBy.AlphabeticallyZA:
query = query.OrderByDescending(x => x.Title);
break;
case CollectionSortBy.PriceHighestToLowest:
query = query.OrderByDescending(x => x.Price);
break;
case CollectionSortBy.PriceLowestToHighest:
query = query.OrderBy(x => x.Price);
break;
case CollectionSortBy.DateNewestToOldest:
query = query.OrderBy(x => x.CreatedDate);
break;
case CollectionSortBy.DateOldestToNewest:
query = query.OrderByDescending(x => x.CreatedDate);
break;
default:
throw new ArgumentOutOfRangeException("sort", sort, null);
}
results = query.ToList();
}
return results;
}
}
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Sprightly.Util
{
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment