连接子句包括软删除的行

huangapple go评论154阅读模式
英文:

Join clause including soft-deleted rows

问题

我遇到了一个问题,使用go-pg orm生成带有连接的正确的select查询语句时出现了困难,其中一个表记录可以进行软删除,而其他两个表则不行。

数据库表:

pipeline_instances
instance_id int
pipeline_id int
event_id int
pipeline_triggers
id int
pipeline_id int
deleted_at timestamp
pipeline_trigger_events
event_id int
trigger_id int

go-pg模型:

type pipelineTriggerEvent struct {
	tableName        struct{}          `pg:"pipeline_trigger_events,alias:pte"`
	Trigger          *pipelineTrigger  `pg:"rel:has-one,join_fk:id"`
	PipelineInstance *pipelineInstance `pg:"rel:has-one,join_fk:event_id"`
	*TriggerEvent
}

type pipelineTrigger struct {
	tableName struct{} `pg:"pipeline_triggers,alias:pt"`
	*Trigger 
}

type pipelineInstance struct {
	tableName struct{} `pg:"pipeline_pipeline_instances,alias:ppi"`
	*PipelineInstance
}

我尝试生成的查询语句:

SELECT 
  pte.*, trigger.*, pipeline_instance.*
FROM 
  pipeline_trigger_events AS pte 
  LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id) 
  LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id 

go-pg orm生成的查询语句:

SELECT 
  pte.*, trigger.*, pipeline_instance.*
FROM 
  pipeline_trigger_events AS pte 
  LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id) 
      AND trigger.deleted_at IS NULL -- 这是不需要的行。
  LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id 

var triggerevents []pipelineTriggerEvent
q := db.Model(&triggerevents).
		Column("pte.*").
		Relation("Trigger").
		Relation("PipelineInstance", func(q *orm.Query) (*orm.Query, error) {
			q = q.Join(" AND trigger.pipeline_id = pipeline_instance.pipeline_id")
			return q, nil
		})

在上述提到的3个表/模型中,只有pipeline_triggers表具有用于软删除的deleted_at列。我的要求是在结果集中包括软删除的pipeline_triggers行。但是,go-pg orm会自动在join子句中添加trigger.deleted_at IS NULL条件。我该如何删除此条件并获取所有行,包括软删除的行。

我尝试使用AllWithDeleted函数,但它适用于主模型pipeline_trigger_events(该表无论如何都没有deleted_at列),而不适用于pipeline_triggers,因此会出现以下错误:
pg: model=PipelineTriggerEvent does not support soft deletes

英文:

I am having trouble generating correct select query with joins using go-pg orm where one table record can be soft-deleted and other 2 cant.

DB tables:

pipeline_instances
instance_id int
pipeline_id int
event_id int
pipeline_triggers
id int
pipeline_id int
deleted_at timestamp
pipeline_trigger_events
event_id int
trigger_id int

go-pg Models:

type pipelineTriggerEvent struct {
	tableName        struct{}          `pg:"pipeline_trigger_events,alias:pte"`
	Trigger          *pipelineTrigger  `pg:"rel:has-one,join_fk:id"`
	PipelineInstance *pipelineInstance `pg:"rel:has-one,join_fk:event_id"`
	*TriggerEvent
}

type pipelineTrigger struct {
	tableName struct{} `pg:"pipeline_triggers,alias:pt"`
	*Trigger 
}

type pipelineInstance struct {
	tableName struct{} `pg:"pipeline_pipeline_instances,alias:ppi"`
	*PipelineInstance
}

The query I am trying to generate:

SELECT 
  pte.*, trigger.*, pipeline_instance.*
FROM 
  pipeline_trigger_events AS pte 
  LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id) 
  LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id 

The query getting generated by go-pg orm:

SELECT 
  pte.*, trigger.*, pipeline_instance.*
FROM 
  pipeline_trigger_events AS pte 
  LEFT JOIN pipeline_triggers AS trigger ON (trigger.id = pte.trigger_id) 
      AND trigger.deleted_at IS NULL -- this is the unwanted line.
  LEFT JOIN pipeline_pipeline_instances AS pipeline_instance ON pipeline_instance.event_id = pte.event_id AND trigger.pipeline_id = pipeline_instance.pipeline_id 

var triggerevents []pipelineTriggerEvent
q := db.Model(&triggerevents).
		Column("pte.*").
		Relation("Trigger").
		Relation("PipelineInstance", func(q *orm.Query) (*orm.Query, error) {
			q = q.Join(" AND trigger.pipeline_id = pipeline_instance.pipeline_id")
			return q, nil
		})

Of all the 3 tables/models mentioned above, only pipeline_triggers table has deleted_at column that is used for soft deletion. My requirement is to include the soft deleted pipeline_triggers rows also in the result set. But go-pg orm is automatically adding the trigger.deleted_at IS NULL condition in the join clause. How can I remove this condition and get all rows including soft deleted ones.

I tried using AllWithDeleted function but it works on the main model, which is pipeline_trigger_events (and this table does not have deleted_at column anyway) and not on pipeline_triggers and therefore fails with this error:
pg: model=PipelineTriggerEvent does not support soft deletes

答案1

得分: 1

在浏览了一下 pg-go 的代码后,我不确定你想要做的是否受支持。要确定,你可能需要在调试器中逐步执行下面的代码。

在构建连接查询的查询时,有这部分代码:

https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L283

if isSoftDelete {
		b = append(b, " AND "...)
		b = j.appendAlias(b)
		b = j.appendSoftDelete(b, q.flags)
	}

第一行 j.appendAlias(b) 调用了下面的 appendAlias() 函数:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L200

func appendAlias(b []byte, j *join) []byte {
	if j.hasParent() {
		b = appendAlias(b, j.Parent)
		b = append(b, "__"...)
	}
	b = append(b, j.Rel.Field.SQLName...)
	return b
}

由于这两个连接查询都有一个 has-one 的父关系,它会被添加到所有的表中:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L153

func (j *join) hasParent() bool {
	if j.Parent != nil {
		switch j.Parent.Rel.Type {
		case HasOneRelation, BelongsToRelation:
			return true
		}
	}
	return false
}

我认为对你有用的解决方法是只为父关系调用 appendAlias(),而不是其他两个关系,但看起来 pg-go 并不支持这样做。

对于这个问题,你可以直接调用 pg.Query()pg.QueryWithContext(),并传入你上面提到的 SQL 语句。

值得一提的是,pg-go/pg 处于维护模式,所以他们可能不会支持这个功能。根据你的项目对 pg-go 的依赖程度,你可以考虑使用正在积极开发的 Bun

附录

这是在上面的第一个代码片段中调用的 appendSoftDelete() 函数:

https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L189

func (j *join) appendSoftDelete(b []byte, flags queryFlag) []byte {
	b = append(b, '.')
	b = append(b, j.JoinModel.Table().SoftDeleteField.Column...)
	if hasFlag(flags, deletedFlag) {
		b = append(b, " IS NOT NULL"...)
	} else {
		b = append(b, " IS NULL"...)
	}
	return b
}
英文:

After looking through the code of pg-go a bit, I don’t know if what you’re trying to do is supported. To know for sure you’d probably want to step through the code below in a debugger.

When the query is being built for the joins, it has this section:

https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L283

if isSoftDelete {
		b = append(b, " AND "...)
		b = j.appendAlias(b)
		b = j.appendSoftDelete(b, q.flags)
	}

The line j.appendAlias(b) calls the appendAlias() function below:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L200

func appendAlias(b []byte, j *join) []byte {
	if j.hasParent() {
		b = appendAlias(b, j.Parent)
		b = append(b, "__"...)
	}
	b = append(b, j.Rel.Field.SQLName...)
	return b
}

Since the joins both have a has-one parent relation, it gets added for all the tables:
https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L153

func (j *join) hasParent() bool {
	if j.Parent != nil {
		switch j.Parent.Rel.Type {
		case HasOneRelation, BelongsToRelation:
			return true
		}
	}
	return false
}

I think what would fix this for you would be to only call appendAlias() for the parent relation and not the other two, but it doesn’t look like that’s supported by pg-go.

What you can do for this is just call pg.Query() or pg.QueryWithContext() and pass in the sql statement you included above.

It’s also worth mentioning that pg-go/pg is in maintenance mode so it’s unlikely they’ll ever support this. Depending on how entrenched this project is in pg-go, you might consider using Bun which is actively being developed.

Appendix

Here’s the appendSoftDelete() function that gets called in the first snippet above:

https://github.com/go-pg/pg/blob/c9ee578a38d6866649072df18a3dbb36ff369747/orm/join.go#L189

func (j *join) appendSoftDelete(b []byte, flags queryFlag) []byte {
	b = append(b, '.')
	b = append(b, j.JoinModel.Table().SoftDeleteField.Column...)
	if hasFlag(flags, deletedFlag) {
		b = append(b, " IS NOT NULL"...)
	} else {
		b = append(b, " IS NULL"...)
	}
	return b
}

huangapple
  • 本文由 发表于 2022年11月29日 19:17:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/74613230.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定