Add Where.raw(String where Sql)' to use join table fields as query conditions

This commit is contained in:
debuggerx01 2022-01-06 12:06:56 +08:00
parent 3bdee0bb7d
commit a21b5a35e3
6 changed files with 312 additions and 229 deletions

View file

@ -1,67 +0,0 @@
// **************************************************************************
// JsonModelGenerator
// **************************************************************************
@generatedSerializable
class Employee extends _Employee {
Employee(
{this.id,
this.firstName,
this.lastName,
this.salary,
this.createdAt,
this.updatedAt});
@override
final String id;
@override
final String firstName;
@override
final String lastName;
@override
final double salary;
@override
final DateTime createdAt;
@override
final DateTime updatedAt;
Employee copyWith(
{String id,
String firstName,
String lastName,
double salary,
DateTime createdAt,
DateTime updatedAt}) {
return new Employee(
id: id ?? this.id,
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
salary: salary ?? this.salary,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt);
}
bool operator ==(other) {
return other is _Employee &&
other.id == id &&
other.firstName == firstName &&
other.lastName == lastName &&
other.salary == salary &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt;
}
@override
int get hashCode {
return hashObjects([id, firstName, lastName, salary, createdAt, updatedAt]);
}
Map<String, dynamic> toJson() {
return EmployeeSerializer.toMap(this);
}
}

View file

@ -1,18 +1,19 @@
import 'dart:async'; import 'dart:async';
import 'package:angel3_migration/angel3_migration.dart';
import 'package:angel3_orm/angel3_orm.dart'; import 'package:angel3_orm/angel3_orm.dart';
import 'package:angel3_serialize/angel3_serialize.dart'; import 'package:angel3_serialize/angel3_serialize.dart';
import 'package:optional/optional.dart'; import 'package:optional/optional.dart';
part 'main.g.dart'; part 'main.g.dart';
part 'main.serializer.g.dart';
void main() async { void main() async {
var query = EmployeeQuery() var query = EmployeeQuery()
..where?.firstName.equals('Rich') ..where?.firstName.equals('Rich')
..where?.lastName.equals('Person') ..where?.lastName.equals('Person')
..where?.raw('COM.deleted = false')
..orWhere((w) => w.salary.greaterThanOrEqualTo(75000)) ..orWhere((w) => w.salary.greaterThanOrEqualTo(75000))
..join('companies', 'company_id', 'id'); ..join('companies', 'company_id', 'id', alias: 'COM');
var richPerson = await query.getOne(_FakeExecutor()); var richPerson = await query.getOne(_FakeExecutor());
if (richPerson.isPresent) { if (richPerson.isPresent) {
@ -31,7 +32,7 @@ class _FakeExecutor extends QueryExecutor {
print( print(
'_FakeExecutor received query: $query and values: $substitutionValues'); '_FakeExecutor received query: $query and values: $substitutionValues');
return [ return [
[1, 'Rich', 'Person', 100000.0, now, now] [1, now, now, 'Rich', 'Person', 100000.0]
]; ];
} }
@ -50,65 +51,3 @@ abstract class _Employee extends Model {
double? get salary; double? get salary;
} }
class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
@override
final QueryValues values = MapQueryValues();
EmployeeQueryWhere? _where;
EmployeeQuery() {
_where = EmployeeQueryWhere(this);
}
@override
EmployeeQueryWhere? get where => _where;
@override
String get tableName => 'employees';
@override
List<String> get fields =>
['id', 'first_name', 'last_name', 'salary', 'created_at', 'updated_at'];
@override
EmployeeQueryWhere newWhereClause() => EmployeeQueryWhere(this);
@override
Optional<Employee> deserialize(List row) {
return Optional.ofNullable(Employee(
id: row[0].toString(),
firstName: row[1] as String,
lastName: row[2] as String,
salary: row[3] as double,
createdAt: row[4] as DateTime,
updatedAt: row[5] as DateTime));
}
}
class EmployeeQueryWhere extends QueryWhere {
EmployeeQueryWhere(EmployeeQuery query)
: id = NumericSqlExpressionBuilder(query, 'id'),
firstName = StringSqlExpressionBuilder(query, 'first_name'),
lastName = StringSqlExpressionBuilder(query, 'last_name'),
salary = NumericSqlExpressionBuilder(query, 'salary'),
createdAt = DateTimeSqlExpressionBuilder(query, 'created_at'),
updatedAt = DateTimeSqlExpressionBuilder(query, 'updated_at');
@override
Iterable<SqlExpressionBuilder> get expressionBuilders {
return [id, firstName, lastName, salary, createdAt, updatedAt];
}
final NumericSqlExpressionBuilder<int> id;
final StringSqlExpressionBuilder firstName;
final StringSqlExpressionBuilder lastName;
final NumericSqlExpressionBuilder<double> salary;
final DateTimeSqlExpressionBuilder createdAt;
final DateTimeSqlExpressionBuilder updatedAt;
}

View file

@ -2,6 +2,171 @@
part of 'main.dart'; part of 'main.dart';
// **************************************************************************
// MigrationGenerator
// **************************************************************************
class EmployeeMigration extends Migration {
@override
void up(Schema schema) {
schema.create('employees', (table) {
table.serial('id').primaryKey();
table.timeStamp('created_at');
table.timeStamp('updated_at');
table.varChar('first_name', length: 255);
table.varChar('last_name', length: 255);
table.declareColumn(
'salary', Column(type: ColumnType('decimal'), length: 255));
});
}
@override
void down(Schema schema) {
schema.drop('employees');
}
}
// **************************************************************************
// OrmGenerator
// **************************************************************************
class EmployeeQuery extends Query<Employee, EmployeeQueryWhere> {
EmployeeQuery({Query? parent, Set<String>? trampoline})
: super(parent: parent) {
trampoline ??= <String>{};
trampoline.add(tableName);
_where = EmployeeQueryWhere(this);
}
@override
final EmployeeQueryValues values = EmployeeQueryValues();
EmployeeQueryWhere? _where;
@override
Map<String, String> get casts {
return {'salary': 'text'};
}
@override
String get tableName {
return 'employees';
}
@override
List<String> get fields {
return const [
'id',
'created_at',
'updated_at',
'first_name',
'last_name',
'salary'
];
}
@override
EmployeeQueryWhere? get where {
return _where;
}
@override
EmployeeQueryWhere newWhereClause() {
return EmployeeQueryWhere(this);
}
static Optional<Employee> parseRow(List row) {
if (row.every((x) => x == null)) {
return Optional.empty();
}
var model = Employee(
id: row[0].toString(),
createdAt: (row[1] as DateTime?),
updatedAt: (row[2] as DateTime?),
firstName: (row[3] as String?),
lastName: (row[4] as String?),
salary: double.tryParse(row[5].toString()));
return Optional.of(model);
}
@override
Optional<Employee> deserialize(List row) {
return parseRow(row);
}
}
class EmployeeQueryWhere extends QueryWhere {
EmployeeQueryWhere(EmployeeQuery query)
: id = NumericSqlExpressionBuilder<int>(query, 'id'),
createdAt = DateTimeSqlExpressionBuilder(query, 'created_at'),
updatedAt = DateTimeSqlExpressionBuilder(query, 'updated_at'),
firstName = StringSqlExpressionBuilder(query, 'first_name'),
lastName = StringSqlExpressionBuilder(query, 'last_name'),
salary = NumericSqlExpressionBuilder<double>(query, 'salary');
final NumericSqlExpressionBuilder<int> id;
final DateTimeSqlExpressionBuilder createdAt;
final DateTimeSqlExpressionBuilder updatedAt;
final StringSqlExpressionBuilder firstName;
final StringSqlExpressionBuilder lastName;
final NumericSqlExpressionBuilder<double> salary;
@override
List<SqlExpressionBuilder> get expressionBuilders {
return [id, createdAt, updatedAt, firstName, lastName, salary];
}
}
class EmployeeQueryValues extends MapQueryValues {
@override
Map<String, String> get casts {
return {'salary': 'decimal'};
}
String? get id {
return (values['id'] as String?);
}
set id(String? value) => values['id'] = value;
DateTime? get createdAt {
return (values['created_at'] as DateTime?);
}
set createdAt(DateTime? value) => values['created_at'] = value;
DateTime? get updatedAt {
return (values['updated_at'] as DateTime?);
}
set updatedAt(DateTime? value) => values['updated_at'] = value;
String? get firstName {
return (values['first_name'] as String?);
}
set firstName(String? value) => values['first_name'] = value;
String? get lastName {
return (values['last_name'] as String?);
}
set lastName(String? value) => values['last_name'] = value;
double? get salary {
return double.tryParse((values['salary'] as String));
}
set salary(double? value) => values['salary'] = value.toString();
void copyFrom(Employee model) {
createdAt = model.createdAt;
updatedAt = model.updatedAt;
firstName = model.firstName;
lastName = model.lastName;
salary = model.salary;
}
}
// ************************************************************************** // **************************************************************************
// JsonModelGenerator // JsonModelGenerator
// ************************************************************************** // **************************************************************************
@ -10,63 +175,154 @@ part of 'main.dart';
class Employee extends _Employee { class Employee extends _Employee {
Employee( Employee(
{this.id, {this.id,
this.createdAt,
this.updatedAt,
this.firstName, this.firstName,
this.lastName, this.lastName,
this.salary, this.salary});
this.createdAt,
this.updatedAt}); /// A unique identifier corresponding to this item.
@override
String? id;
/// The time at which this item was created.
@override
DateTime? createdAt;
/// The last time at which this item was updated.
@override
DateTime? updatedAt;
@override @override
final String? id; String? firstName;
@override @override
final String? firstName; String? lastName;
@override @override
final String? lastName; double? salary;
@override
final double? salary;
@override
final DateTime? createdAt;
@override
final DateTime? updatedAt;
Employee copyWith( Employee copyWith(
{String? id, {String? id,
DateTime? createdAt,
DateTime? updatedAt,
String? firstName, String? firstName,
String? lastName, String? lastName,
double? salary, double? salary}) {
DateTime? createdAt,
DateTime? updatedAt}) {
return Employee( return Employee(
id: id ?? this.id, id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
firstName: firstName ?? this.firstName, firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName, lastName: lastName ?? this.lastName,
salary: salary ?? this.salary, salary: salary ?? this.salary);
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt);
} }
@override @override
bool operator ==(other) { bool operator ==(other) {
return other is _Employee && return other is _Employee &&
other.id == id && other.id == id &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt &&
other.firstName == firstName && other.firstName == firstName &&
other.lastName == lastName && other.lastName == lastName &&
other.salary == salary && other.salary == salary;
other.createdAt == createdAt &&
other.updatedAt == updatedAt;
} }
@override @override
int get hashCode { int get hashCode {
return hashObjects([id, firstName, lastName, salary, createdAt, updatedAt]); return hashObjects([id, createdAt, updatedAt, firstName, lastName, salary]);
}
@override
String toString() {
return 'Employee(id=$id, createdAt=$createdAt, updatedAt=$updatedAt, firstName=$firstName, lastName=$lastName, salary=$salary)';
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return EmployeeSerializer.toMap(this); return EmployeeSerializer.toMap(this);
} }
} }
// **************************************************************************
// SerializerGenerator
// **************************************************************************
const EmployeeSerializer employeeSerializer = EmployeeSerializer();
class EmployeeEncoder extends Converter<Employee, Map> {
const EmployeeEncoder();
@override
Map convert(Employee model) => EmployeeSerializer.toMap(model);
}
class EmployeeDecoder extends Converter<Map, Employee> {
const EmployeeDecoder();
@override
Employee convert(Map map) => EmployeeSerializer.fromMap(map);
}
class EmployeeSerializer extends Codec<Employee, Map> {
const EmployeeSerializer();
@override
EmployeeEncoder get encoder => const EmployeeEncoder();
@override
EmployeeDecoder get decoder => const EmployeeDecoder();
static Employee fromMap(Map map) {
return Employee(
id: map['id'] as String?,
createdAt: map['created_at'] != null
? (map['created_at'] is DateTime
? (map['created_at'] as DateTime)
: DateTime.parse(map['created_at'].toString()))
: null,
updatedAt: map['updated_at'] != null
? (map['updated_at'] is DateTime
? (map['updated_at'] as DateTime)
: DateTime.parse(map['updated_at'].toString()))
: null,
firstName: map['first_name'] as String?,
lastName: map['last_name'] as String?,
salary: map['salary'] as double?);
}
static Map<String, dynamic> toMap(_Employee? model) {
if (model == null) {
return {};
}
return {
'id': model.id,
'created_at': model.createdAt?.toIso8601String(),
'updated_at': model.updatedAt?.toIso8601String(),
'first_name': model.firstName,
'last_name': model.lastName,
'salary': model.salary
};
}
}
abstract class EmployeeFields {
static const List<String> allFields = <String>[
id,
createdAt,
updatedAt,
firstName,
lastName,
salary
];
static const String id = 'id';
static const String createdAt = 'created_at';
static const String updatedAt = 'updated_at';
static const String firstName = 'first_name';
static const String lastName = 'last_name';
static const String salary = 'salary';
}

View file

@ -1,61 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'main.dart';
// **************************************************************************
// SerializerGenerator
// **************************************************************************
abstract class EmployeeSerializer {
static Employee fromMap(Map map) {
return Employee(
id: map['id'] as String?,
firstName: map['first_name'] as String?,
lastName: map['last_name'] as String?,
salary: map['salary'] as double?,
createdAt: map['created_at'] != null
? (map['created_at'] is DateTime
? (map['created_at'] as DateTime?)
: DateTime.parse(map['created_at'].toString()))
: null,
updatedAt: map['updated_at'] != null
? (map['updated_at'] is DateTime
? (map['updated_at'] as DateTime?)
: DateTime.parse(map['updated_at'].toString()))
: null);
}
static Map<String, dynamic> toMap(Employee model) {
return {
'id': model.id,
'first_name': model.firstName,
'last_name': model.lastName,
'salary': model.salary,
'created_at': model.createdAt?.toIso8601String(),
'updated_at': model.updatedAt?.toIso8601String()
};
}
}
abstract class EmployeeFields {
static const List<String> allFields = <String>[
id,
firstName,
lastName,
salary,
createdAt,
updatedAt
];
static const String id = 'id';
static const String firstName = 'first_name';
static const String lastName = 'last_name';
static const String salary = 'salary';
static const String createdAt = 'created_at';
static const String updatedAt = 'updated_at';
}

View file

@ -167,6 +167,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void _makeJoin( void _makeJoin(
tableName, tableName,
Set<String>? trampoline, Set<String>? trampoline,
String? alias,
JoinType type, JoinType type,
String localKey, String localKey,
String foreignKey, String foreignKey,
@ -182,7 +183,7 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
} }
var to = _compileJoin(tableName, trampoline); var to = _compileJoin(tableName, trampoline);
var alias = _joinAlias(trampoline); alias ??= _joinAlias(trampoline);
if (tableName is Query) { if (tableName is Query) {
for (var field in tableName.fields) { for (var field in tableName.fields) {
tableName.aliases[field] = '${alias}_$field'; tableName.aliases[field] = '${alias}_$field';
@ -199,8 +200,9 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void join(tableName, String localKey, String foreignKey, void join(tableName, String localKey, String foreignKey,
{String op = '=', {String op = '=',
List<String> additionalFields = const [], List<String> additionalFields = const [],
Set<String>? trampoline}) { Set<String>? trampoline,
_makeJoin(tableName, trampoline, JoinType.inner, localKey, foreignKey, op, String? alias}) {
_makeJoin(tableName, trampoline, alias, JoinType.inner, localKey, foreignKey, op,
additionalFields); additionalFields);
} }
@ -208,8 +210,9 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void leftJoin(tableName, String localKey, String foreignKey, void leftJoin(tableName, String localKey, String foreignKey,
{String op = '=', {String op = '=',
List<String> additionalFields = const [], List<String> additionalFields = const [],
Set<String>? trampoline}) { Set<String>? trampoline,
_makeJoin(tableName, trampoline, JoinType.left, localKey, foreignKey, op, String? alias}) {
_makeJoin(tableName, trampoline, alias, JoinType.left, localKey, foreignKey, op,
additionalFields); additionalFields);
} }
@ -217,8 +220,9 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void rightJoin(tableName, String localKey, String foreignKey, void rightJoin(tableName, String localKey, String foreignKey,
{String op = '=', {String op = '=',
List<String> additionalFields = const [], List<String> additionalFields = const [],
Set<String>? trampoline}) { Set<String>? trampoline,
_makeJoin(tableName, trampoline, JoinType.right, localKey, foreignKey, op, String? alias}) {
_makeJoin(tableName, trampoline, alias, JoinType.right, localKey, foreignKey, op,
additionalFields); additionalFields);
} }
@ -226,8 +230,9 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void fullOuterJoin(tableName, String localKey, String foreignKey, void fullOuterJoin(tableName, String localKey, String foreignKey,
{String op = '=', {String op = '=',
List<String> additionalFields = const [], List<String> additionalFields = const [],
Set<String>? trampoline}) { Set<String>? trampoline,
_makeJoin(tableName, trampoline, JoinType.full, localKey, foreignKey, op, String? alias}) {
_makeJoin(tableName, trampoline, alias, JoinType.full, localKey, foreignKey, op,
additionalFields); additionalFields);
} }
@ -235,8 +240,9 @@ abstract class Query<T, Where extends QueryWhere> extends QueryBase<T> {
void selfJoin(tableName, String localKey, String foreignKey, void selfJoin(tableName, String localKey, String foreignKey,
{String op = '=', {String op = '=',
List<String> additionalFields = const [], List<String> additionalFields = const [],
Set<String>? trampoline}) { Set<String>? trampoline,
_makeJoin(tableName, trampoline, JoinType.self, localKey, foreignKey, op, String? alias}) {
_makeJoin(tableName, trampoline, alias, JoinType.self, localKey, foreignKey, op,
additionalFields); additionalFields);
} }

View file

@ -5,6 +5,7 @@ abstract class QueryWhere {
final Set<QueryWhere> _and = {}; final Set<QueryWhere> _and = {};
final Set<QueryWhere> _not = {}; final Set<QueryWhere> _not = {};
final Set<QueryWhere> _or = {}; final Set<QueryWhere> _or = {};
final Set<String> _raw = {};
Iterable<SqlExpressionBuilder> get expressionBuilders; Iterable<SqlExpressionBuilder> get expressionBuilders;
@ -20,6 +21,10 @@ abstract class QueryWhere {
_or.add(other); _or.add(other);
} }
void raw(String whereRaw) {
_raw.add(whereRaw);
}
String compile({String? tableName}) { String compile({String? tableName}) {
var b = StringBuffer(); var b = StringBuffer();
var i = 0; var i = 0;
@ -39,6 +44,11 @@ abstract class QueryWhere {
} }
} }
for (var raw in _raw) {
if (i++ > 0) b.write(' AND ');
b.write(' ($raw)');
}
for (var other in _and) { for (var other in _and) {
var sql = other.compile(); var sql = other.compile();
if (sql.isNotEmpty) b.write(' AND ($sql)'); if (sql.isNotEmpty) b.write(' AND ($sql)');