Skip to content

Commit

Permalink
Handle large and all entity queries (#3544)
Browse files Browse the repository at this point in the history
* Remove upper page size limit
* Batch GetMany function
* Remove upper query limit from UI
  • Loading branch information
WithoutPants authored Mar 15, 2023
1 parent ac67d64 commit 58852f8
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 74 deletions.
8 changes: 4 additions & 4 deletions pkg/models/find_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ func (ff FindFilterType) GetPage() int {
func (ff FindFilterType) GetPageSize() int {
const defaultPerPage = 25
const minPerPage = 0
const maxPerPage = 1000

if ff.PerPage == nil {
return defaultPerPage
}

if *ff.PerPage > maxPerPage {
return maxPerPage
} else if *ff.PerPage < minPerPage {
// removed the maxPerPage check. We already all -1 to indicate all results
// so there is no conceivable reason we should limit the page size

if *ff.PerPage < minPerPage {
// negative page sizes should return all results
// this is a sanity check in case GetPageSize is
// called with a negative page size.
Expand Down
20 changes: 20 additions & 0 deletions pkg/sqlite/batch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sqlite

const defaultBatchSize = 1000

// batchExec executes the provided function in batches of the provided size.
func batchExec(ids []int, batchSize int, fn func(batch []int) error) error {
for i := 0; i < len(ids); i += batchSize {
end := i + batchSize
if end > len(ids) {
end = len(ids)
}

batch := ids[i:end]
if err := fn(batch); err != nil {
return err
}
}

return nil
}
24 changes: 15 additions & 9 deletions pkg/sqlite/gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,17 +363,23 @@ func (qb *GalleryStore) Find(ctx context.Context, id int) (*models.Gallery, erro
}

func (qb *GalleryStore) FindMany(ctx context.Context, ids []int) ([]*models.Gallery, error) {
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(ids))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

galleries := make([]*models.Gallery, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
galleries[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(batch))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
galleries[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range galleries {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,17 +260,23 @@ func (qb *ImageStore) Find(ctx context.Context, id int) (*models.Image, error) {
}

func (qb *ImageStore) FindMany(ctx context.Context, ids []int) ([]*models.Image, error) {
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(ids))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

images := make([]*models.Image, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
images[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(batch))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
images[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range images {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/movies.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,23 @@ func (qb *movieQueryBuilder) Find(ctx context.Context, id int) (*models.Movie, e

func (qb *movieQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Movie, error) {
tableMgr := movieTableMgr
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

ret := make([]*models.Movie, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range ret {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/performer.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,17 +299,23 @@ func (qb *PerformerStore) Find(ctx context.Context, id int) (*models.Performer,

func (qb *PerformerStore) FindMany(ctx context.Context, ids []int) ([]*models.Performer, error) {
tableMgr := performerTableMgr
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

ret := make([]*models.Performer, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range ret {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,18 +364,24 @@ func (qb *SceneStore) Find(ctx context.Context, id int) (*models.Scene, error) {
}

func (qb *SceneStore) FindMany(ctx context.Context, ids []int) ([]*models.Scene, error) {
scenes := make([]*models.Scene, len(ids))

table := qb.table()
q := qb.selectDataset().Prepared(true).Where(table.Col(idColumn).In(ids))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := qb.selectDataset().Prepared(true).Where(table.Col(idColumn).In(batch))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

scenes := make([]*models.Scene, len(ids))
for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
scenes[i] = s
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
scenes[i] = s
return nil
}); err != nil {
return nil, err
}

for i := range scenes {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/studio.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,23 @@ func (qb *studioQueryBuilder) Find(ctx context.Context, id int) (*models.Studio,

func (qb *studioQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Studio, error) {
tableMgr := studioTableMgr
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

ret := make([]*models.Studio, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range ret {
Expand Down
24 changes: 15 additions & 9 deletions pkg/sqlite/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,23 @@ func (qb *tagQueryBuilder) Find(ctx context.Context, id int) (*models.Tag, error

func (qb *tagQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Tag, error) {
tableMgr := tagTableMgr
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return nil, err
}

ret := make([]*models.Tag, len(ids))

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
unsorted, err := qb.getMany(ctx, q)
if err != nil {
return err
}

for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
ret[i] = s
}

return nil
}); err != nil {
return nil, err
}

for i := range ret {
Expand Down
7 changes: 0 additions & 7 deletions ui/v2.5/src/components/List/ListFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { useDebounce } from "src/hooks/debounce";

const maxPageSize = 1000;
interface IListFilterProps {
onFilterUpdate: (newFilter: ListFilterModel) => void;
filter: ListFilterModel;
Expand Down Expand Up @@ -134,11 +133,6 @@ export const ListFilter: React.FC<IListFilterProps> = ({
return;
}

// don't allow page sizes over 1000
if (pp > maxPageSize) {
pp = maxPageSize;
}

const newFilter = cloneDeep(filter);
newFilter.itemsPerPage = pp;
newFilter.currentPage = 1;
Expand Down Expand Up @@ -377,7 +371,6 @@ export const ListFilter: React.FC<IListFilterProps> = ({
<Form.Control
type="number"
min={1}
max={maxPageSize}
className="text-input"
ref={perPageInput}
onKeyPress={(e: React.KeyboardEvent<HTMLInputElement>) => {
Expand Down

0 comments on commit 58852f8

Please sign in to comment.