Skip to content

Commit 94401cd

Browse files
committed
add SeparatedByComma/NewLine
1 parent d16aa89 commit 94401cd

12 files changed

Lines changed: 357 additions & 38 deletions

File tree

Signum.Engine/DynamicQuery/DQueryable.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ static LambdaExpression SelectTupleConstructor(BuildExpressionContext context, H
149149
if (str.HasText())
150150
throw new ApplicationException(str);
151151

152-
List<Expression> expressions = tokens.Select(t => t.BuildExpression(context)).ToList();
152+
List<Expression> expressions = tokens.Select(t => t.BuildExpression(context, searchToArray: true)).ToList();
153153
Expression ctor = TupleReflection.TupleChainConstructor(expressions);
154154

155155
var pe = Expression.Parameter(ctor.Type);
@@ -865,15 +865,15 @@ static LambdaExpression ResultSelectSelectorAndContext(BuildExpressionContext co
865865
if (isQueryable)
866866
{
867867
var tempContext = new BuildExpressionContext(keyTupleType, pk, rootKeyTokens.Select((kqt, i) => KeyValuePair.Create(kqt, new ExpressionBox(TupleReflection.TupleChainProperty(pk, i)))).ToDictionary());
868-
resultExpressions.AddRange(redundantKeyTokens.Select(t => KeyValuePair.Create(t, t.BuildExpression(tempContext))));
868+
resultExpressions.AddRange(redundantKeyTokens.Select(t => KeyValuePair.Create(t, t.BuildExpression(tempContext, searchToArray: true))));
869869
}
870870
else
871871
{
872872
var first = Expression.Call(miFirstE.MakeGenericMethod(typeof(object)), pe);
873873

874874
resultExpressions.AddRange(redundantKeyTokens.Select(t =>
875875
{
876-
var exp = t.BuildExpression(context);
876+
var exp = t.BuildExpression(context, searchToArray: true);
877877
var replaced = ExpressionReplacer.Replace(exp,
878878
new Dictionary<ParameterExpression, Expression>
879879
{
@@ -1101,7 +1101,7 @@ public static ResultTable ToResultTable<T>(this DEnumerableCount<T> collection,
11011101

11021102
var rc = new ResultColumn(c, array);
11031103

1104-
if (c.Token.Type.IsLite() || isMultiKeyGrupping && c.Token is not AggregateToken)
1104+
if ((c.Token.Type.IsLite() || isMultiKeyGrupping && c.Token is not AggregateToken) && c.Token.HasToArray() == null)
11051105
rc.CompressUniqueValues = true;
11061106

11071107
return rc;

Signum.Engine/Json/FilterJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public class ColumnTS
130130

131131
public Column ToColumn(QueryDescription qd, bool canAggregate)
132132
{
133-
var queryToken = QueryUtils.Parse(token, qd, SubTokensOptions.CanElement | (canAggregate ? SubTokensOptions.CanAggregate : SubTokensOptions.CanOperation));
133+
var queryToken = QueryUtils.Parse(token, qd, SubTokensOptions.CanElement | SubTokensOptions.CanToArray | (canAggregate ? SubTokensOptions.CanAggregate : SubTokensOptions.CanOperation));
134134

135135
return new Column(queryToken, displayName ?? queryToken.NiceName());
136136
}

Signum.Entities/DynamicQuery/QueryUtils.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ public static LambdaExpression CreateOrderLambda(QueryToken token, BuildExpressi
427427
if (QueryToken.IsCollection(token.Type))
428428
return "Collections can not be ordered";
429429

430+
if (token.HasToArray() != null)
431+
return "ToArray can not be ordered";
432+
430433
if (token.HasAllOrAny())
431434
return "'{0}', '{1}', '{2}' or '{3}' can not be ordered".FormatWith(
432435
CollectionAnyAllType.All.NiceToString(),
@@ -599,4 +602,5 @@ public enum SubTokensOptions
599602
CanAnyAll = 2,
600603
CanElement = 4,
601604
CanOperation = 8,
605+
CanToArray = 16,
602606
}

Signum.Entities/DynamicQuery/Tokens/CollectionElementToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ protected override Expression BuildExpressionInternal(BuildExpressionContext con
127127
public static List<CollectionElementToken> GetElements(HashSet<QueryToken> allTokens)
128128
{
129129
return allTokens
130-
.SelectMany(t => t.Follow(tt => tt.Parent))
130+
.SelectMany(t => (t.HasToArray() ?? t).Follow(tt => tt.Parent))
131131
.OfType<CollectionElementToken>()
132132
.Distinct()
133133
.OrderBy(a => a.FullKey().Length)
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
using Signum.Entities.Reflection;
2+
using Signum.Utilities.Reflection;
3+
using System.ComponentModel;
4+
5+
namespace Signum.Entities.DynamicQuery;
6+
7+
public class CollectionToArrayToken : QueryToken
8+
{
9+
public CollectionToArrayType ToArrayType { get; private set; }
10+
11+
QueryToken parent;
12+
public override QueryToken? Parent => parent;
13+
14+
readonly Type elementType;
15+
internal CollectionToArrayToken(QueryToken parent, CollectionToArrayType toArrayType)
16+
{
17+
elementType = parent.Type.ElementType()!;
18+
if (elementType == null)
19+
throw new InvalidOperationException("not a collection");
20+
21+
this.ToArrayType = toArrayType;
22+
23+
this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
24+
}
25+
26+
public override Type Type
27+
{
28+
get { return elementType.BuildLiteNullifyUnwrapPrimaryKey(new[] { this.GetPropertyRoute()! }); }
29+
}
30+
31+
public override string ToString()
32+
{
33+
return ToArrayType.NiceToString();
34+
}
35+
36+
public override string Key
37+
{
38+
get { return ToArrayType.ToString(); }
39+
}
40+
41+
protected override List<QueryToken> SubTokensOverride(SubTokensOptions options)
42+
{
43+
var st = SubTokensBase(Type, options, GetImplementations());
44+
45+
var ept = MListElementPropertyToken.AsMListEntityProperty(this.parent);
46+
if (ept != null)
47+
{
48+
var mleType = MListElementPropertyToken.MListElementType(ept);
49+
50+
st.Add(new MListElementPropertyToken(this, mleType.GetProperty("RowId")!, ept.PropertyRoute, "RowId", () => QueryTokenMessage.RowId.NiceToString()) { Priority = -5 });
51+
52+
if (MListElementPropertyToken.HasAttribute(ept.PropertyRoute, typeof(PreserveOrderAttribute)))
53+
st.Add(new MListElementPropertyToken(this, mleType.GetProperty("RowOrder")!, ept.PropertyRoute, "RowOrder", () => QueryTokenMessage.RowOrder.NiceToString()) { Priority = -5 });
54+
}
55+
56+
return st;
57+
}
58+
59+
public override Implementations? GetImplementations()
60+
{
61+
return parent.GetElementImplementations();
62+
}
63+
64+
public override string? Format
65+
{
66+
get
67+
{
68+
69+
if (Parent is ExtensionToken et && et.IsProjection)
70+
return et.ElementFormat;
71+
72+
return parent.Format;
73+
}
74+
}
75+
76+
public override string? Unit
77+
{
78+
get
79+
{
80+
81+
if (Parent is ExtensionToken et && et.IsProjection)
82+
return et.ElementUnit;
83+
84+
return parent.Unit;
85+
}
86+
}
87+
88+
public override string? IsAllowed()
89+
{
90+
return parent.IsAllowed();
91+
}
92+
93+
94+
public override CollectionToArrayToken? HasToArray() => this;
95+
96+
public override PropertyRoute? GetPropertyRoute()
97+
{
98+
if (parent is ExtensionToken et && et.IsProjection)
99+
return et.GetElementPropertyRoute();
100+
101+
PropertyRoute? pr = this.parent!.GetPropertyRoute();
102+
if (pr != null && pr.Type.ElementType() != null)
103+
return pr.Add("Item");
104+
105+
return pr;
106+
}
107+
108+
public override string NiceName()
109+
{
110+
return this.parent.NiceName();
111+
}
112+
113+
public override QueryToken Clone()
114+
{
115+
return new CollectionToArrayToken(parent.Clone(), ToArrayType);
116+
}
117+
118+
protected override Expression BuildExpressionInternal(BuildExpressionContext context)
119+
{
120+
throw new InvalidOperationException("ToArrayToken should have a replacement at this stage");
121+
}
122+
123+
124+
public override string TypeColor
125+
{
126+
get { return "#0000FF"; }
127+
}
128+
129+
public static int MaxToArrayValues = 100;
130+
131+
static MethodInfo miToArray = ReflectionTools.GetMethodInfo(() => Enumerable.ToArray<int>(null!)).GetGenericMethodDefinition();
132+
static MethodInfo miToDistict = ReflectionTools.GetMethodInfo(() => Enumerable.Distinct<int>(null!)).GetGenericMethodDefinition();
133+
static MethodInfo miSelect = ReflectionTools.GetMethodInfo(() => Enumerable.Select<int, long>(null!, a => a)).GetGenericMethodDefinition();
134+
static MethodInfo miTake = ReflectionTools.GetMethodInfo(() => Enumerable.Take<int>(null!, 0)).GetGenericMethodDefinition();
135+
static MethodInfo miSelectMany = ReflectionTools.GetMethodInfo(() => Enumerable.SelectMany<string, char>(null!, a => a)).GetGenericMethodDefinition();
136+
137+
internal static Expression BuildToArrayExpression(QueryToken token, CollectionToArrayToken cta, BuildExpressionContext context)
138+
{
139+
var ept = MListElementPropertyToken.AsMListEntityProperty(cta.Parent!);
140+
141+
BuildExpressionContext subCtx;
142+
Expression query;
143+
if(ept != null)
144+
{
145+
var collection = MListElementPropertyToken.BuildMListElements(ept, context);
146+
query = collection;
147+
Type mleType = collection.Type.ElementType()!;
148+
var param = Expression.Parameter(mleType, mleType.Name.Substring(0, 1).ToLower());
149+
subCtx = new BuildExpressionContext(mleType, param, new Dictionary<QueryToken, ExpressionBox>
150+
{
151+
{ cta, new ExpressionBox(param, mlistElementRoute: cta.GetPropertyRoute()) }
152+
});
153+
}
154+
else
155+
{
156+
var collection = cta.Parent!.BuildExpression(context);
157+
query = collection;
158+
Type elemeType = collection.Type.ElementType()!;
159+
var param = Expression.Parameter(elemeType, elemeType.Name.Substring(0, 1).ToLower());
160+
subCtx = new BuildExpressionContext(elemeType, param, new Dictionary<QueryToken, ExpressionBox>()
161+
{
162+
{ cta, new ExpressionBox(param.BuildLiteNullifyUnwrapPrimaryKey(new[] { cta.GetPropertyRoute()! }))}
163+
});
164+
}
165+
166+
Expression body;
167+
if (token is CollectionToArrayToken)
168+
{
169+
body = subCtx.Parameter.BuildLite();
170+
}
171+
else
172+
{
173+
var cets = token.Follow(a => a.Parent).TakeWhile(a => a != cta).OfType<CollectionElementToken>().Reverse().ToList();
174+
foreach (var ce in cets)
175+
{
176+
var ept2 = MListElementPropertyToken.AsMListEntityProperty(ce.Parent!);
177+
if (ept2 != null)
178+
{
179+
var collection = MListElementPropertyToken.BuildMListElements(ept2, subCtx);
180+
Type mleType = collection.Type.ElementType()!;
181+
query = Expression.Call(miSelectMany.MakeGenericMethod(query.Type.ElementType()!, mleType), query, Expression.Lambda(collection, subCtx.Parameter));
182+
var param = Expression.Parameter(mleType, mleType.Name.Substring(0, 1).ToLower());
183+
subCtx = new BuildExpressionContext(mleType, param, new Dictionary<QueryToken, ExpressionBox>()
184+
{
185+
{ ce, new ExpressionBox(param, mlistElementRoute: ce.GetPropertyRoute())}
186+
});
187+
}
188+
else
189+
{
190+
var collection = ce.Parent!.BuildExpression(subCtx);
191+
Type elementType = collection.Type.ElementType()!;
192+
query = Expression.Call(miSelectMany.MakeGenericMethod(query.Type.ElementType()!, elementType), query, Expression.Lambda(collection, subCtx.Parameter));
193+
194+
var param = Expression.Parameter(elementType, elementType.Name.Substring(0, 1).ToLower());
195+
subCtx = new BuildExpressionContext(elementType, param, new Dictionary<QueryToken, ExpressionBox>()
196+
{
197+
{ ce, new ExpressionBox(param.BuildLiteNullifyUnwrapPrimaryKey(new[] { ce.GetPropertyRoute()! }))}
198+
});
199+
}
200+
}
201+
202+
body = token.BuildExpression(subCtx);
203+
}
204+
205+
if (body != subCtx.Parameter)
206+
query = Expression.Call(miSelect.MakeGenericMethod(query.Type.ElementType()!, body.Type), query, Expression.Lambda(body, subCtx.Parameter));
207+
208+
if (cta.ToArrayType == CollectionToArrayType.SeparatedByCommaDistict ||
209+
cta.ToArrayType == CollectionToArrayType.SeparatedByNewLineDistict)
210+
query = Expression.Call(miToDistict.MakeGenericMethod(token.Type), query);
211+
212+
query = Expression.Call(miTake.MakeGenericMethod(token.Type), query, Expression.Constant(MaxToArrayValues));
213+
214+
return Expression.Call(miToArray.MakeGenericMethod(token.Type), query);
215+
}
216+
}
217+
218+
[DescriptionOptions(DescriptionOptions.Members)]
219+
public enum CollectionToArrayType
220+
{
221+
SeparatedByComma,
222+
SeparatedByCommaDistict,
223+
SeparatedByNewLine,
224+
SeparatedByNewLineDistict,
225+
}

Signum.Entities/DynamicQuery/Tokens/DateToken.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public override string? Unit
3434

3535
public override Type Type
3636
{
37-
get { return typeof(DateTime?); }
37+
get { return typeof(DateOnly?); }
3838
}
3939

4040
public override string Key
@@ -47,13 +47,13 @@ protected override List<QueryToken> SubTokensOverride(SubTokensOptions options)
4747
return new List<QueryToken>();
4848
}
4949

50-
static PropertyInfo miDate = ReflectionTools.GetPropertyInfo((DateTime d) => d.Date);
50+
static MethodInfo miDate = ReflectionTools.GetMethodInfo((DateTime d) => d.ToDateOnly());
5151

5252
protected override Expression BuildExpressionInternal(BuildExpressionContext context)
5353
{
5454
var exp = parent.BuildExpression(context);
5555

56-
return Expression.Property(exp.UnNullify(), miDate).Nullify();
56+
return Expression.Call(miDate, exp.UnNullify()).Nullify();
5757
}
5858

5959
public override PropertyRoute? GetPropertyRoute()

Signum.Entities/DynamicQuery/Tokens/MListElementPropertyToken.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class MListElementPropertyToken : QueryToken
1919
internal MListElementPropertyToken(QueryToken parent, PropertyInfo pi, PropertyRoute pr, string key, Func<string> nicePropertyName)
2020
{
2121
this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
22-
if (parent is not (CollectionAnyAllToken or CollectionElementToken))
22+
if (parent is not (CollectionAnyAllToken or CollectionElementToken or CollectionToArrayToken))
2323
throw new InvalidOperationException("Unexpected parent");
2424

2525
this.PropertyInfo = pi ?? throw new ArgumentNullException(nameof(pi));

Signum.Entities/DynamicQuery/Tokens/QueryToken.cs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
using Signum.Entities.Reflection;
33
using System.ComponentModel;
44
using System.Collections.Concurrent;
5+
using System.Diagnostics;
56

67
namespace Signum.Entities.DynamicQuery;
78

9+
[DebuggerDisplay("{FullKey(),nq}")]
810
public abstract class QueryToken : IEquatable<QueryToken>
911
{
1012
public int Priority = 0;
@@ -74,11 +76,18 @@ public Func<object, T> GetAccessor<T>(BuildExpressionContext context)
7476
return Expression.Lambda<Func<object, T>>(this.BuildExpression(context), context.Parameter).Compile();
7577
}
7678

77-
public Expression BuildExpression(BuildExpressionContext context)
79+
public Expression BuildExpression(BuildExpressionContext context, bool searchToArray = false)
7880
{
79-
if (context.Replacements != null && context.Replacements.TryGetValue(this, out var result))
81+
if(context.Replacements.TryGetValue(this, out var result))
8082
return result.GetExpression();
8183

84+
if (searchToArray)
85+
{
86+
var cta = this.HasToArray();
87+
if (cta != null)
88+
return CollectionToArrayToken.BuildToArrayExpression(this, cta, context);
89+
}
90+
8291
return BuildExpressionInternal(context);
8392
}
8493

@@ -352,19 +361,30 @@ public static List<QueryToken> StepTokens(QueryToken parent, int decimals)
352361
public static List<QueryToken> CollectionProperties(QueryToken parent, SubTokensOptions options)
353362
{
354363
if (parent.HasAllOrAny())
355-
options &= ~SubTokensOptions.CanElement;
364+
options &= ~(SubTokensOptions.CanElement | SubTokensOptions.CanToArray | SubTokensOptions.CanOperation | SubTokensOptions.CanAggregate);
365+
366+
if (parent.HasToArray() != null)
367+
options &= ~(SubTokensOptions.CanAnyAll | SubTokensOptions.CanToArray | SubTokensOptions.CanOperation | SubTokensOptions.CanAggregate);
356368

357369
List<QueryToken> tokens = new List<QueryToken>() { new CountToken(parent) };
358370

359-
if ((options & SubTokensOptions.CanElement) == SubTokensOptions.CanElement)
371+
if (options.HasFlag(SubTokensOptions.CanElement))
360372
tokens.AddRange(EnumExtensions.GetValues<CollectionElementType>().Select(cet => new CollectionElementToken(parent, cet)));
361373

362-
if ((options & SubTokensOptions.CanAnyAll) == SubTokensOptions.CanAnyAll)
374+
if (options.HasFlag(SubTokensOptions.CanAnyAll))
363375
tokens.AddRange(EnumExtensions.GetValues<CollectionAnyAllType>().Select(caat => new CollectionAnyAllToken(parent, caat)));
364376

377+
if (options.HasFlag(SubTokensOptions.CanToArray))
378+
tokens.AddRange(EnumExtensions.GetValues<CollectionToArrayType>().Select(ctat => new CollectionToArrayToken(parent, ctat)));
379+
365380
return tokens;
366381
}
367382

383+
public virtual CollectionToArrayToken? HasToArray()
384+
{
385+
return Parent?.HasToArray();
386+
}
387+
368388
public virtual bool HasAllOrAny()
369389
{
370390
return Parent != null && Parent.HasAllOrAny();

0 commit comments

Comments
 (0)