-
Hi, I am new to drift. I have a few questions around the json extension. Table definitionclass Company extends Table {
@override
Set<Column> get primaryKey => {id};
TextColumn get id => text()();
TextColumn get name => text()();
}
class Employee extends Table {
@override
Set<Column> get primaryKey => {id};
TextColumn get id => text()();
TextColumn get companyId => text().references(Company, #id)();
TextColumn get name => text()();
}
class Project extends Table {
@override
Set<Column> get primaryKey => {id};
TextColumn get id => text()();
TextColumn get name => text()();
}
class ProjectMember extends Table {
@override
Set<Column> get primaryKey => {projectId, employeeId};
TextColumn get projectId => text().references(Project, #id)();
TextColumn get employeeId => text().references(Employee, #id)();
TextColumn get role => textEnum<ProjectMemberRole>()();
} What result I want[
{
"id": "1",
"name": "project-x",
"projectMembers": [
{
"employeee": {
"id": "3",
"companyId": "70",
"name": "Alex"
},
"role": "manager"
},
{
"employeee": {
"id": "4",
"companyId": "71",
"name": "Rob"
},
"role": "engineer"
},
]
}
] I think the following SQL will produce the above results. SELECT
"project"."id" AS "project.id",
"project"."name" AS "project.name",
json_group_array (
json_object (
"role", "project_member"."role",
"employee", (
SELECT
json_object (
"id", "employee"."id",
"companyId", "employee"."company_id",
"name", "employee"."name"
)
FROM
"project_member"
INNER JOIN "employee" ON "employee"."id" = "project_member"."employee_id"
)
)
ORDER BY
json_extract (employee, '$.name') ASC
) AS "project.members"
FROM
"project"
INNER JOIN "project_member" ON "project_member"."project_id" = "project"."id"
WHERE
"project"."id" = ?; So, I defined the Expression<T> jsonObject<T extends Object>(
Map<Expression<String>, Expression> values,
) {
return FunctionCallExpression<T>('json_object', _jsonObjectArgs(values));
}
List<Expression> _jsonObjectArgs(
Map<Expression<String>, Expression> values,
) {
final expressions = <Expression>[];
for (final MapEntry(:key, :value) in values.entries) {
expressions.add(key);
expressions.add(value);
}
return expressions;
}
...
final employeeJsonObject = jsonObject(
employee.columnsByName.map(
(key, value) => MapEntry(
Constant(key),
value,
),
),
);
final projectMemberSubquery = subqueryExpression(
projectMember.selectOnly().join([
innerJoin(
employee,
employee.id.equalsExp(
projectMember.employeeId,
),
),
])
..addColumns([employeeJsonObject]),
);
final projectMembersJsonGroupArray = jsonGroupArray(
jsonObject({
const Constant('role'): projectMember.role,
const Constant('member'): projectMemberSubquery,
}),
orderBy: OrderBy([
OrderingTerm(
expression: "**how can I write expression here?**",
mode: OrderingMode.asc,
),
]),
);
final statement = select(project)
.addColumns([projectMembersJsonGroupArray]).join([
innerJoin(
projectMember,
projectMember.projectId.equalsExp(
project.id,
),
),
]);
print(statement.constructQuery().sql); Questions
My goal is not to use the json function, but to get the result I want (preferably in a concise way), so if you have any other good ideas, I'd love to know! (However, I would like to avoid (in a sense) denormalizing the data structure, e.g., having the |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
In the latest drift version,
I think traditionally the way you'd write this is to first select the projects you're interested in. Then you collect their ids and run a second query filtering members and employees involved in that particular project. We have an example for that here. Using a single join to fetch everything and then filter out duplicates in the project columns would also work. |
Beta Was this translation helpful? Give feedback.
In the latest drift version,
jsonGroupArray
is part of the package and has an orderBy argument.I think traditionally the way you'd write this is to first select the projects you're interested in. Then you collect their ids and run a second query filtering members and employees involved in that particular project. We have an example for that here. Using a single join to fetch everything and then filter out duplicates in the project columns would also work.