Last active
March 13, 2025 05:26
-
-
Save noam-honig/4619219f137d0b416cd29fef89d52404 to your computer and use it in GitHub Desktop.
Inner Select Helper With Child Rows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using Firefly.Box; | |
using Firefly.Box.Data.Advanced; | |
using ENV.Data.DataProvider; | |
using Firefly.Box.Data.DataProvider; | |
namespace ENV.Data | |
{ | |
public class ChildRowsColumn : TextColumn | |
{ | |
ColumnBase[] _columns; | |
public ChildRowsColumn(ColumnBase[] columns) | |
{ | |
_columns = columns; | |
DbReadOnly = true; | |
} | |
public long ForEachRow(Action value) | |
{ | |
var rows = Value.Trim().ToString().Split(new string[] { InnerSelectHelper.RowSeperator }, StringSplitOptions.RemoveEmptyEntries); | |
foreach (var row in rows) | |
{ | |
var cols = row.Split(new string[] { InnerSelectHelper.ColumnSeperator }, StringSplitOptions.None); | |
for (int i = 0; i < cols.Length; i++) | |
{ | |
_columns[i].Value = _columns[i].GetValueFromDB(new MyValueLoader(cols[i])); | |
} | |
value(); | |
} | |
return rows.Length; | |
} | |
public T[] Map<T>(Func<T> value) | |
{ | |
var result = new List<T>(); | |
ForEachRow(() => result.Add(value())); | |
return result.ToArray(); | |
} | |
class MyValueLoader : IValueLoader | |
{ | |
string _value; | |
public MyValueLoader(string value) | |
{ | |
_value = value; | |
} | |
public bool GetBoolean() | |
{ | |
throw new NotImplementedException(); | |
} | |
public byte[] GetByteArray() | |
{ | |
throw new NotImplementedException(); | |
} | |
public DateTime GetDateTime() | |
{ | |
throw new NotImplementedException(); | |
} | |
public Number GetNumber() | |
{ | |
return Number.Parse(_value); | |
} | |
public string GetString() | |
{ | |
if (IsNull()) | |
return null; | |
return _value; | |
} | |
public TimeSpan GetTimeSpan() | |
{ | |
throw new NotImplementedException(); | |
} | |
public bool IsNull() | |
{ | |
return _value==InnerSelectHelper.NullIndicator; | |
} | |
} | |
} | |
public class InnerSelectHelper | |
{ | |
internal static string ColumnSeperator = "{|}", | |
RowSeperator = "{||}", NullIndicator = "{|null|}"; | |
public void TurnToCount(NumberColumn column, Entity childEntity, FilterBase where) | |
{ | |
ApplyTo("count (*)", column, childEntity, where); | |
} | |
public void TurnToSum(NumberColumn column, NumberColumn childColumn, FilterBase where) | |
{ | |
ApplyTo("sum (" + childColumn.Name + ")", column, childColumn.Entity, where); | |
} | |
public void TurnToGetValue<T>(TypedColumnBase<T> column, TypedColumnBase<T> childColumn, FilterBase where, Sort orderBy = null) | |
{ | |
ApplyTo("top 1 " + childColumn.Name, column, childColumn.Entity, where, orderBy); | |
} | |
public void TurnToExist(BoolColumn column, Entity childEntity, FilterBase where) | |
{ | |
ApplyTo("max (1)", column, childEntity, where); | |
} | |
public ChildRowsColumn ChildRows(FilterBase where, params ColumnBase[] columns) | |
{ | |
return ChildRows(where, null, columns); | |
} | |
public ChildRowsColumn ChildRows(FilterBase where, Sort orderBy, params ColumnBase[] columns) | |
{ | |
var entity = columns[0].Entity; | |
var result = new ChildRowsColumn(columns) { Caption = entity.Caption }; | |
var sb = new StringBuilder(); | |
foreach (var column in columns) | |
{ | |
if (sb.Length > 0) | |
{ | |
sb.Append($"+'{ColumnSeperator}'+"); | |
} | |
sb.Append($"ISNULL(trim(CONVERT(varchar, {column.Name})),'{NullIndicator}')"); | |
} | |
var ob = ""; | |
if (orderBy != null && orderBy.Segments.Count > 0) | |
{ | |
ob = $"within group ({BuildOrderBy(orderBy)})"; | |
} | |
ApplyTo($"STRING_AGG ({sb.ToString()}, '{RowSeperator}') {ob}", result, entity, where, defaultValue: "''"); | |
_controller.Columns.Add(result); | |
return result; | |
} | |
public class GetValueHelperClass | |
{ | |
private FilterBase _where; | |
private Sort _orderBy; | |
private InnerSelectHelper _parent; | |
public GetValueHelperClass(FilterBase where, Sort orderBy, InnerSelectHelper parent) | |
{ | |
this._where = where; | |
this._orderBy = orderBy; | |
this._parent = parent; | |
} | |
public void TurnToGetValue<T>(TypedColumnBase<T> column, TypedColumnBase<T> childColumn) | |
{ | |
_parent.ApplyTo("top 1 " + childColumn.Name, column, childColumn.Entity, _where, _orderBy); | |
} | |
} | |
public GetValueHelperClass GetValueHelper(FilterBase where, Sort orderBy = null) | |
{ | |
return new GetValueHelperClass(where, orderBy, this); | |
} | |
void ApplyTo(string agregateStatement, ColumnBase resultColumn, Firefly.Box.Data.Entity childEntity, FilterBase where, Sort orderBy = null, string defaultValue = "0") | |
{ | |
_controller.Load += () => | |
{ | |
resultColumn.Name = GetSQL(agregateStatement, childEntity, where, orderBy, defaultValue); | |
}; | |
resultColumn.DbReadOnly = true; | |
if (_controller != null) | |
_controller.From.Columns.Add(resultColumn); | |
} | |
List<Firefly.Box.Data.Entity> _relevantEntities = new List<Firefly.Box.Data.Entity>(); | |
Dictionary<Firefly.Box.Data.Entity, string> _aliases = new Dictionary<Firefly.Box.Data.Entity, string>(); | |
bool _isOracle; | |
ControllerBase _controller; | |
public InnerSelectHelper(ControllerBase controller = null) | |
{ | |
_controller = controller; | |
if (_controller != null) | |
{ | |
var envEntity = controller.From as ENV.Data.Entity; | |
if (envEntity != null) | |
{ | |
var dp = envEntity.DataProvider as DynamicSQLSupportingDataProvider; | |
if (dp != null) | |
_isOracle = dp.IsOracle; | |
} | |
_relevantEntities.Add(controller.From); | |
foreach (var item in controller.GetRelations()) | |
{ | |
if (item.Type == RelationType.Join || item.Type == RelationType.OuterJoin) | |
{ | |
if (_aliases.Count == 0) | |
{ | |
_aliases.Add(controller.From, "A"); | |
} | |
_aliases.Add(item.From, ((char)('A' + _aliases.Count)).ToString()); | |
_relevantEntities.Add(item.From); | |
} | |
} | |
if (_aliases.Count == 0) | |
_aliases.Add(controller.From, Entity.GetEntityName(controller.From)); | |
} | |
} | |
//public readonly Firefly.Box.Data.Advanced.FilterCollection Where = new Firefly.Box.Data.Advanced.FilterCollection(); | |
string GetSQL(string agregateStatement, Firefly.Box.Data.Entity innerEntity, FilterBase whereFilter, Sort orderBy, string defaultValue = "0") | |
{ | |
var _select = @" | |
isnull(( | |
select " + agregateStatement + @" | |
from " + Entity.GetEntityName(innerEntity); | |
string where = ""; | |
var re = new List<Firefly.Box.Data.Entity>(_relevantEntities); | |
re.Add(innerEntity); | |
if (whereFilter != null) | |
{ | |
var x = FilterBase.GetIFilter(whereFilter, false, re.ToArray()); | |
var p = new NoParametersFilterItemSaver(true, _isOracle ? OracleClientEntityDataProvider.DateTimeStringFormat : SQLClientEntityDataProvider.DateTimeStringFormat, DummyDateTimeCollector.Instance, _isOracle ? OracleClientEntityDataProvider.DateTimeStringFormatForToString : null); | |
var z = new SQLFilterConsumer( | |
p, | |
y => | |
{ | |
string s; | |
if (_aliases.TryGetValue(y.Entity, out s)) | |
return s + "." + y.Name; | |
else if (y.Entity == innerEntity) | |
return y.Name; | |
throw new InvalidOperationException("Only expected columns from main table or inner table"); | |
}, false, new dummySqlFilterHelper(p)) | |
{ NewLinePrefix = "" }; | |
x.AddTo(z); | |
where = " where " + z.Result.ToString(); | |
} | |
where = where + BuildOrderBy(orderBy); | |
return "=" + _select + where + $"),{defaultValue})"; | |
} | |
static string BuildOrderBy(Sort orderBy) | |
{ | |
string result = ""; | |
if (orderBy != null && orderBy.Segments.Count > 0) | |
{ | |
string s = ""; | |
foreach (var col in orderBy.Segments) | |
{ | |
if (s.Length > 0) | |
s += ", "; | |
s += col.Column.Name; | |
var dir = col.Direction == SortDirection.Descending; | |
if (orderBy.Reversed) | |
dir = !dir; | |
if (dir) | |
s += " desc"; | |
} | |
result += " Order By " + s; | |
} | |
return result; | |
} | |
public class OracleInnerSelectHelper | |
{ | |
Firefly.Box.Data.Entity _from; | |
public OracleInnerSelectHelper(Firefly.Box.Data.Entity From) | |
{ | |
_from = From; | |
} | |
public void TurnToGetValue<T>(TypedColumnBase<T> column, TypedColumnBase<T> childColumn, FilterBase where) | |
{ | |
column.Name = "(select " + childColumn.Name + | |
" from " + Entity.GetEntityName(childColumn.Entity) + | |
" where " + ENV.Utilities.FilterHelper.ToSQLWhere(where, false, _from, childColumn.Entity) + | |
" fetch next 1 row only)"; | |
if (column is TextColumn) | |
{ | |
column.Name = "nvl(" + column.Name + ", ' ')"; | |
} | |
column.DbReadOnly = true; | |
_from.Columns.Add(column); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment