Skip to content

Commit

Permalink
feat: union filters and expands (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZEXSM authored Mar 27, 2022
1 parent 5c72a1e commit 7258abc
Show file tree
Hide file tree
Showing 17 changed files with 326 additions and 57 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Library for creating complex OData queries (OData version 4.01) based on data mo
[![Nuget Status](https://img.shields.io/nuget/dt/OData.QueryBuilder.svg)](https://www.nuget.org/packages/OData.QueryBuilder)

## Benefits
* Expression is not used to `Compile()`, which generates `MSIL` code, which leads to memory leaks
* Support:
* nested extenders with a choice of filtering
* operators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ public AddressingEntries(StringBuilder stringBuilder, ODataQueryBuilderOptions o

public IODataQueryKey<TEntity> ByKey(params int[] keys)
{
_stringBuilder.Append($"({string.Join(QuerySeparators.Comma, keys)}){QuerySeparators.Begin}");
_stringBuilder.Append($"{QuerySeparators.LeftBracket}{string.Join(QuerySeparators.StringComma, keys)}{QuerySeparators.RigthBracket}{QuerySeparators.Begin}");

return new ODataQueryKey<TEntity>(_stringBuilder, _odataQueryBuilderOptions);
}

public IODataQueryKey<TEntity> ByKey(params string[] keys)
{
_stringBuilder.Append($"('{string.Join($"'{QuerySeparators.Comma}'", keys)}'){QuerySeparators.Begin}");
_stringBuilder.Append($"{QuerySeparators.LeftBracket}'{string.Join($"'{QuerySeparators.Comma}'", keys)}'{QuerySeparators.RigthBracket}{QuerySeparators.Begin}");

return new ODataQueryKey<TEntity>(_stringBuilder, _odataQueryBuilderOptions);
}

public IODataQueryKey<TEntity> ByKey(params Guid[] keys)
{
_stringBuilder.Append($"({string.Join(QuerySeparators.Comma, keys)}){QuerySeparators.Begin}");
_stringBuilder.Append($"{QuerySeparators.LeftBracket}{string.Join(QuerySeparators.StringComma, keys)}{QuerySeparators.RigthBracket}{QuerySeparators.Begin}");

return new ODataQueryKey<TEntity>(_stringBuilder, _odataQueryBuilderOptions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using OData.QueryBuilder.Conventions.Functions;
using OData.QueryBuilder.Conventions.Operators;
using OData.QueryBuilder.Expressions.Visitors;
using OData.QueryBuilder.Extensions;
using OData.QueryBuilder.Options;
using System;
using System.Linq.Expressions;
Expand All @@ -12,11 +13,12 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query.Expand
{
internal class ODataQueryExpand<TEntity> : AbstractODataQueryExpand, IODataQueryExpand<TEntity>
{
private bool _hasMultyFilters;
public ODataQueryExpand(ODataQueryBuilderOptions odataQueryBuilderOptions)
: base(new StringBuilder(), odataQueryBuilderOptions)
{
_hasMultyFilters = false;
}

public IODataQueryExpand<TEntity> Expand(Expression<Func<TEntity, object>> expandNested)
{
var query = new ODataOptionExpandExpressionVisitor().ToQuery(expandNested.Body);
Expand All @@ -41,27 +43,21 @@ public IODataQueryExpand<TEntity> Filter(Expression<Func<TEntity, bool>> filterN
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filterNested.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Nested}");

return this;
return Filter(query);
}

public IODataQueryExpand<TEntity> Filter(Expression<Func<TEntity, IODataFunction, bool>> filterNested, bool useParenthesis = false)
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filterNested.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Nested}");

return this;
return Filter(query);
}

public IODataQueryExpand<TEntity> Filter(Expression<Func<TEntity, IODataFunction, IODataOperator, bool>> filterNested, bool useParenthesis = false)
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filterNested.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Nested}");

return this;
return Filter(query);
}

public IODataQueryExpand<TEntity> OrderBy(Expression<Func<TEntity, object>> orderByNested)
Expand Down Expand Up @@ -120,5 +116,21 @@ public IODataQueryExpand<TEntity> Count(bool value = true)

return this;
}

private IODataQueryExpand<TEntity> Filter(string query)
{
if (_hasMultyFilters)
{
_stringBuilder.Merge(ODataOptionNames.Filter, QuerySeparators.Nested, $" {ODataLogicalOperations.And} {query}");
}
else
{
_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Nested}");
}

_hasMultyFilters = true;

return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using OData.QueryBuilder.Conventions.Functions;
using OData.QueryBuilder.Conventions.Operators;
using OData.QueryBuilder.Expressions.Visitors;
using OData.QueryBuilder.Extensions;
using OData.QueryBuilder.Options;
using System;
using System.Linq.Expressions;
Expand All @@ -12,45 +13,42 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query
{
internal class ODataQueryCollection<TEntity> : ODataQuery, IODataQueryCollection<TEntity>
{
private bool _hasMultyFilters;
private bool _hasMultyExpands;

public ODataQueryCollection(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
: base(stringBuilder, odataQueryBuilderOptions)
{
_hasMultyFilters = false;
_hasMultyExpands = false;
}

public IODataQueryCollection<TEntity> Filter(Expression<Func<TEntity, bool>> filter, bool useParenthesis = false)
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");

return this;
return Filter(query);
}

public IODataQueryCollection<TEntity> Filter(Expression<Func<TEntity, IODataFunction, bool>> filter, bool useParenthesis = false)
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");

return this;
return Filter(query);
}

public IODataQueryCollection<TEntity> Filter(Expression<Func<TEntity, IODataFunction, IODataOperator, bool>> filter, bool useParenthesis = false)
{
var query = new ODataOptionFilterExpressionVisitor(_odataQueryBuilderOptions).ToQuery(filter.Body, useParenthesis);

_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");

return this;
return Filter(query);
}

public IODataQueryCollection<TEntity> Expand(Expression<Func<TEntity, object>> expand)
{
var query = new ODataOptionExpandExpressionVisitor().ToQuery(expand.Body);

_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");

return this;
return Expand(query);
}

public IODataQueryCollection<TEntity> Expand(Action<IODataExpandResource<TEntity>> expandNested)
Expand All @@ -59,9 +57,7 @@ public IODataQueryCollection<TEntity> Expand(Action<IODataExpandResource<TEntity

expandNested(builder);

_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{builder.Query}{QuerySeparators.Main}");

return this;
return Expand(builder.Query);
}

public IODataQueryCollection<TEntity> Select(Expression<Func<TEntity, object>> select)
Expand Down Expand Up @@ -120,5 +116,37 @@ public IODataQueryCollection<TEntity> Count(bool value = true)

return this;
}

private IODataQueryCollection<TEntity> Expand<T>(T query) where T : class
{
if (_hasMultyExpands)
{
_stringBuilder.Merge(ODataOptionNames.Expand, QuerySeparators.Main, $"{QuerySeparators.Comma}{query}");
}
else
{
_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");
}

_hasMultyExpands = true;

return this;
}

private IODataQueryCollection<TEntity> Filter(string query)
{
if (_hasMultyFilters)
{
_stringBuilder.Merge(ODataOptionNames.Filter, QuerySeparators.Main, $" {ODataLogicalOperations.And} {query}");
}
else
{
_stringBuilder.Append($"{ODataOptionNames.Filter}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");
}

_hasMultyFilters = true;

return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query
{
internal class ODataQueryKey<TEntity> : ODataQuery, IODataQueryKey<TEntity>
{
private bool _hasMultyExpands;

public ODataQueryKey(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
: base(stringBuilder, odataQueryBuilderOptions)
{
_hasMultyExpands = false;
}

public IAddressingEntries<TResource> For<TResource>(Expression<Func<TEntity, object>> resource)
Expand All @@ -28,9 +31,7 @@ public IODataQueryKey<TEntity> Expand(Expression<Func<TEntity, object>> expand)
{
var query = new ODataOptionExpandExpressionVisitor().ToQuery(expand.Body);

_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");

return this;
return Expand(query);
}

public IODataQueryKey<TEntity> Expand(Action<IODataExpandResource<TEntity>> expandNested)
Expand All @@ -39,9 +40,7 @@ public IODataQueryKey<TEntity> Expand(Action<IODataExpandResource<TEntity>> expa

expandNested(builder);

_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{builder.Query}{QuerySeparators.Main}");

return this;
return Expand(builder.Query);
}

public IODataQueryKey<TEntity> Select(Expression<Func<TEntity, object>> select)
Expand All @@ -52,5 +51,21 @@ public IODataQueryKey<TEntity> Select(Expression<Func<TEntity, object>> select)

return this;
}

private IODataQueryKey<TEntity> Expand<T>(T query) where T : class
{
if (_hasMultyExpands)
{
_stringBuilder.Merge(ODataOptionNames.Expand, QuerySeparators.Main, $"{QuerySeparators.Comma}{query}");
}
else
{
_stringBuilder.Append($"{ODataOptionNames.Expand}{QuerySeparators.EqualSign}{query}{QuerySeparators.Main}");
}

_hasMultyExpands = true;

return this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public StringBuilder Query
{
if (_odataQueryExpand?.Query?.Length > 0)
{
return _stringBuilder.Append($"({_odataQueryExpand.Query})");
return _stringBuilder.Append($"{QuerySeparators.LeftBracket}{_odataQueryExpand.Query}{QuerySeparators.RigthBracket}");
}

return _stringBuilder;
Expand All @@ -39,7 +39,7 @@ public IODataQueryExpand<TNestedEntity> For<TNestedEntity>(Expression<Func<TEnti

if (_odataQueryExpand?.Query?.Length > 0)
{
_stringBuilder.Append($"({_odataQueryExpand.Query}){QuerySeparators.Comma}{query}");
_stringBuilder.Append($"{QuerySeparators.LeftBracket}{_odataQueryExpand.Query}{QuerySeparators.RigthBracket}{QuerySeparators.Comma}{query}");
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace OData.QueryBuilder.Conventions.Constants
{
internal class ODataLogicalOperations
{
public const string And = "and";
public const string Or = "or";
public const string Equal = "eq";
public const string Not = "not";
public const string NotEqual = "ne";
public const string LessThan = "lt";
public const string LessThanOrEqual = "le";
public const string GreaterThan = "gt";
public const string GreaterThanOrEqual = "ge";
}
}
15 changes: 13 additions & 2 deletions src/OData.QueryBuilder/Conventions/Constants/QuerySeparators.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace OData.QueryBuilder.Conventions.Constants
using System;

namespace OData.QueryBuilder.Conventions.Constants
{
internal struct QuerySeparators
{
Expand All @@ -12,7 +14,16 @@ internal struct QuerySeparators

public const char Slash = '/';

public const string Comma = ",";
public const char Comma = ',';

public const char DollarSign = '$';

public const char RigthBracket = ')';

public const char LeftBracket = '(';

[Obsolete("Remove after upgrade to netstandard 2.1")]
public const string StringComma = ",";

public const char Dot = '.';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected override string VisitNewExpression(NewExpression newExpression)
names[i] = newExpression.Members[i].Name;
}

return string.Join(QuerySeparators.Comma, names);
return string.Join(QuerySeparators.StringComma, names);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OData.QueryBuilder.Conventions.Functions;
using OData.QueryBuilder.Conventions.Constants;
using OData.QueryBuilder.Conventions.Functions;
using OData.QueryBuilder.Conventions.Operators;
using OData.QueryBuilder.Extensions;
using OData.QueryBuilder.Options;
Expand Down Expand Up @@ -41,7 +42,7 @@ protected override string VisitBinaryExpression(BinaryExpression binaryExpressio
}

return hasParenthesis ?
$"({left} {binaryExpression.NodeType.ToODataOperator()} {right})"
$"{QuerySeparators.LeftBracket}{left} {binaryExpression.NodeType.ToODataOperator()} {right}{QuerySeparators.RigthBracket}"
:
$"{left} {binaryExpression.NodeType.ToODataOperator()} {right}";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ protected override string VisitMethodCallExpression(MethodCallExpression methodC
var ascending0 = VisitExpression(methodCallExpression.Arguments[0]);

var ascendingQuery = VisitExpression(methodCallExpression.Object as MethodCallExpression);
var ascendingQueryComma = ascendingQuery == default ? string.Empty : QuerySeparators.Comma;
var ascendingQueryComma = ascendingQuery == default ? string.Empty : QuerySeparators.StringComma;

return $"{ascendingQuery}{ascendingQueryComma}{ascending0} {QuerySorts.Asc}";
case nameof(ISortFunction.Descending):
var descending0 = VisitExpression(methodCallExpression.Arguments[0]);

var descendingQuery = VisitExpression(methodCallExpression.Object as MethodCallExpression);
var descendingQueryComma = descendingQuery == default ? string.Empty : QuerySeparators.Comma;
var descendingQueryComma = descendingQuery == default ? string.Empty : QuerySeparators.StringComma;

return $"{descendingQuery}{descendingQueryComma}{descending0} {QuerySorts.Desc}";
default:
Expand Down
Loading

0 comments on commit 7258abc

Please sign in to comment.