Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/YesSql.Core/Sql/JsonPathExpressionFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;

namespace YesSql.Sql
{
public class JsonPathExpressionFunction : ISqlFunction
{
private readonly string _template;
private readonly int[] _argumentIds;

public JsonPathExpressionFunction(string template, params int[] argumentIds)
{
_template = template;
_argumentIds = argumentIds;
}

public string Render(string[] arguments)
{
foreach (var id in _argumentIds)
{
arguments[id] = TransformPathExpression(arguments[id]);
}
return String.Format(_template, arguments);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return String.Format(_template, arguments);
return String.Format(_template, arguments);

}

private string TransformPathExpression(string jsonPathExpression)
{
return jsonPathExpression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might need to think about better way here

.Replace("$.", "")
.Replace("[", ",")
.Replace("].", ",")
.Replace("]", "")
.Replace(".", ",");
}
}
}
2 changes: 2 additions & 0 deletions src/YesSql.Provider.MySql/MySqlDialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public MySqlDialect()
{
AddTypeHandler<TimeSpan, long>(x => x.Ticks);
Methods.Add("now", new TemplateFunction("UTC_TIMESTAMP()"));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Methods.Add("JSON_MODIFY", new TemplateFunction("json_set({0}, {1}, {2})"));
}

public override string Name => "MySql";
Expand Down
3 changes: 3 additions & 0 deletions src/YesSql.Provider.PostgreSql/PostgreSqlDialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ public PostgreSqlDialect()
Methods.Add("month", new TemplateFunction("extract(month from {0})"));
Methods.Add("year", new TemplateFunction("extract(year from {0})"));
Methods.Add("now", new TemplateFunction("now() at time zone 'utc'"));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Methods.Add("JSON_VALUE", new JsonPathExpressionFunction("{0}::json#>>string_to_array({1}, ',')", 1));
Methods.Add("JSON_MODIFY", new JsonPathExpressionFunction("jsonb_set({0}, string_to_array({1}, ','), to_jsonb({2}), false)", 1));
}

public override string Name => "PostgreSql";
Expand Down
3 changes: 3 additions & 0 deletions src/YesSql.Provider.Sqlite/SqliteDialect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public SqliteDialect()
Methods.Add("month", new TemplateFunction("cast(strftime('%m', {0}) as int)"));
Methods.Add("year", new TemplateFunction("cast(strftime('%Y', {0}) as int)"));
Methods.Add("now", new TemplateFunction("DATETIME('now')"));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Methods.Add("JSON_VALUE", new TemplateFunction("json_extract({0}, {1})"));
Methods.Add("JSON_MODIFY", new TemplateFunction("json_set({0}, {1}, {2})"));
}

public override string Name => "Sqlite";
Expand Down
39 changes: 39 additions & 0 deletions test/YesSql.Tests/CoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4372,6 +4372,45 @@ public async Task SqlNowFunction()
Assert.Equal(10, publishedInThePastResult);
}

[Theory]
[InlineData("JSON_VALUE", "'{ \"a\" : 1, \"b\" : 2 }'", "'$.b'", "2")]
[InlineData("JSON_VALUE", "'{ \"a\" : { \"b\" : { \"c\" : 3 } } }'", "'$.a.b.c'", "3")]
[InlineData("JSON_VALUE", "'{ \"a\" : { \"b\" : [{ \"c\" : 1 }, { \"c\" : 2 }, { \"c\" : 3 }] } }'", "'$.a.b[2].c'", "3")]
public async Task SqlJsonValueFunction(string method, string json, string jsonPathExpression, string expected)
{
string result;

using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();
var dialect = _store.Configuration.SqlDialect;
var sql = "SELECT " + dialect.RenderMethod(method, json, jsonPathExpression);
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}

Assert.Equal(expected, result);
Comment on lines +4381 to +4391
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string result;
using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();
var dialect = _store.Configuration.SqlDialect;
var sql = "SELECT " + dialect.RenderMethod(method, json, jsonPathExpression);
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}
Assert.Equal(expected, result);
// Arrange & Act
string result;
using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();
var dialect = _store.Configuration.SqlDialect;
var sql = $"SELECT {dialect.RenderMethod(method, json, jsonPathExpression)}" ;
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}
// Assert
Assert.Equal(expected, result);

}

[Theory]
[InlineData("JSON_MODIFY", "'{\"a\":1,\"b\":2}'", "'$.b'", "3", "{\"a\":1,\"b\":3}")]
[InlineData("JSON_MODIFY", "'{ \"a\" : { \"b\" : { \"c\" : 3 } } }'", "'$.a.b.c'", "4", "{\"a\":{\"b\":{\"c\":4}}}")]
[InlineData("JSON_MODIFY", "'{ \"a\" : { \"b\" : [{ \"c\" : 1 }, { \"c\" : 2 }, { \"c\" : 3 }] } }'", "'$.a.b[2].c'", "5", "{\"a\":{\"b\":[{\"c\":1},{\"c\":2},{\"c\":5}]}}")]
public async Task SqlJsonModifyFunction(string method, string json, string jsonPathExpression, string newValue, string expected)
{
string result;

using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();

var dialect = _store.Configuration.SqlDialect;
var sql = "SELECT " + dialect.RenderMethod(method, json, jsonPathExpression, newValue);
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}
//we are getting rid of unnecessary JSON formatting
Assert.Equal(expected, result.Replace(": ", ":").Replace(" :", ":").Replace(", ", ",").Replace("{ ", "{").Replace(" }", "}"));
Comment on lines +4400 to +4411
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
string result;
using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();
var dialect = _store.Configuration.SqlDialect;
var sql = "SELECT " + dialect.RenderMethod(method, json, jsonPathExpression, newValue);
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}
//we are getting rid of unnecessary JSON formatting
Assert.Equal(expected, result.Replace(": ", ":").Replace(" :", ":").Replace(", ", ",").Replace("{ ", "{").Replace(" }", "}"));
// Arrange & Act
string result;
using (var connection = _store.Configuration.ConnectionFactory.CreateConnection())
{
await connection.OpenAsync();
var dialect = _store.Configuration.SqlDialect;
var sql = $"SELECT {dialect.RenderMethod(method, json, jsonPathExpression, newValue)}";
result = await connection.QueryFirstOrDefaultAsync<string>(sql);
}
// Assert
Assert.Equal(expected, result.Replace(": ", ":").Replace(" :", ":").Replace(", ", ",").Replace("{ ", "{").Replace(" }", "}")); //we are getting rid of unnecessary JSON formatting

}

[Fact]
public virtual async Task CanUseStaticMethodsInLinqQueries()
{
Expand Down