Skip to content

Commit

Permalink
contextual shape exclusion
Browse files Browse the repository at this point in the history
  • Loading branch information
quinchs committed Jan 25, 2024
1 parent 23594db commit fe33002
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 58 deletions.
32 changes: 32 additions & 0 deletions src/EdgeDB.Net.QueryBuilder/Grammar/Rules/NodeReducer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using EdgeDB.QueryNodes;

namespace EdgeDB;

internal static class NodeReducer
{
public static void Apply(QueryStringWriter writer, QueryGlobal global, Action<QueryGlobal, QueryStringWriter, Action<QueryNode>?> compile)
{
if (!writer.TryGetLabeled(global.Name, out var markers))
return;

// general rules for reducing nodes:
// - Shapes are not included in function arguments

foreach (var marker in markers)
{
Action<QueryNode>? modifier = marker switch
{
_ when marker.Parent?.Type is MarkerType.Function => ApplyShapeReducer,
_ => null
};

marker.Replace(writer => compile(global, writer, modifier));
}
}

private static void ApplyShapeReducer(QueryNode node)
{
if (node.Context is SelectContext sctx)
sctx.IncludeShape = false;
}
}
43 changes: 43 additions & 0 deletions src/EdgeDB.Net.QueryBuilder/Lexical/Marker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,49 @@ internal sealed class Marker
public int Position { get; private set; }
public int Size { get; }

/// <summary>
/// Gets the closest parent to this marker
/// </summary>
public Marker? Parent
{
get
{
Marker? bestMatch = null;

foreach (var marker in _writer.Labels.Values.SelectMany(x => x))
{
if(marker == this)
continue;

if (marker.Position <= Position && marker.Size >= Size)
{
if (bestMatch is null)
{
bestMatch = marker;
continue;
}

if (marker.Position > bestMatch.Position || marker.Size < bestMatch.Size)
bestMatch = marker;
}
}

return bestMatch;
}
}

/// <summary>
/// Gets a collection of parents of this marker, with the first being the closest parent; and so on.
/// </summary>
public IOrderedEnumerable<Marker> Parents
{
get
{
return _writer.Labels.Values.SelectMany(x => x).Where(x => x != this && x.Position <= Position && x.Size >= Size)
.OrderBy(x => Position - x.Position + x.Size);
}
}

private readonly QueryStringWriter _writer;

internal Marker(MarkerType type, QueryStringWriter writer, int size, int position)
Expand Down
2 changes: 1 addition & 1 deletion src/EdgeDB.Net.QueryBuilder/Lexical/MarkerType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ public enum MarkerType
{
Global,
Function,

Shape
}
115 changes: 62 additions & 53 deletions src/EdgeDB.Net.QueryBuilder/Lexical/QueryStringWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace EdgeDB;
internal class QueryStringWriter
{
private sealed class PositionedQueryStringWriter(int position, QueryStringWriter parent)
: QueryStringWriter(parent._builder, parent._chunks, parent._labels)
: QueryStringWriter(parent._builder, parent._chunks, parent.Labels)
{
protected override void OnExternalWrite(int position1, int length)
{
Expand Down Expand Up @@ -36,7 +36,7 @@ protected override void WriteInternal(string str)
}
}

private readonly Dictionary<string, List<Marker>> _labels;
internal readonly Dictionary<string, List<Marker>> Labels;
private readonly StringBuilder _builder;
private readonly SortedList<int, IntrospectionChunk> _chunks;

Expand All @@ -56,7 +56,7 @@ private QueryStringWriter(StringBuilder stringBuilder, SortedList<int, Introspec
{
_builder = stringBuilder;
_chunks = chunks;
_labels = labels;
Labels = labels;
}

[return: NotNullIfNotNull(nameof(o))]
Expand All @@ -71,7 +71,7 @@ private QueryStringWriter(StringBuilder stringBuilder, SortedList<int, Introspec

protected void UpdateLabels(int pos, int sz)
{
foreach (var label in _labels.Values.SelectMany(x => x).Where(x => x.Position <= pos))
foreach (var label in Labels.Values.SelectMany(x => x).Where(x => x.Position <= pos))
{
label.Update(sz);
}
Expand Down Expand Up @@ -111,8 +111,8 @@ public QueryStringWriter GetPositionalWriter(int index = -1)

public QueryStringWriter Label(MarkerType type, string name, Value value)
{
if (!_labels.TryGetValue(name, out var labels))
_labels[name] = labels = new();
if (!Labels.TryGetValue(name, out var labels))
Labels[name] = labels = new();

var pos = Position;
Append(value);
Expand All @@ -125,8 +125,8 @@ public QueryStringWriter Label(MarkerType type, string value)

public QueryStringWriter Label(MarkerType type, string name, Action<string, QueryStringWriter> func)
{
if (!_labels.TryGetValue(name, out var labels))
_labels[name] = labels = new();
if (!Labels.TryGetValue(name, out var labels))
Labels[name] = labels = new();

var pos = Position;
func(name, this);
Expand All @@ -135,7 +135,7 @@ public QueryStringWriter Label(MarkerType type, string name, Action<string, Quer
}

public bool TryGetLabeled(string name, [NotNullWhen(true)] out List<Marker>? markers)
=> _labels.TryGetValue(name, out markers);
=> Labels.TryGetValue(name, out markers);

public int IndexOf(string value, bool ignoreCase = false, int startIndex = 0)
{
Expand Down Expand Up @@ -319,17 +319,20 @@ public QueryStringWriter Wrapped(Value value, string chars = "()")
public QueryStringWriter Shape<T>(params T[] elements)
where T : notnull
{
Append('{');

for (var i = 0; i != elements.Length;)
Label(MarkerType.Shape, $"shape_{elements.GetHashCode()}", (name, writer) =>
{
Append(elements[i++]);
writer.Append('{');
if (i != elements.Length)
Append(", ");
}
for (var i = 0; i != elements.Length;)
{
writer.Append(elements[i++]);
Append('}');
if (i != elements.Length)
writer.Append(", ");
}
writer.Append('}');
});

return this;
}
Expand All @@ -340,32 +343,35 @@ public QueryStringWriter Shape<T>(IEnumerable<T> elements, Action<QueryStringWri
if (shapeChars.Length is not 2)
throw new ArgumentOutOfRangeException(nameof(shapeChars), "must contain 2 characters");

Append(shapeChars[0]);
Label(MarkerType.Shape, $"shape_{elements.GetHashCode()}", (name, wt) =>
{
wt.Append(shapeChars[0]);
using var enumerator = elements.GetEnumerator();
using var enumerator = elements.GetEnumerator();
enumerator.MoveNext();
enumerator.MoveNext();
loop:
loop:
// check for empty entries
var i = Position;
writer(this, enumerator.Current);
// check for empty entries
var i = Position;
writer(wt, enumerator.Current);
// if nothing was written, continue the iteration without adding a delimiter
if (i == Position)
{
enumerator.MoveNext();
goto loop;
}
// if nothing was written, continue the iteration without adding a delimiter
if (i == Position)
{
enumerator.MoveNext();
goto loop;
}
if (enumerator.MoveNext())
{
Append(", ");
goto loop;
}
if (enumerator.MoveNext())
{
wt.Append(", ");
goto loop;
}
Append(shapeChars[1]);
wt.Append(shapeChars[1]);
});

return this;
}
Expand All @@ -392,30 +398,33 @@ public FunctionArg(Value value, string? named = null)

public QueryStringWriter Function(object function, params FunctionArg[] args)
{
Append(function).Append('(');

for (var i = 0; i < args.Length;)
Label(MarkerType.Function, $"func_{function}_{args.GetHashCode()}", (name, writer) =>
{
var arg = args[i++];
writer.Append(function).Append('(');
var pos = Position;
for (var i = 0; i < args.Length;)
{
var arg = args[i++];
Append(arg.Value);
var pos = Position;
if (pos == Position)
continue;
writer.Append(arg.Value);
// append the named part if its specified
if (arg.Named is not null)
GetPositionalWriter(pos + 1)
.Append(arg.Named)
.Append(" := ");
if (pos == Position)
continue;
if (i != args.Length)
Append(", ");
}
// append the named part if its specified
if (arg.Named is not null)
writer.GetPositionalWriter(pos + 1)
.Append(arg.Named)
.Append(" := ");
if (i != args.Length)
writer.Append(", ");
}
Append(')');
writer.Append(')');
});

return this;
}
Expand Down
2 changes: 1 addition & 1 deletion src/EdgeDB.Net.QueryBuilder/QueryNodes/SelectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public override void FinalizeQuery(QueryStringWriter writer)
TranslateExpression(Context.Expression, expressionWriter);
}
else
writer.Insert(0, $"select {Context.SelectName ?? OperatingType.GetEdgeDBTypeName()}");
writer.Append($"select {Context.SelectName ?? OperatingType.GetEdgeDBTypeName()}");
}
else if (_shape is not null)
{
Expand Down
8 changes: 5 additions & 3 deletions src/EdgeDB.Net.QueryBuilder/QueryNodes/WithNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ private bool TryReduceNode(QueryStringWriter writer, QueryGlobal global)
if (EdgeDBTypeUtils.IsLink(operatingType, out _, out _) && bannedTypes.Contains(operatingType))
continue;

node.ReplaceSubqueryAsLiteral(writer, global, CompileGlobalValue);
NodeReducer.Apply(writer, global, CompileGlobalValue);

//node.ReplaceSubqueryAsLiteral(writer, global, (global, writer) => CompileGlobalValue(global, writer, ShapeReducer.Create(writer, global)));
count--;
}

Expand Down Expand Up @@ -72,12 +74,12 @@ public override void FinalizeQuery(QueryStringWriter writer)
}
}

private void CompileGlobalValue(QueryGlobal global, QueryStringWriter writer)
private void CompileGlobalValue(QueryGlobal global, QueryStringWriter writer, Action<QueryNode>? preFinalizerModifier = null)
{
// if its a query builder, build it and add it as a sub-query.
if (global.Value is IQueryBuilder queryBuilder)
{
writer.Wrapped(writer => queryBuilder.WriteTo(writer, this));
writer.Wrapped(writer => queryBuilder.WriteTo(writer, this, preFinalizerModifier: preFinalizerModifier));
return;
}

Expand Down

0 comments on commit fe33002

Please sign in to comment.