1
0
mirror of https://github.com/pocketbase/pocketbase.git synced 2025-03-22 06:51:56 +02:00

synced with master

This commit is contained in:
Gani Georgiev 2023-07-03 20:53:09 +03:00
commit a4101f7670
6 changed files with 50 additions and 7 deletions

View File

@ -76,6 +76,12 @@
- Allowed `0` as `RelationOptions.MinSelect` value to avoid the ambiguity between 0 and non-filled input value ([#2817](https://github.com/pocketbase/pocketbase/discussions/2817)).
## v0.16.7
- Minor optimization for the list/search queries to use `rowid` with the `COUNT` statement when available.
_This eliminates the temp B-TREE step when executing the query and for large datasets (eg. 150k) it could have 10x improvement (from ~580ms to ~60ms)._
## v0.16.6
- Fixed collection index column sort normalization in the Admin UI ([#2681](https://github.com/pocketbase/pocketbase/pull/2681); thanks @SimonLoir).

View File

@ -68,6 +68,11 @@ func (api *recordApi) list(c echo.Context) error {
searchProvider := search.NewProvider(fieldsResolver).
Query(api.app.Dao().RecordQuery(collection))
// views don't have "rowid" so we fallback to "id"
if collection.IsView() {
searchProvider.CountCol("id")
}
if requestData.Admin == nil && collection.ListRule != nil {
searchProvider.AddFilter(search.FilterData(*collection.ListRule))
}

View File

@ -190,7 +190,7 @@ func (f SchemaField) Validate() error {
excludeNames := BaseModelFieldNames()
// exclude special filter literals
excludeNames = append(excludeNames, "null", "true", "false")
excludeNames = append(excludeNames, "null", "true", "false", "_rowid_")
// exclude system literals
excludeNames = append(excludeNames, SystemFieldNames()...)

View File

@ -313,6 +313,15 @@ func TestSchemaFieldValidate(t *testing.T) {
},
[]string{"name"},
},
{
"reserved name (_rowid_)",
schema.SchemaField{
Type: schema.FieldTypeText,
Id: "1234567890",
Name: "_rowid_",
},
[]string{"name"},
},
{
"reserved name (id)",
schema.SchemaField{

View File

@ -36,6 +36,7 @@ type Result struct {
type Provider struct {
fieldResolver FieldResolver
query *dbx.SelectQuery
countCol string
page int
perPage int
sort []SortField
@ -56,6 +57,7 @@ type Provider struct {
func NewProvider(fieldResolver FieldResolver) *Provider {
return &Provider{
fieldResolver: fieldResolver,
countCol: "_rowid_",
page: 1,
perPage: DefaultPerPage,
sort: []SortField{},
@ -69,6 +71,13 @@ func (s *Provider) Query(query *dbx.SelectQuery) *Provider {
return s
}
// CountCol allows changing the default column (_rowid_) that is used
// to generated the COUNT SQL query statement.
func (s *Provider) CountCol(name string) *Provider {
s.countCol = name
return s
}
// Page sets the `page` field of the current search provider.
//
// Normalization on the `page` value is done during `Exec()`.
@ -198,7 +207,7 @@ func (s *Provider) Exec(items any) (*Result, error) {
baseTable = queryInfo.From[0]
}
clone := modelsQuery
countQuery := clone.Select("COUNT(DISTINCT [[" + baseTable + ".id]])").OrderBy()
countQuery := clone.Distinct(false).Select("COUNT(DISTINCT [[" + baseTable + "." + s.countCol + "]])").OrderBy()
if err := countQuery.Row(&totalCount); err != nil {
return nil, err
}

View File

@ -42,6 +42,20 @@ func TestProviderQuery(t *testing.T) {
}
}
func TestProviderCountCol(t *testing.T) {
p := NewProvider(&testFieldResolver{})
if p.countCol != "_rowid_" {
t.Fatalf("Expected the default countCol to be %s, got %s", "_rowid_", p.countCol)
}
p.CountCol("test")
if p.countCol != "test" {
t.Fatalf("Expected colCount to change to %s, got %s", "test", p.countCol)
}
}
func TestProviderPage(t *testing.T) {
r := &testFieldResolver{}
p := NewProvider(r).Page(10)
@ -228,7 +242,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":10,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT [[test.id]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT COUNT(DISTINCT [[test._rowid_]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 10",
},
},
@ -241,7 +255,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":30,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT [[test.id]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT COUNT(DISTINCT [[test._rowid_]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 30",
},
},
@ -274,7 +288,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":` + fmt.Sprint(MaxPerPage) + `,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT [[test.id]]) FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (((test2 != '' AND test2 IS NOT NULL)))) AND (test1 >= 2)",
"SELECT COUNT(DISTINCT [[test._rowid_]]) FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (((test2 != '' AND test2 IS NOT NULL)))) AND (test1 >= 2)",
"SELECT * FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (((test2 != '' AND test2 IS NOT NULL)))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC LIMIT 500",
},
},
@ -287,7 +301,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":10,"totalItems":0,"totalPages":0,"items":[]}`,
[]string{
"SELECT COUNT(DISTINCT [[test.id]]) FROM `test` WHERE (NOT (`test1` IS NULL)) AND (((test3 != '' AND test3 IS NOT NULL)))",
"SELECT COUNT(DISTINCT [[test._rowid_]]) FROM `test` WHERE (NOT (`test1` IS NULL)) AND (((test3 != '' AND test3 IS NOT NULL)))",
"SELECT * FROM `test` WHERE (NOT (`test1` IS NULL)) AND (((test3 != '' AND test3 IS NOT NULL))) ORDER BY `test1` ASC, `test3` ASC LIMIT 10",
},
},
@ -300,7 +314,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":2,"perPage":1,"totalItems":2,"totalPages":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT [[test.id]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT COUNT(DISTINCT [[test._rowid_]]) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 1 OFFSET 1",
},
},