Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

many2many, joinForeignKey, JoinReferences support #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions deep-filtering.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,28 +193,39 @@ func getNestedType(naming schema.Namer, dbField *schema.Field, ofType reflect.Ty
fieldStructInstance: sourceStructType,
}

sourceForeignKey, ok := dbField.TagSettings["FOREIGNKEY"]
if ok {
result.fieldForeignKey = naming.ColumnName(dbField.Schema.Table, sourceForeignKey)
return result, nil
}
// Detetct many2many and foreignkey at the same time (fields can have both defined)
sourceForeignKey, sourceForeignKeyOk := dbField.TagSettings["FOREIGNKEY"]
manyToMany, manyToManyOk := dbField.TagSettings["MANY2MANY"]

// No foreign key found, then it must be a manyToMany
manyToMany, ok := dbField.TagSettings["MANY2MANY"]

if !ok {
if !sourceForeignKeyOk && !manyToManyOk {
return nil, fmt.Errorf("no 'foreignKey:...' or 'many2many:...' found in field %s", dbField.Name)
}

// Only foreignkey
if sourceForeignKeyOk && !manyToManyOk {
result.fieldForeignKey = naming.ColumnName(dbField.Schema.Table, sourceForeignKey)
return result, nil
}

// Woah it's a many-to-many!
result.relationType = "manyToMany"
result.manyToManyTable = manyToMany

// Based on the type we can just put _id behind it, again this only works with simple many-to-many structs
result.fieldForeignKey = naming.ColumnName(dbField.Schema.Table, ensureNotASlice(dbField.FieldType).Name()) + "_id"
fieldForeignKey, ok := dbField.TagSettings["JOINREFERENCES"]
if ok {
result.fieldForeignKey = naming.ColumnName(dbField.Schema.Table, fieldForeignKey)
} else {
result.fieldForeignKey = naming.ColumnName(dbField.Schema.Table, ensureNotASlice(dbField.FieldType).Name()) + "_id"
}

// Now the other table that we're getting information from.
result.destinationManyToManyForeignKey = naming.ColumnName(dbField.Schema.Table, ofType.Name()) + "_id"
destinationManyToManyForeignKey, ok := dbField.TagSettings["JOINFOREIGNKEY"]
if ok {
result.destinationManyToManyForeignKey = naming.ColumnName(dbField.Schema.Table, destinationManyToManyForeignKey)
} else {
result.destinationManyToManyForeignKey = naming.ColumnName(dbField.Schema.Table, ofType.Name()) + "_id"
}

return result, nil
}
Expand Down
108 changes: 105 additions & 3 deletions deep-filtering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package deepgorm

import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"reflect"
"strings"
"sync"
"testing"

"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"gorm.io/gorm/clause"
Expand Down Expand Up @@ -2368,3 +2370,103 @@ func TestAddDeepFilters_AddsDeepFiltersWithManyToMany2OnMultiFilter(t *testing.T
})
}
}

func TestAddDeepFilters_AddsDeepFiltersWithManyToManyCustomFields(t *testing.T) {
t.Parallel()
type End struct {
ID uuid.UUID `gorm:"column:endId;primaryKey"`
Value string `gorm:"column:endValue"`
}

type Middle struct {
ID uuid.UUID `gorm:"column:middleId;primaryKey"`
ResourceId uuid.UUID `gorm:"column:resourceIdJ"`
EndId uuid.UUID `gorm:"column:endIdJ"`
}

type Resource struct {
ID uuid.UUID `gorm:"column:resourceId;primaryKey"`
Name string `gorm:"column:resourceName"`
Ends []*End `gorm:"many2many:middles;foreignKey:resourceId;joinForeignKey:resourceIdJ;References:endId;JoinReferences:endIdJ"`
}

tests := map[string]struct {
records []*Resource
expected []Resource
filterMap []map[string]any
}{
"looking for 1 resource": {
records: []*Resource{
{
ID: uuid.MustParse("59aa5a8f-c5de-44fa-9355-080650481687"),
Name: "TestResource",
Ends: []*End{
{
ID: uuid.MustParse("c53184d8-e506-49f4-af18-93fb370f6df2"), // A
Value: "InfraNL",
},
{
ID: uuid.MustParse("4de16d5f-c10f-4206-b6ce-c14997341113"), // B
Value: "Blub",
},
},
},
},
expected: []Resource{
{
ID: uuid.MustParse("59aa5a8f-c5de-44fa-9355-080650481687"),
Name: "TestResource",
Ends: []*End{
{
ID: uuid.MustParse("c53184d8-e506-49f4-af18-93fb370f6df2"), // A
Value: "InfraNL",
},
},
},
},
filterMap: []map[string]any{
{
"ends": map[string]any{
"value": "InfraNL",
},
},
},
},
}

for name, testData := range tests {
testData := testData
t.Run(name, func(t *testing.T) {
t.Parallel()
// Arrange
database := newDatabase(t)
database.NamingStrategy = schema.NamingStrategy{
SingularTable: true,
NoLowerCase: false,
NameReplacer: strings.NewReplacer(
"resource_id_j", "resourceIdJ",
"end_id_j", "endIdJ",
),
}
_ = database.AutoMigrate(&Resource{}, &Middle{}, &End{})

database.CreateInBatches(testData.records, len(testData.records))

// Act
query, err := AddDeepFilters(database, Resource{}, testData.filterMap...)

// Assert
assert.Nil(t, err)

if assert.NotNil(t, query) {
var result []Resource
res := query.Preload(clause.Associations).Find(&result)

// Handle error
assert.Nil(t, res.Error)

assert.EqualValues(t, testData.expected, result)
}
})
}
}