V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  f0rb  ›  全部回复第 1 页 / 共 2 页
回复总数  24
1  2  
@VVVYGD "其实就是类似 SQL 解析"
看懂了,我做的东西和你不一样,
你做的是 SQL 转 MongoDB
我做的是对象转 SQL 或者 MongoDB 或者其他 NoSQL 语言
@VVVYGD 所以你这里每条查询都需要构造一次类似这样的条件是吧:#b" local="abc" && (xx=1 || b=2 && c=3)",那你这种怎么重用了,到处都是字符串吗?你这里不过是把 AND 换成&&,OR 换成||了,别的和 SQL 有什么区别呢?你并没有造出个新轮子,只是给旧轮子涂了层新漆。

你要考虑到做一个信息系统, 前端都会有多个输入框由用户填写查询条件进行搜索,而多个查询条件是可以任意组合的,只能靠 if 语句判断用户是否填写了某个条件来决定是否拼接对应的条件,不知道这种场景你是怎么处理的。

pub enum MongoOp {
Eq,
Gt,
Gte,
Lt,
Lte,
Ne,
Like,
NotLike, // { item: { $not: { $regex: "^p.*" } } }
In,
NotIn,
}

我看你这里都定义了这些谓词了,你直接把列名和他们拼接成 ageGt 这样,再解析成条件岂不是方便多了。
@VVVYGD 而且使用字符串不利于 IDE 的代码检查还有代码重构和复用
@VVVYGD 不一样哦,a > 1 对应 field(a, gt,1) 需要 3 个参数,而 aGt 只需要一个参数,值是跟着参数传进来
前后端传参也不一样的方式也不一样,aGt=1 对应 json 是{"aGt":1}, 或者是?aGt=1&b=2, 不太清楚你这个前端参数是怎么传的,我这也有个构造或语句的例子: https://juejin.cn/post/7409608924054700047
那路径里面 windows 的\怎么处理呢,只在 linux 上开发?
@RainCats 楼主是 JDK21 啊
@NikoXu 何出此言
你要是个人项目的话,看看 DoytoQuery ,通过定义对象来控制生成的 SQL 语句,上面那个 Comment 的例子大概可以这样写:

@SuperBuilder
public class CommentQuery extends PageQuery {
private List<Long> parentIdIn;
}

CommentQuery commentQuery = CommentQuery.builder().parentIdIn(list).sort("createdAt,DESC").build();
List<Comment> comments = dataQueryClient.query(commentQuery);

更复杂的查询示例还有 TPC-H 的 22 个用来对数据库进行性能测试的查询语句。
之前推广的比较佛系,所以估计没什么人了解。
前几天还在这里发了个帖子想问问有没有人搞过推广的,想先推下 Go 版本的试试。
之前在 BAT 一家干过,有个 C++的模块我到离职了都没编译成功过
@VVVYGD 我这个方案是重构来的,一开始也是拼字符串,不过是在 Java 的注解里,比如这样
public class UserQuery {
@QueryField(and = "username Like CONCAT('%', ?, '%')")
private String usernameLike;
}
这种写法遇到 IN 查询的时候有个问题,就是不能提前知道数组里元素的个数来确定占位符的个数
后面发现,字段名称里面已经包含列名和谓词两个要素了,剩下的占位符直接根据赋值去确定
然后就确定了这种通过后缀来推导查询条件的方案。
因为通过重构把重复的代码都进行了封装,所以代码量是最少的
@VVVYGD 我看了一下例子,是用 format!("a > 1");转成 ("a":"$gt",1) ? 这还是要拼字符串吧?
我这儿的方案是把列名和谓词合二为一,把原来需要 3 个元素的查询条件变成了两个,变成键值对的形式,
比如 aGt = 1 ,既能表示为 a > 1 ,也能表示为("a":"$gt",1) ,相当于直接把 OO 编程语言翻译成不同数据库的查询语言了,不需要额外去拼接字符串
想搞副业的都是想立即获得现金流的,但真正能发大财的都需要一定的时间沉淀,先建好护城河,再考虑赚钱,一般人扛不住
@wwhontheway 传统的 ORM 概念里,使用实体构建一些使用等号为比较符的查询条件,使得实体对象额外承担了构建查询子句的责任,显然不符合单一职责原则。

实体对象的字段只能对应一个列名,如 age ,但是查询子句可能会包含同一列的多个查询条件,如 age > ? and age < ?,这就是使得实体对象无法同时兼顾列名的映射和查询条件的映射。

而单独使用查询对象构建查询子句就不存在这样的限制,例如
type UserQuery struct {
PageQuery
Name *string
NameNe *string
Age *int
AgeLt *int
AgeGt *int
}

注意这里字段的类型都是指针,在构建查询子句时,只为不为 nil 的字段构建查询条件和参数。
@wwhontheway intentory.go 里就包含了增删查改的全部代码

Gorm 只能基于 model 构造比较符是’=‘的查询条件:

db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;

非=的查询条件需要手搓:

db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;

而使用 GoooQo ,你只需要定义好查询对象,
type UserQuery struct {
PageQuery
NameNe *string
AgeGt *int
}

然后基于查询参数构造实例就好了
userDataAccess.Query(ctx, &UserQuery{NameNe: PStr("f0rb"), AgeGt: PInt(20)})

这就是 OQM 技术中通过后缀来推导字段对应的查询条件。查询对象专注于构建查询子句,符合单一职责原则。

好处就是,前端向后端传递查询参数时,可以通过反射自动构建查询对象的实例。而当需求变更需要增减查询条件时,只需要在查询对象里增减字段即可。
@VVVYGD 我看到你这个是 rust 写的,我是先用 Java 写了一个,然后写专利的时候写了个 Go 的,来验证 OQM 这个 idea 是不是能适用所有语言
@VVVYGD 我这里面也有个 MongoDB 的模块,这次文章里没提,用的是代码生成的方法,比如为 QtyGt 生成这样的代码 if q.QtyGt != nil {
d = append(d, D{{"qty", D{{"$gt", q.QtyGt}}}})
}
完整的在这里:
https://github.com/doytowin/goooqo/blob/main/main/inventory_query_builder.go
@matrix1010 我这里一张表只需要编写实体对象和查询对象两个结构体,根据查询对象字段的名称生成查询条件,生成方式有两种,一种是通过反射读取字段信息,生成查询条件,一种是通过 gen 包为查询对象生成一堆 if 来拼接查询条件,比如这个 https://github.com/doytowin/goooqo/blob/main/main/user_query_builder.go 。 除了这两个结构体和两行构建代码,其他代码全部都封装了。
没明白你这里说的完全手写是什么意思。

我这里的代码生成是基于查询对象生成 if 构建语句,而不是从表生成实体对象。
我感觉国外一些开发一看到 ORM 几个字母就 PTSD 了,也不看内容,一上来就攻击你
1  2  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1570 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 23ms · UTC 16:54 · PVG 00:54 · LAX 09:54 · JFK 12:54
Developed with CodeLauncher
♥ Do have faith in what you're doing.