improve WhereBuilder feature for package gdb

This commit is contained in:
John Guo 2022-05-07 15:11:31 +08:00
parent eaae7f46d2
commit 5332ce4c79
11 changed files with 101 additions and 5 deletions

View File

@ -17,6 +17,7 @@ import (
// Model is core struct implementing the DAO for ORM.
type Model struct {
*modelWhereBuilder
db DB // Underlying DB interface.
tx *TX // Underlying TX interface.
rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model.
@ -52,6 +53,8 @@ type Model struct {
onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns ON "DUPLICATE KEY UPDATE" statement.
}
type modelWhereBuilder = WhereBuilder
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
type ModelHandler func(m *Model) *Model

View File

@ -10,6 +10,7 @@ import (
"fmt"
)
// WhereBuilder holds multiple where conditions in a group.
type WhereBuilder struct {
safe *bool // If nil, it uses the safe attribute of its model.
model *Model // A WhereBuilder should be bound to certain Model.
@ -25,6 +26,7 @@ type WhereHolder struct {
Prefix string // Field prefix, eg: "user.", "order.".
}
// Builder creates and returns a WhereBuilder.
func (m *Model) Builder() *WhereBuilder {
// The WhereBuilder is safe in default when it is created using Builder().
var isSafe = true
@ -52,6 +54,7 @@ func (b *WhereBuilder) getBuilder() *WhereBuilder {
}
}
// Clone clones and returns a WhereBuilder that is a copy of current one.
func (b *WhereBuilder) Clone() *WhereBuilder {
newBuilder := b.model.Builder()
newBuilder.safe = b.safe
@ -60,6 +63,7 @@ func (b *WhereBuilder) Clone() *WhereBuilder {
return newBuilder
}
// Build builds current WhereBuilder and returns the condition string and parameters.
func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface{}) {
var (
ctx = b.model.GetCtx()
@ -115,7 +119,8 @@ func (b *WhereBuilder) Build() (conditionWhere string, conditionArgs []interface
return
}
func (b *WhereBuilder) convertWrappedBuilder(where interface{}, args []interface{}) (newWhere interface{}, newArgs []interface{}) {
// convertWhereBuilder converts parameter `where` to condition string and parameters if `where` is also a WhereBuilder.
func (b *WhereBuilder) convertWhereBuilder(where interface{}, args []interface{}) (newWhere interface{}, newArgs []interface{}) {
var builder *WhereBuilder
switch v := where.(type) {
case WhereBuilder:

View File

@ -16,7 +16,7 @@ import (
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
// multiple conditions will be joined into where statement using "AND".
func (b *WhereBuilder) doWhereType(whereType string, where interface{}, args ...interface{}) *WhereBuilder {
where, args = b.convertWrappedBuilder(where, args)
where, args = b.convertWhereBuilder(where, args)
builder := b.getBuilder()
if builder.whereHolder == nil {

View File

@ -11,7 +11,7 @@ package gdb
// WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid'
// WherePrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE `order`.`status`='paid' AND `order`.`channel`='bank'
func (b *WhereBuilder) WherePrefix(prefix string, where interface{}, args ...interface{}) *WhereBuilder {
where, args = b.convertWrappedBuilder(where, args)
where, args = b.convertWhereBuilder(where, args)
builder := b.getBuilder()
if builder.whereHolder == nil {

View File

@ -14,7 +14,7 @@ import (
// WhereOr adds "OR" condition to the where statement.
func (b *WhereBuilder) doWhereOrType(t string, where interface{}, args ...interface{}) *WhereBuilder {
where, args = b.convertWrappedBuilder(where, args)
where, args = b.convertWhereBuilder(where, args)
builder := b.getBuilder()
if builder.whereHolder == nil {

View File

@ -11,7 +11,7 @@ package gdb
// WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid')
// WhereOrPrefix("order", struct{Status:"paid", "channel":"bank"}) => WHERE xxx OR (`order`.`status`='paid' AND `order`.`channel`='bank')
func (b *WhereBuilder) WhereOrPrefix(prefix string, where interface{}, args ...interface{}) *WhereBuilder {
where, args = b.convertWrappedBuilder(where, args)
where, args = b.convertWhereBuilder(where, args)
builder := b.getBuilder()
builder.whereHolder = append(builder.whereHolder, WhereHolder{

View File

@ -6,84 +6,114 @@
package gdb
// callWhereBuilder creates and returns a new Model, and sets its WhereBuilder if current Model is safe.
// It sets the WhereBuilder and returns current Model directly if it is not safe.
func (m *Model) callWhereBuilder(builder *WhereBuilder) *Model {
model := m.getModel()
model.whereBuilder = builder
return model
}
// Where sets the condition statement for the builder. The parameter `where` can be type of
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
// multiple conditions will be joined into where statement using "AND".
// See WhereBuilder.Where.
func (m *Model) Where(where interface{}, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.Where(where, args...))
}
// Wheref builds condition string using fmt.Sprintf and arguments.
// Note that if the number of `args` is more than the placeholder in `format`,
// the extra `args` will be used as the where condition arguments of the Model.
// See WhereBuilder.Wheref.
func (m *Model) Wheref(format string, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.Wheref(format, args...))
}
// WherePri does the same logic as Model.Where except that if the parameter `where`
// is a single condition like int/string/float/slice, it treats the condition as the primary
// key value. That is, if primary key is "id" and given `where` parameter as "123", the
// WherePri function treats the condition as "id=123", but Model.Where treats the condition
// as string "123".
// See WhereBuilder.WherePri.
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePri(where, args...))
}
// WhereLT builds `column < value` statement.
// See WhereBuilder.WhereLT.
func (m *Model) WhereLT(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereLT(column, value))
}
// WhereLTE builds `column <= value` statement.
// See WhereBuilder.WhereLTE.
func (m *Model) WhereLTE(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereLTE(column, value))
}
// WhereGT builds `column > value` statement.
// See WhereBuilder.WhereGT.
func (m *Model) WhereGT(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereGT(column, value))
}
// WhereGTE builds `column >= value` statement.
// See WhereBuilder.WhereGTE.
func (m *Model) WhereGTE(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereGTE(column, value))
}
// WhereBetween builds `column BETWEEN min AND max` statement.
// See WhereBuilder.WhereBetween.
func (m *Model) WhereBetween(column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereBetween(column, min, max))
}
// WhereLike builds `column LIKE like` statement.
// See WhereBuilder.WhereLike.
func (m *Model) WhereLike(column string, like string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereLike(column, like))
}
// WhereIn builds `column IN (in)` statement.
// See WhereBuilder.WhereIn.
func (m *Model) WhereIn(column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereIn(column, in))
}
// WhereNull builds `columns[0] IS NULL AND columns[1] IS NULL ...` statement.
// See WhereBuilder.WhereNull.
func (m *Model) WhereNull(columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNull(columns...))
}
// WhereNotBetween builds `column NOT BETWEEN min AND max` statement.
// See WhereBuilder.WhereNotBetween.
func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNotBetween(column, min, max))
}
// WhereNotLike builds `column NOT LIKE like` statement.
// See WhereBuilder.WhereNotLike.
func (m *Model) WhereNotLike(column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNotLike(column, like))
}
// WhereNot builds `column != value` statement.
// See WhereBuilder.WhereNot.
func (m *Model) WhereNot(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNot(column, value))
}
// WhereNotIn builds `column NOT IN (in)` statement.
// See WhereBuilder.WhereNotIn.
func (m *Model) WhereNotIn(column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNotIn(column, in))
}
// WhereNotNull builds `columns[0] IS NOT NULL AND columns[1] IS NOT NULL ...` statement.
// See WhereBuilder.WhereNotNull.
func (m *Model) WhereNotNull(columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereNotNull(columns...))
}

View File

@ -7,71 +7,85 @@
package gdb
// WherePrefix performs as Where, but it adds prefix to each field in where statement.
// See WhereBuilder.WherePrefix.
func (m *Model) WherePrefix(prefix string, where interface{}, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefix(prefix, where, args...))
}
// WherePrefixLT builds `prefix.column < value` statement.
// See WhereBuilder.WherePrefixLT.
func (m *Model) WherePrefixLT(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixLT(prefix, column, value))
}
// WherePrefixLTE builds `prefix.column <= value` statement.
// See WhereBuilder.WherePrefixLTE.
func (m *Model) WherePrefixLTE(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixLTE(prefix, column, value))
}
// WherePrefixGT builds `prefix.column > value` statement.
// See WhereBuilder.WherePrefixGT.
func (m *Model) WherePrefixGT(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixGT(prefix, column, value))
}
// WherePrefixGTE builds `prefix.column >= value` statement.
// See WhereBuilder.WherePrefixGTE.
func (m *Model) WherePrefixGTE(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixGTE(prefix, column, value))
}
// WherePrefixBetween builds `prefix.column BETWEEN min AND max` statement.
// See WhereBuilder.WherePrefixBetween.
func (m *Model) WherePrefixBetween(prefix string, column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixBetween(prefix, column, min, max))
}
// WherePrefixLike builds `prefix.column LIKE like` statement.
// See WhereBuilder.WherePrefixLike.
func (m *Model) WherePrefixLike(prefix string, column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixLike(prefix, column, like))
}
// WherePrefixIn builds `prefix.column IN (in)` statement.
// See WhereBuilder.WherePrefixIn.
func (m *Model) WherePrefixIn(prefix string, column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixIn(prefix, column, in))
}
// WherePrefixNull builds `prefix.columns[0] IS NULL AND prefix.columns[1] IS NULL ...` statement.
// See WhereBuilder.WherePrefixNull.
func (m *Model) WherePrefixNull(prefix string, columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNull(prefix, columns...))
}
// WherePrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement.
// See WhereBuilder.WherePrefixNotBetween.
func (m *Model) WherePrefixNotBetween(prefix string, column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNotBetween(prefix, column, min, max))
}
// WherePrefixNotLike builds `prefix.column NOT LIKE like` statement.
// See WhereBuilder.WherePrefixNotLike.
func (m *Model) WherePrefixNotLike(prefix string, column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNotLike(prefix, column, like))
}
// WherePrefixNot builds `prefix.column != value` statement.
// See WhereBuilder.WherePrefixNot.
func (m *Model) WherePrefixNot(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNot(prefix, column, value))
}
// WherePrefixNotIn builds `prefix.column NOT IN (in)` statement.
// See WhereBuilder.WherePrefixNotIn.
func (m *Model) WherePrefixNotIn(prefix string, column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNot(prefix, column, in))
}
// WherePrefixNotNull builds `prefix.columns[0] IS NOT NULL AND prefix.columns[1] IS NOT NULL ...` statement.
// See WhereBuilder.WherePrefixNotNull.
func (m *Model) WherePrefixNotNull(prefix string, columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WherePrefixNotNull(prefix, columns...))
}

View File

@ -7,71 +7,85 @@
package gdb
// WhereOr adds "OR" condition to the where statement.
// See WhereBuilder.WhereOr.
func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOr(where, args...))
}
// WhereOrf builds `OR` condition string using fmt.Sprintf and arguments.
// See WhereBuilder.WhereOrf.
func (m *Model) WhereOrf(format string, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrf(format, args...))
}
// WhereOrLT builds `column < value` statement in `OR` conditions..
// See WhereBuilder.WhereOrLT.
func (m *Model) WhereOrLT(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrLT(column, value))
}
// WhereOrLTE builds `column <= value` statement in `OR` conditions..
// See WhereBuilder.WhereOrLTE.
func (m *Model) WhereOrLTE(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrLTE(column, value))
}
// WhereOrGT builds `column > value` statement in `OR` conditions..
// See WhereBuilder.WhereOrGT.
func (m *Model) WhereOrGT(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrGT(column, value))
}
// WhereOrGTE builds `column >= value` statement in `OR` conditions..
// See WhereBuilder.WhereOrGTE.
func (m *Model) WhereOrGTE(column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrGTE(column, value))
}
// WhereOrBetween builds `column BETWEEN min AND max` statement in `OR` conditions.
// See WhereBuilder.WhereOrBetween.
func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrBetween(column, min, max))
}
// WhereOrLike builds `column LIKE like` statement in `OR` conditions.
// See WhereBuilder.WhereOrLike.
func (m *Model) WhereOrLike(column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrLike(column, like))
}
// WhereOrIn builds `column IN (in)` statement in `OR` conditions.
// See WhereBuilder.WhereOrIn.
func (m *Model) WhereOrIn(column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrIn(column, in))
}
// WhereOrNull builds `columns[0] IS NULL OR columns[1] IS NULL ...` statement in `OR` conditions.
// See WhereBuilder.WhereOrNull.
func (m *Model) WhereOrNull(columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrNull(columns...))
}
// WhereOrNotBetween builds `column NOT BETWEEN min AND max` statement in `OR` conditions.
// See WhereBuilder.WhereOrNotBetween.
func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrNotBetween(column, min, max))
}
// WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions.
// See WhereBuilder.WhereOrNotLike.
func (m *Model) WhereOrNotLike(column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrNotLike(column, like))
}
// WhereOrNotIn builds `column NOT IN (in)` statement.
// See WhereBuilder.WhereOrNotIn.
func (m *Model) WhereOrNotIn(column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrNotIn(column, in))
}
// WhereOrNotNull builds `columns[0] IS NOT NULL OR columns[1] IS NOT NULL ...` statement in `OR` conditions.
// See WhereBuilder.WhereOrNotNull.
func (m *Model) WhereOrNotNull(columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrNotNull(columns...))
}

View File

@ -7,66 +7,79 @@
package gdb
// WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement.
// See WhereBuilder.WhereOrPrefix.
func (m *Model) WhereOrPrefix(prefix string, where interface{}, args ...interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefix(prefix, where, args...))
}
// WhereOrPrefixLT builds `prefix.column < value` statement in `OR` conditions..
// See WhereBuilder.WhereOrPrefixLT.
func (m *Model) WhereOrPrefixLT(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLT(prefix, column, value))
}
// WhereOrPrefixLTE builds `prefix.column <= value` statement in `OR` conditions..
// See WhereBuilder.WhereOrPrefixLTE.
func (m *Model) WhereOrPrefixLTE(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLTE(prefix, column, value))
}
// WhereOrPrefixGT builds `prefix.column > value` statement in `OR` conditions..
// See WhereBuilder.WhereOrPrefixGT.
func (m *Model) WhereOrPrefixGT(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixGT(prefix, column, value))
}
// WhereOrPrefixGTE builds `prefix.column >= value` statement in `OR` conditions..
// See WhereBuilder.WhereOrPrefixGTE.
func (m *Model) WhereOrPrefixGTE(prefix string, column string, value interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixGTE(prefix, column, value))
}
// WhereOrPrefixBetween builds `prefix.column BETWEEN min AND max` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixBetween.
func (m *Model) WhereOrPrefixBetween(prefix string, column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixBetween(prefix, column, min, max))
}
// WhereOrPrefixLike builds `prefix.column LIKE like` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixLike.
func (m *Model) WhereOrPrefixLike(prefix string, column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLike(prefix, column, like))
}
// WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixIn.
func (m *Model) WhereOrPrefixIn(prefix string, column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixIn(prefix, column, in))
}
// WhereOrPrefixNull builds `prefix.columns[0] IS NULL OR prefix.columns[1] IS NULL ...` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixNull.
func (m *Model) WhereOrPrefixNull(prefix string, columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNull(prefix, columns...))
}
// WhereOrPrefixNotBetween builds `prefix.column NOT BETWEEN min AND max` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixNotBetween.
func (m *Model) WhereOrPrefixNotBetween(prefix string, column string, min, max interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotBetween(prefix, column, min, max))
}
// WhereOrPrefixNotLike builds `prefix.column NOT LIKE like` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixNotLike.
func (m *Model) WhereOrPrefixNotLike(prefix string, column string, like interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotLike(prefix, column, like))
}
// WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement.
// See WhereBuilder.WhereOrPrefixNotIn.
func (m *Model) WhereOrPrefixNotIn(prefix string, column string, in interface{}) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotIn(prefix, column, in))
}
// WhereOrPrefixNotNull builds `prefix.columns[0] IS NOT NULL OR prefix.columns[1] IS NOT NULL ...` statement in `OR` conditions.
// See WhereBuilder.WhereOrPrefixNotNull.
func (m *Model) WhereOrPrefixNotNull(prefix string, columns ...string) *Model {
return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotNull(prefix, columns...))
}

View File

@ -28,6 +28,7 @@ func Test_Model_Builder(t *testing.T) {
t.Assert(len(all), 6)
})
// Where And
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
@ -43,4 +44,20 @@ func Test_Model_Builder(t *testing.T) {
t.Assert(len(all), 1)
})
db.SetDebug(true)
// Where Or
gtest.C(t, func(t *gtest.T) {
m := db.Model(table)
b := m.Builder()
all, err := m.WhereOr(
b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}),
).WhereOr(
b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}),
).WhereOr(
b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}),
).All()
t.AssertNil(err)
t.Assert(len(all), 6)
})
}