Handle large and all entity queries (#3544)

* Remove upper page size limit
* Batch GetMany function
* Remove upper query limit from UI
This commit is contained in:
WithoutPants 2023-03-16 09:08:21 +11:00 committed by GitHub
parent ac67d640db
commit 58852f86fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 130 additions and 75 deletions

View File

@ -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.

20
pkg/sqlite/batch.go Normal file
View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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) {
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
}
scenes := make([]*models.Scene, len(ids))
for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
scenes[i] = s
table := qb.table()
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
}
for _, s := range unsorted {
i := intslice.IntIndex(ids, s.ID)
scenes[i] = s
}
return nil
}); err != nil {
return nil, err
}
for i := range scenes {

View File

@ -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 {

View File

@ -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 {

View File

@ -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;
@ -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;
@ -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>) => {