Order & Sorting

This commit is contained in:
2021-02-08 19:42:20 -05:00
parent 6697358960
commit af237110f9
12 changed files with 727 additions and 604 deletions

View File

@@ -2,6 +2,7 @@ package db
import (
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"reichard.io/imagini/graph/model"
)
@@ -18,11 +19,17 @@ func (dbm *DBManager) Album(album *model.Album) (int64, error) {
return count, err
}
func (dbm *DBManager) Albums() ([]*model.Album, int64, error) {
func (dbm *DBManager) Albums(userID string, filters *model.AlbumFilter, page *model.Page, order *model.Order) ([]*model.Album, model.PageResponse, error) {
// Initial User Filter
tx := dbm.db.Session(&gorm.Session{}).Model(&model.Album{}).Where("user_id == ?", userID)
// Dynamically Generate Base Query
tx, pageResponse := dbm.generateBaseQuery(tx, filters, page, order)
// Acquire Results
var foundAlbums []*model.Album
var count int64
err := dbm.db.Find(&foundAlbums).Count(&count).Error
return foundAlbums, count, err
err := tx.Find(&foundAlbums).Error
return foundAlbums, pageResponse, err
}
func (dbm *DBManager) DeleteAlbum(album *model.Album) error {

View File

@@ -1,11 +1,13 @@
package db
import (
"errors"
"fmt"
"path"
"reflect"
"github.com/iancoleman/strcase"
log "github.com/sirupsen/logrus"
// "gorm.io/gorm/logger"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
@@ -30,6 +32,7 @@ func NewMgr(c *config.Config) *DBManager {
if c.DBType == "SQLite" {
dbLocation := path.Join(c.ConfigPath, "imagini.db")
dbm.db, _ = gorm.Open(sqlite.Open(dbLocation), gormConfig)
dbm.db = dbm.db.Debug()
} else {
log.Fatal("Unsupported Database")
}
@@ -48,28 +51,9 @@ func NewMgr(c *config.Config) *DBManager {
dbm.bootstrapDatabase()
}
dbm.testFeatures()
return dbm
}
func (dbm *DBManager) testFeatures() {
// Get Devices By UserID
// var myDevices []model.Device
// dbm.db.Debug().Where(&model.Device{UserID: "97589354-cd42-40e2-bc5e-7ba6badf89fa"}).Find(&myDevices)
// fmt.Printf("Devices: %+v\n", myDevices)
// Get User by DeviceID
// var myUser []model.User
// dbm.db.Debug().Model(&model.User{}).
// Select("users.*").
// Joins("left join devices on users.id = devices.user_id").
// Where("devices.id = ?", "4e9aa851-f25b-4330-91dc-c0f975e56fa1").
// Find(&myUser)
// fmt.Printf("User: %+v\n", myUser)
}
func (dbm *DBManager) bootstrapDatabase() {
log.Info("[query] Bootstrapping database.")
@@ -88,46 +72,84 @@ func (dbm *DBManager) bootstrapDatabase() {
}
}
// TODO
func (dbm *DBManager) QueryBuilder(dest interface{}, params []byte) (int64, error) {
// TODO:
// - Where Filters
// - Sort Filters
// - Paging Filters
func (dbm *DBManager) generateBaseQuery(tx *gorm.DB, filter interface{}, page *model.Page, order *model.Order) (*gorm.DB, model.PageResponse) {
tx = dbm.generateFilter(tx, filter)
tx = dbm.generateOrder(tx, order, filter)
tx, pageResponse := dbm.generatePage(tx, page)
return tx, pageResponse
}
objType := fmt.Sprintf("%T", dest)
if objType == "*[]model.MediaItem" {
// TODO: Validate MediaItem Type
} else {
// Return Error
return 0, errors.New("Invalid type")
func (dbm *DBManager) generateOrder(tx *gorm.DB, order *model.Order, filter interface{}) *gorm.DB {
// Set Defaults
orderBy := "created_at"
orderDirection := model.OrderDirectionDesc
if order == nil {
order = &model.Order{
By: &orderBy,
Direction: &orderDirection,
}
}
var count int64
err := dbm.db.Find(dest).Count(&count).Error
return count, err
if order.By == nil {
order.By = &orderBy
}
// Paging:
// - Regular Pagination:
// - /api/v1/MediaItems?page[limit]=50&page=2
// - Meta Count Only
// - /api/v1/MediaItems?page[limit]=0
if order.Direction == nil {
order.Direction = &orderDirection
}
// Sorting:
// - Ascending Sort:
// - /api/v1/MediaItems?sort=created_at
// - Descending Sort:
// - /api/v1/MediaItems?sort=-created_at
// Get Possible Values
ptr := reflect.New(reflect.TypeOf(filter).Elem())
v := reflect.Indirect(ptr)
// Filters:
// - Greater Than / Less Than (created_at, updated_at, exif_date)
// - /api/v1/MediaItems?filter[created_at]>=2020-01-01&filter[created_at]<=2021-01-01
// - Long / Lat Range (latitude, longitude)
// - /api/v1/MediaItems?filter[latitude]>=71.1827&filter[latitude]<=72.0000&filter[longitude]>=100.000&filter[longitude]<=101.0000
// - Image / Video (media_type)
// - /api/v1/MediaItems?filter[media_type]=Image
// - Tags (tags)
// - /api/v1/MediaItems?filter[tags]=id1,id2,id3
// - Albums (albums)
// - /api/v1/MediaItems?filter[albums]=id1
isValid := false
for i := 0; i < v.NumField(); i++ {
fieldName := v.Type().Field(i).Name
if strcase.ToSnake(*order.By) == strcase.ToSnake(fieldName) {
isValid = true
break
}
}
if isValid {
tx = tx.Order(fmt.Sprintf("%s %s", strcase.ToSnake(*order.By), order.Direction.String()))
}
return tx
}
func (dbm *DBManager) generatePage(tx *gorm.DB, page *model.Page) (*gorm.DB, model.PageResponse) {
// Set Defaults
var count int64
pageSize := 50
pageNum := 1
if page == nil {
page = &model.Page{
Size: &pageSize,
Page: &pageNum,
}
}
if page.Size == nil {
page.Size = &pageSize
}
if page.Page == nil {
page.Page = &pageNum
}
// Acquire Counts Before Pagination
tx.Count(&count)
// Calculate Offset
calculatedOffset := (*page.Page - 1) * *page.Size
tx = tx.Limit(*page.Size).Offset(calculatedOffset)
return tx, model.PageResponse{
Page: *page.Page,
Size: *page.Size,
Total: int(count),
}
}

View File

@@ -3,6 +3,7 @@ package db
import (
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"reichard.io/imagini/graph/model"
)
@@ -21,11 +22,17 @@ func (dbm *DBManager) Device(device *model.Device) (int64, error) {
return count, err
}
func (dbm *DBManager) Devices() ([]*model.Device, int64, error) {
func (dbm *DBManager) Devices(userID string, filters *model.DeviceFilter, page *model.Page, order *model.Order) ([]*model.Device, model.PageResponse, error) {
// Initial User Filter
tx := dbm.db.Session(&gorm.Session{}).Model(&model.Device{}).Where("user_id == ?", userID)
// Dynamically Generate Base Query
tx, pageResponse := dbm.generateBaseQuery(tx, filters, page, order)
// Acquire Results
var foundDevices []*model.Device
var count int64
err := dbm.db.Find(&foundDevices).Count(&count).Error
return foundDevices, count, err
err := tx.Find(&foundDevices).Error
return foundDevices, pageResponse, err
}
func (dbm *DBManager) DeleteDevice(user *model.Device) error {

View File

@@ -10,10 +10,13 @@ import (
)
// Generic function used to generate filters for the DB
func (dbm *DBManager) generateFilters(filter interface{}) (*gorm.DB, error) {
tx := dbm.db.Session(&gorm.Session{}).Debug()
func (dbm *DBManager) generateFilter(tx *gorm.DB, filter interface{}) *gorm.DB {
ptr := reflect.ValueOf(filter)
v := reflect.Indirect(ptr)
v := reflect.ValueOf(filter)
if v == reflect.ValueOf(nil) {
return tx
}
for i := 0; i < v.NumField(); i++ {
fieldName := strcase.ToSnake(v.Type().Field(i).Name)
@@ -45,7 +48,7 @@ func (dbm *DBManager) generateFilters(filter interface{}) (*gorm.DB, error) {
}
}
return tx, nil
return tx
}
func generateStringFilter(tx *gorm.DB, fieldName string, filter *model.StringFilter) *gorm.DB {

View File

@@ -2,6 +2,7 @@ package db
import (
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"reichard.io/imagini/graph/model"
)
@@ -18,14 +19,16 @@ func (dbm *DBManager) MediaItem(mediaItem *model.MediaItem) (int64, error) {
return count, err
}
func (dbm *DBManager) MediaItems(filters *model.MediaItemFilter) ([]*model.MediaItem, int64, error) {
// Perform Filters
tx, err := dbm.generateFilters(*filters)
if err != nil {
return nil, 0, err
}
// UserID, Filters, Sort, Page, Delete
func (dbm *DBManager) MediaItems(userID string, filters *model.MediaItemFilter, page *model.Page, order *model.Order) ([]*model.MediaItem, model.PageResponse, error) {
// Initial User Filter
tx := dbm.db.Session(&gorm.Session{}).Model(&model.MediaItem{}).Where("user_id == ?", userID)
// Dynamically Generate Base Query
tx, pageResponse := dbm.generateBaseQuery(tx, filters, page, order)
// Acquire Results
var mediaItems []*model.MediaItem
var count int64
err = tx.Find(&mediaItems).Count(&count).Error
return mediaItems, count, err
err := tx.Find(&mediaItems).Error
return mediaItems, pageResponse, err
}

View File

@@ -2,6 +2,7 @@ package db
import (
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
"reichard.io/imagini/graph/model"
)
@@ -18,11 +19,17 @@ func (dbm *DBManager) Tag(tag *model.Tag) (int64, error) {
return count, err
}
func (dbm *DBManager) Tags() ([]*model.Tag, int64, error) {
func (dbm *DBManager) Tags(userID string, filters *model.TagFilter, page *model.Page, order *model.Order) ([]*model.Tag, model.PageResponse, error) {
// Initial User Filter
tx := dbm.db.Session(&gorm.Session{}).Model(&model.Tag{}).Where("user_id == ?", userID)
// Dynamically Generate Base Query
tx, pageResponse := dbm.generateBaseQuery(tx, filters, page, order)
// Acquire Results
var foundTags []*model.Tag
var count int64
err := dbm.db.Find(&foundTags).Count(&count).Error
return foundTags, count, err
err := tx.Find(&foundTags).Error
return foundTags, pageResponse, err
}
func (dbm *DBManager) DeleteTag(tag *model.Tag) error {

View File

@@ -3,6 +3,7 @@ package db
import (
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"reichard.io/imagini/graph/model"
)
@@ -25,11 +26,17 @@ func (dbm *DBManager) User(user *model.User) (int64, error) {
return count, err
}
func (dbm *DBManager) Users() ([]*model.User, int64, error) {
func (dbm *DBManager) Users(filters *model.UserFilter, page *model.Page, order *model.Order) ([]*model.User, model.PageResponse, error) {
// Initial User Filter
tx := dbm.db.Session(&gorm.Session{}).Model(&model.Tag{})
// Dynamically Generate Base Query
tx, pageResponse := dbm.generateBaseQuery(tx, filters, page, order)
// Acquire Results
var foundUsers []*model.User
var count int64
err := dbm.db.Find(&foundUsers).Count(&count).Error
return foundUsers, count, err
err := tx.Find(&foundUsers).Error
return foundUsers, pageResponse, err
}
func (dbm *DBManager) DeleteUser(user model.User) error {