Skip to content

Instantly share code, notes, and snippets.

@noam-honig
Last active March 13, 2025 05:26
Show Gist options
  • Save noam-honig/4619219f137d0b416cd29fef89d52404 to your computer and use it in GitHub Desktop.
Save noam-honig/4619219f137d0b416cd29fef89d52404 to your computer and use it in GitHub Desktop.
Inner Select Helper With Child Rows
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