Skip to content

Commit

Permalink
Merge branch 'feat/querybuilder-v2' of https://github.com/edgedb/edge…
Browse files Browse the repository at this point in the history
…db-net into feat/querybuilder-v2
  • Loading branch information
Quin Lynch committed May 5, 2024
2 parents 40a3919 + 1fd32f2 commit 017c701
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 42 deletions.
15 changes: 15 additions & 0 deletions src/EdgeDB.Net.QueryBuilder/QueryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ namespace EdgeDB
{
public abstract class QueryContext : IQueryContext
{
/// <summary>
/// References a defined type within the schema.
/// </summary>
/// <typeparam name="TType">The type to reference.</typeparam>
/// <returns>A reference to the type.</returns>
public abstract TType Type<TType>();

/// <summary>
/// References a defined type within the schema.
/// </summary>
/// <typeparam name="TType">The type to reference.</typeparam>
/// <param name="module">The module of the type.</param>
/// <returns>A reference to the type.</returns>
public abstract TType Type<TType>(string module);

/// <summary>
/// References a defined query argument with the given name.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,217 @@ internal class EnumerableMethodTranslator : MethodTranslator
/// <inheritdoc/>
public override Type TranslatorTargetType => typeof(Enumerable);

[MethodName(nameof(Enumerable.All))]
public void All(QueryWriter writer, TranslatedParameter source, TranslatedParameter func)
{
if (func.RawValue is not LambdaExpression lambda)
throw new ArgumentException("Expected a lambda function", nameof(func));

// any reference to the source is replaced with the source itself.
func.Context.ParameterAliases[lambda.Parameters[0]] = source;

writer.Function(
"all",
Defer.This(() => "Enumerable.All translation"),
new FunctionMetadata("std::all"),
func
);
}

[MethodName(nameof(Enumerable.Any))]
public void Any(QueryWriter writer, TranslatedParameter source, TranslatedParameter? func)
{
if (func is null)
{
writer.Append("exists ", source);
return;
}

if (func.RawValue is not LambdaExpression lambda)
throw new ArgumentException("Expected a lambda function", nameof(func));

// any reference to the source is replaced with the source itself.
func.Context.ParameterAliases[lambda.Parameters[0]] = source;

writer.Function(
"any",
Defer.This(() => "Enumerable.Any translation"),
new FunctionMetadata("std::any"),
func
);
}

[MethodName(nameof(Enumerable.Append))]
public void Append(QueryWriter writer, TranslatedParameter source, TranslatedParameter other)
=> writer.Append(source, " union ", other);

[MethodName(nameof(Enumerable.AsEnumerable))]
public void AsEnumerable(QueryWriter writer, TranslatedParameter source)
{} // TODO: maybe do array -> set?

[MethodName(nameof(Enumerable.Average))]
public void Average(QueryWriter writer, TranslatedParameter source, TranslatedParameter? func)
{
if (func is null)
{
writer.Function(
"mean",
Defer.This(() => "Enumerable.Average translation"),
new FunctionMetadata(
"std::mean",
Info.OfMethod(nameof(EdgeDB), nameof(EdgeQL), nameof(EdgeQL.Mean))
),
source
);
return;
}

if (func.RawValue is not LambdaExpression lambda)
throw new ArgumentException("Expected a lambda function", nameof(func));

// any reference to the source is replaced with the source itself.
func.Context.ParameterAliases[lambda.Parameters[0]] = source;

writer.Function(
"mean",
Defer.This(() => "Enumerable.Average translation"),
new FunctionMetadata(
"std::mean",
Info.OfMethod(nameof(EdgeDB), nameof(EdgeQL), nameof(EdgeQL.Mean))
),
func
);
}

[MethodName(nameof(Enumerable.Cast))]
public void Cast(QueryWriter writer, MethodCallExpression method, TranslatedParameter source)
{
var type = method.Method.GetGenericArguments()[0];

if (!EdgeDBTypeUtils.TryGetScalarType(type, out var info))
throw new ArgumentException($"No scalar type information found for {type}", nameof(method));

writer.TypeCast(info.ToString()).Append(source);
}

[MethodName(nameof(Enumerable.Concat))]
public void Concat(QueryWriter writer, TranslatedParameter source, TranslatedParameter other)
=> writer.Append(source, " ++ ", other);

/// <summary>
/// Translates the method <see cref="Enumerable.Contains{TSource}(IEnumerable{TSource}, TSource)"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection.</param>
/// <param name="target">The value to locate within the collection.</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.Contains))]
public void Contains(QueryWriter writer, TranslatedParameter source, TranslatedParameter target)
{
if (source.IsScalarArrayType || source.IsScalarType)
writer.Function("std::contains", source);
else
writer.Append(target).Append(" in ").Append(source);
}

/// <summary>
/// Translates the method <see cref="Enumerable.Count{TSource}(IEnumerable{TSource})"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection to count.</param>
/// <param name="mapper">The mapping function of what to count.</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.Count))]
public void Count(QueryWriter writer, TranslatedParameter source, TranslatedParameter? mapper)
{
if (mapper is null)
{
if (source.IsScalarArrayType || source.IsScalarType)
writer.Function(
"len",
Defer.This(() => "Scalar length of Enumerable.Count"),
new FunctionMetadata(
"std::len",
Info.OfMethod(nameof(EdgeDB), nameof(EdgeQL), nameof(EdgeQL.Len))
),
source
);
else
writer.Function(
"count",
Defer.This(() => "count from Enumerable.Count"),
new FunctionMetadata(
"std::count",
Info.OfMethod(nameof(EdgeDB), nameof(EdgeQL), nameof(EdgeQL.Count))
),
source
);

return;
}

if (mapper.RawValue is not LambdaExpression lambda)
throw new ArgumentException("Expected a lambda function", nameof(mapper));

// any reference to the source is replaced with the source itself.
mapper.Context.ParameterAliases[lambda.Parameters[0]] = source;

writer.Function(
"count",
Defer.This(() => "Enumerable.Count mapping translation"),
new FunctionMetadata(
"std::count",
Info.OfMethod(nameof(EdgeDB), nameof(EdgeQL), nameof(EdgeQL.Count))
),
mapper
);
}

[MethodName(nameof(Enumerable.DefaultIfEmpty))]
public void DefaultIfEmpty(QueryWriter writer, TranslatedParameter source, TranslatedParameter? defaultParam)
{
if (defaultParam is null)
writer.Append(source);
else
writer.Append(source, " ?? ", defaultParam);
}

[MethodName(nameof(Enumerable.Distinct))]
public void Distinct(QueryWriter writer, TranslatedParameter source)
{
writer.Append("distinct ", source);
}

/// <summary>
/// Translates the method <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, Index)"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection.</param>
/// <param name="index">The index of the element to get</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.ElementAt))]
[MethodName(nameof(Enumerable.ElementAtOrDefault))]
public void ElementAt(QueryWriter writer, TranslatedParameter source, TranslatedParameter index)
{
// TODO: figure out set detection, since array_get doesn't work on sets.
writer.Function("array_get", source, index);
}

[MethodName(nameof(Enumerable.Empty))]
public void Empty(QueryWriter writer, MethodCallExpression method)
{
if (!EdgeDBTypeUtils.TryGetScalarType(method.Method.GetGenericArguments()[0], out var info))
throw new ArgumentException("Expected scalar type", nameof(method));

writer.TypeCast(info.ToString()).Append("{}");
}

[MethodName(nameof(Enumerable.Except))]
public void Except(QueryWriter writer, TranslatedParameter source, TranslatedParameter other)
{
writer.Append(source, " except ", other);
}

[MethodName(nameof(Enumerable.Select))]
public void Select(QueryWriter writer, MethodCallExpression method, TranslatedParameter source, TranslatedParameter expressive, ExpressionContext context)
{
Expand Down Expand Up @@ -80,48 +291,6 @@ public void Select(QueryWriter writer, MethodCallExpression method, TranslatedPa
writer.Append(expressive);
}

/// <summary>
/// Translates the method <see cref="Enumerable.Count{TSource}(IEnumerable{TSource})"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection to count.</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.Count))]
public void Count(QueryWriter writer, TranslatedParameter source)
{
if (source.IsScalarArrayType || source.IsScalarType)
writer.Function("std::len", source);
else
writer.Function("std::count", source);
}

/// <summary>
/// Translates the method <see cref="Enumerable.Contains{TSource}(IEnumerable{TSource}, TSource)"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection.</param>
/// <param name="target">The value to locate within the collection.</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.Contains))]
public void Contains(QueryWriter writer, TranslatedParameter source, TranslatedParameter target)
{
if (source.IsScalarArrayType || source.IsScalarType)
writer.Function("std::contains", source);
else
writer.Append(target).Append(" in ").Append(source);
}

/// <summary>
/// Translates the method <see cref="Enumerable.ElementAt{TSource}(IEnumerable{TSource}, Index)"/>.
/// </summary>
/// <param name="writer">The query string writer to append the translated method to.</param>
/// <param name="source">The source collection.</param>
/// <param name="index">The index of the element to get</param>
/// <returns>The EdgeQL equivalent of the method.</returns>
[MethodName(nameof(Enumerable.ElementAt))]
public void ElementAt(QueryWriter writer, TranslatedParameter source, TranslatedParameter index)
=> writer.Function("array_get", source, index);

/// <summary>
/// Translates the method <see cref="Enumerable.FirstOrDefault{TSource}(IEnumerable{TSource})"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ internal sealed class QueryContextTranslator : MethodTranslator<IQueryContext>
{
internal override bool CanTranslate(Type type) => type.IsAssignableTo(typeof(IQueryContext));

[MethodName(nameof(QueryContext.Type))]
public void Type(QueryWriter writer, MethodCallExpression method, TranslatedParameter? module)
{
if (module is not null)
{
module.Context.StringWithoutQuotes = true;
writer.Append(module, "::");
}

writer.Append(method.Method.GetGenericArguments()[0].GetEdgeDBTypeName());
}

[MethodName(nameof(QueryContext.QueryArgument))]
public void QueryArgument(QueryWriter writer, TranslatedParameter param)
{
Expand Down

0 comments on commit 017c701

Please sign in to comment.