Skip to content

Commit 12d67ba

Browse files
authored
Merge pull request #39 from mk3008/38-enhanced-json-conversion-processing
ToJsonQuery improvements, including breaking changes
2 parents 4efbff7 + 5625f0a commit 12d67ba

File tree

10 files changed

+1319
-262
lines changed

10 files changed

+1319
-262
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
-- 親の親: Organization
2+
CREATE TABLE organizations (
3+
organization_id SERIAL PRIMARY KEY,
4+
name TEXT NOT NULL
5+
);
6+
7+
-- 親: Blog(各Postが所属する)
8+
CREATE TABLE blogs (
9+
blog_id SERIAL PRIMARY KEY,
10+
name TEXT NOT NULL,
11+
organization_id INT REFERENCES organizations(organization_id) ON DELETE CASCADE
12+
);
13+
14+
-- 親: User(投稿者)
15+
CREATE TABLE users (
16+
user_id SERIAL PRIMARY KEY,
17+
name TEXT NOT NULL
18+
);
19+
20+
-- 投稿: Post(起点)
21+
CREATE TABLE posts (
22+
post_id SERIAL PRIMARY KEY,
23+
title TEXT NOT NULL,
24+
content TEXT NOT NULL,
25+
user_id INT REFERENCES users(user_id) ON DELETE CASCADE,
26+
blog_id INT REFERENCES blogs(blog_id) ON DELETE CASCADE,
27+
created_at TIMESTAMP DEFAULT NOW()
28+
);
29+
30+
-- 子: Tags(多対多)
31+
CREATE TABLE tags (
32+
tag_id SERIAL PRIMARY KEY,
33+
name TEXT NOT NULL UNIQUE
34+
);
35+
36+
CREATE TABLE post_tags (
37+
post_id INT REFERENCES posts(post_id) ON DELETE CASCADE,
38+
tag_id INT REFERENCES tags(tag_id) ON DELETE CASCADE,
39+
PRIMARY KEY (post_id, tag_id)
40+
);
41+
42+
-- 子: Comments(投稿に紐づく)
43+
CREATE TABLE comments (
44+
comment_id SERIAL PRIMARY KEY,
45+
post_id INT REFERENCES posts(post_id) ON DELETE CASCADE,
46+
content TEXT NOT NULL,
47+
created_at TIMESTAMP DEFAULT NOW()
48+
);
49+
50+
-- 子の子: Likes(コメントに紐づく)
51+
CREATE TABLE likes (
52+
like_id SERIAL PRIMARY KEY,
53+
comment_id INT REFERENCES comments(comment_id) ON DELETE CASCADE,
54+
user_id INT REFERENCES users(user_id) ON DELETE CASCADE
55+
);
56+
57+
-- 組織データ
58+
INSERT INTO organizations (name) VALUES ('Tech Corp'), ('Creative Hub');
59+
60+
-- ブログデータ
61+
INSERT INTO blogs (name, organization_id) VALUES
62+
('AI Insights', 1),
63+
('Design Trends', 2);
64+
65+
-- ユーザーデータ
66+
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
67+
68+
-- 投稿データ
69+
INSERT INTO posts (title, content, user_id, blog_id) VALUES
70+
('Understanding AI', 'This is a post about AI.', 1, 1),
71+
('Deep Learning Advances', 'Exploring the latest in deep learning.', 1, 1),
72+
('Latest Design Trends', 'Exploring modern design.', 2, 2),
73+
('User Experience Matters', 'How UX shapes our interactions.', 2, 2);
74+
75+
-- タグデータ
76+
INSERT INTO tags (name) VALUES ('AI'), ('Machine Learning'), ('Design'), ('UX');
77+
78+
-- 投稿とタグの関連データ
79+
INSERT INTO post_tags (post_id, tag_id) VALUES
80+
(1, 1), (1, 2),
81+
(2, 3), (2, 4);
82+
83+
-- コメントデータ
84+
INSERT INTO comments (post_id, content) VALUES
85+
(1, 'Great post!'),
86+
(1, 'Very insightful!'),
87+
(2, 'I love these design ideas.');
88+
89+
-- いいねデータ
90+
INSERT INTO likes (comment_id, user_id) VALUES
91+
(1, 2),
92+
(2, 1);

demo/Sample/PostgresSpecificSample.cs

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,125 @@ public class PostgresSpecificSample(ITestOutputHelper output)
1111
{
1212
private readonly ITestOutputHelper output = output;
1313

14+
15+
private string QueryText = """
16+
select
17+
posts.post_id
18+
, posts.title
19+
, posts.content
20+
, posts.created_at
21+
, users.user_id
22+
, users.name as user_name
23+
, blogs.blog_id
24+
, blogs.name as blog_name
25+
, organizations.organization_id
26+
, organizations.name as organization_name
27+
from
28+
posts
29+
inner join users on posts.user_id = users.user_id
30+
inner join blogs on posts.blog_id = blogs.blog_id
31+
inner join organizations on blogs.organization_id = organizations.organization_id
32+
""";
33+
1434
/// <summary>
1535
/// Sample to generate a query that returns JSON
1636
/// </summary>
1737
[Fact]
1838
public void ToJsonQuery()
1939
{
20-
var query = QueryAstParser.Parse("""
21-
select
22-
posts.post_id
23-
, posts.title
24-
, posts.content
25-
, posts.created_at
26-
, users.user_id
27-
, users.name as user_name
28-
, blogs.blog_id
29-
, blogs.name as blog_name
30-
, organizations.organization_id
31-
, organizations.name as organization_name
32-
from
33-
posts
34-
inner join users on posts.user_id = users.user_id
35-
inner join blogs on posts.blog_id = blogs.blog_id
36-
inner join organizations on blogs.organization_id = organizations.organization_id
37-
where
38-
posts.post_id = :post_id
39-
""");
40-
41-
query.NormalizeSelectClause()
42-
.Serialize("organizations", objectName: "organization")
43-
.Serialize("users", objectName: "user")
44-
.Serialize("blogs", objectName: "blog", include: ["organization"])
45-
.Serialize("posts", objectName: "post", include: ["user", "blog"])
46-
.ToJson();
40+
var query = QueryAstParser.Parse(QueryText);
41+
42+
query.Where("post_id", action: x => x.Equal(":post_id"))
43+
.NormalizeSelectClause()
44+
.ToPostgresJsonQuery(x =>
45+
{
46+
return x.Serialize(datasource: "posts", jsonKey: "post", parent: static x =>
47+
{
48+
return x.Serialize(datasource: "users", jsonKey: "user")
49+
.Serialize(datasource: "blogs", jsonKey: "blog", parent: static x =>
50+
{
51+
return x.Serialize(datasource: "organizations", jsonKey: "organization");
52+
});
53+
});
54+
});
4755

4856
var actual = query.ToSql();
4957

5058
output.WriteLine(actual);
5159

52-
var expected = "select row_to_json(d) from (select json_build_object('post_id', posts.post_id, 'title', posts.title, 'content', posts.content, 'created_at', posts.created_at, 'user', json_build_object('user_id', users.user_id, 'user_name', users.name), 'blog', json_build_object('blog_id', blogs.blog_id, 'blog_name', blogs.name, 'organization', json_build_object('organization_id', organizations.organization_id, 'organization_name', organizations.name))) as post from posts inner join users on posts.user_id = users.user_id inner join blogs on posts.blog_id = blogs.blog_id inner join organizations on blogs.organization_id = organizations.organization_id where posts.post_id = :post_id) as d limit 1";
60+
var expected = "with __json as (select posts.post_id as posts__post_id, posts.title as posts__title, posts.content as posts__content, posts.created_at as posts__created_at, users.user_id as users__user_id, users.name as users__user_name, blogs.blog_id as blogs__blog_id, blogs.name as blogs__blog_name, organizations.organization_id as organizations__organization_id, organizations.name as organizations__organization_name from posts inner join users on posts.user_id = users.user_id inner join blogs on posts.blog_id = blogs.blog_id inner join organizations on blogs.organization_id = organizations.organization_id where posts.post_id = :post_id) select row_to_json(d) from (select json_build_object('post_id', __json.posts__post_id, 'title', __json.posts__title, 'content', __json.posts__content, 'created_at', __json.posts__created_at, 'user', json_build_object('user_id', __json.users__user_id, 'user_name', __json.users__user_name), 'blog', json_build_object('blog_id', __json.blogs__blog_id, 'blog_name', __json.blogs__blog_name, 'organization', json_build_object('organization_id', __json.organizations__organization_id, 'organization_name', __json.organizations__organization_name))) as \"post\" from __json) as d limit 1";
5361
Assert.Equal(expected, actual);
62+
63+
/* JSON Sample
64+
{
65+
"post": {
66+
"post_id": 9,
67+
"title": "Understanding AI",
68+
"content": "This is a post about AI.",
69+
"created_at": "2025-02-18T20:25:21.974106",
70+
"user": {
71+
"user_id": 1,
72+
"user_name": "Alice"
73+
},
74+
"blog": {
75+
"blog_id": 1,
76+
"blog_name": "AI Insights",
77+
"organization": {
78+
"organization_id": 1,
79+
"organization_name": "Tech Corp"
80+
}
81+
}
82+
}
83+
}
84+
*/
5485
}
86+
87+
[Fact]
88+
public void ToJsonQueryFlat()
89+
{
90+
var query = QueryAstParser.Parse(QueryText);
91+
92+
query.Where("post_id", action: x => x.Equal(":post_id"))
93+
.NormalizeSelectClause()
94+
.ToPostgresJsonQuery(x =>
95+
{
96+
return x.Serialize("posts", isFlat: true, parent: static x =>
97+
{
98+
return x.Serialize("users", jsonKey: "user")
99+
.Serialize("blogs", jsonKey: "blog", parent: static x =>
100+
{
101+
return x.Serialize("organizations", jsonKey: "organization");
102+
});
103+
});
104+
});
105+
106+
var actual = query.ToSql();
107+
108+
output.WriteLine(actual);
109+
110+
var expected = "with __json as (select posts.post_id as posts__post_id, posts.title as posts__title, posts.content as posts__content, posts.created_at as posts__created_at, users.user_id as users__user_id, users.name as users__user_name, blogs.blog_id as blogs__blog_id, blogs.name as blogs__blog_name, organizations.organization_id as organizations__organization_id, organizations.name as organizations__organization_name from posts inner join users on posts.user_id = users.user_id inner join blogs on posts.blog_id = blogs.blog_id inner join organizations on blogs.organization_id = organizations.organization_id where posts.post_id = :post_id) select row_to_json(d) from (select __json.posts__post_id as \"post_id\", __json.posts__title as \"title\", __json.posts__content as \"content\", __json.posts__created_at as \"created_at\", json_build_object('user_id', __json.users__user_id, 'user_name', __json.users__user_name) as \"user\", json_build_object('blog_id', __json.blogs__blog_id, 'blog_name', __json.blogs__blog_name, 'organization', json_build_object('organization_id', __json.organizations__organization_id, 'organization_name', __json.organizations__organization_name)) as \"blog\" from __json) as d limit 1";
111+
Assert.Equal(expected, actual);
112+
113+
/* JSON Sample
114+
{
115+
"post_id": 9,
116+
"title": "Understanding AI",
117+
"content": "This is a post about AI.",
118+
"created_at": "2025-02-18T20:25:21.974106",
119+
"user": {
120+
"user_id": 1,
121+
"user_name": "Alice"
122+
},
123+
"blog": {
124+
"blog_id": 1,
125+
"blog_name": "AI Insights",
126+
"organization": {
127+
"organization_id": 1,
128+
"organization_name": "Tech Corp"
129+
}
130+
}
131+
}
132+
*/
133+
}
134+
55135
}

src/Carbunqlex/Carbunqlex.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
<RepositoryUrl>https://github.com/mk3008/CarbunqleX</RepositoryUrl>
99
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1010
<FileVersion></FileVersion>
11-
<Version>0.0.2.1</Version>
11+
<Version>0.0.3</Version>
1212
<Authors>mk3008net</Authors>
1313
<Description>CarbunqleX is a lightweight library that enhances the reusability and maintainability of RawSQL through advanced AST analysis.</Description>
1414
<Copyright>mk3008net</Copyright>
1515
<PackageProjectUrl>https://github.com/mk3008/CarbunqleX</PackageProjectUrl>
1616
<PackageTags>postgres;sql;postgresql;ast;sql-parser;raw-sql;dynamic-query;postgres-sql-parser</PackageTags>
17+
<SignAssembly>False</SignAssembly>
1718
</PropertyGroup>
1819

1920
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

0 commit comments

Comments
 (0)