public virtual SqlExpression?TranslateArrayLength(SqlExpression expression) { if (expression is ColumnExpression columnExpression && columnExpression.TypeMapping is NpgsqlJsonTypeMapping mapping) { return(_sqlExpressionFactory.Function( mapping.IsJsonb ? "jsonb_array_length" : "json_array_length", new[] { expression }, nullable: true, argumentsPropagateNullability: TrueArrays[2], typeof(int))); } if (expression is PostgresJsonTraversalExpression traversal) { // The traversal expression has ReturnsText=true (e.g. ->> not ->), so we recreate it to return // the JSON object instead. var lastPathComponent = traversal.Path.Last(); var newTraversal = new PostgresJsonTraversalExpression( traversal.Expression, traversal.Path, returnsText: false, lastPathComponent.Type, _typeMappingSource.FindMapping(lastPathComponent.Type)); var jsonMapping = (NpgsqlJsonTypeMapping)traversal.Expression.TypeMapping !; return(_sqlExpressionFactory.Function( jsonMapping.IsJsonb ? "jsonb_array_length" : "json_array_length", new[] { newTraversal }, nullable: true, argumentsPropagateNullability: TrueArrays[2], typeof(int))); } return(null); }
public virtual SqlExpression?Translate( SqlExpression?instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (method.DeclaringType != typeof(JsonElement) || instance?.TypeMapping is not NpgsqlJsonTypeMapping mapping) { return(null); } // The root of the JSON expression is a ColumnExpression. We wrap that with an empty traversal // expression (col #>> '{}'); subsequent traversals will gradually append the path into that. // Note that it's possible to call methods such as GetString() directly on the root, and the // empty traversal is necessary to properly convert it to a text. instance = instance is ColumnExpression columnExpression ? _sqlExpressionFactory.JsonTraversal( columnExpression, returnsText : false, typeof(string), mapping) : instance; if (method == GetProperty || method == ArrayIndexer) { return(instance is PostgresJsonTraversalExpression prevPathTraversal ? prevPathTraversal.Append(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0])) : null); } if (GetMethods.Contains(method.Name) && arguments.Count == 0 && instance is PostgresJsonTraversalExpression traversal) { var traversalToText = new PostgresJsonTraversalExpression( traversal.Expression, traversal.Path, returnsText: true, typeof(string), _stringTypeMapping); return(method.Name == nameof(JsonElement.GetString) ? traversalToText : ConvertFromText(traversalToText, method.ReturnType)); } if (method == GetArrayLength) { return(_sqlExpressionFactory.Function( mapping.IsJsonb ? "jsonb_array_length" : "json_array_length", new[] { instance }, nullable: true, argumentsPropagateNullability: TrueArrays[1], typeof(int))); } if (method.Name.StartsWith("TryGet", StringComparison.Ordinal) && arguments.Count == 0) { throw new InvalidOperationException($"The TryGet* methods on {nameof(JsonElement)} aren't translated yet, use Get* instead.'"); } return(null); }
public SqlExpression?Translate(SqlExpression instance, MethodInfo method, IReadOnlyList <SqlExpression> arguments, IDiagnosticsLogger <DbLoggerCategory.Query> logger) { if (typeof(JToken).IsAssignableFrom(method.DeclaringType) && method.Name == "get_Item" && arguments.Count == 1) { return((instance is ColumnExpression columnExpression ? _sqlExpressionFactory.JsonTraversal( columnExpression, returnsText: false, typeof(string), instance.TypeMapping) : instance) is PostgresJsonTraversalExpression prevPathTraversal ? prevPathTraversal.Append(_sqlExpressionFactory.ApplyDefaultTypeMapping(arguments[0])) : null); } if (arguments.FirstOrDefault() is PostgresJsonTraversalExpression traversal) { // Support for .Value<T>() and .Value<U, T>(): if (instance == null && method.Name == nameof(Extensions.Value) && method.DeclaringType == typeof(Extensions) && method.IsGenericMethod && method.GetParameters().Length == 1 && arguments.Count == 1) { var traversalToText = new PostgresJsonTraversalExpression( traversal.Expression, traversal.Path, returnsText: true, typeof(string), _stringTypeMapping); if (method.ReturnType == typeof(string)) { return(traversalToText); } else { return(_sqlExpressionFactory.Convert(traversalToText, method.ReturnType, _typeMappingSource.FindMapping(method.ReturnType))); } } // Support for Count() if (instance == null && method.Name == nameof(Enumerable.Count) && method.DeclaringType == typeof(Enumerable) && method.IsGenericMethod && method.GetParameters().Length == 1 && arguments.Count == 1) { return(_jsonPocoTranslator.TranslateArrayLength(traversal)); } // Predicate-less Any - translate to a simple length check. if (method.IsClosedFormOf(_enumerableAnyWithoutPredicate) && arguments.Count == 1 && arguments[0].Type.TryGetElementType(out _) && arguments[0].TypeMapping is NpgsqlJsonTypeMapping) { return(_sqlExpressionFactory.GreaterThan( _jsonPocoTranslator.TranslateArrayLength(arguments[0]), _sqlExpressionFactory.Constant(0))); } } return(null); }