kjk / notionapi

Unofficial Go API for Notion.so
https://blog.kowalczyk.info/article/c9df78cbeaae4e0cb2848c9964bcfc94/using-notion-api-go-client.html
BSD 2-Clause "Simplified" License
1.82k stars 86 forks source link

Nested filters #24

Closed tattali closed 4 years ago

tattali commented 4 years ago

Hi, thanks for your outstanding work with this unofficial API !

But I find out that Notion team has improve the way to handle filters to add filter groups, since 28/05. Change log - Feature

This new way seems to break the actual filters structure so I update locally the query_collection.go to make it work

Add two new types

// query_collection.go

// Query2FilterGroup describes the filtering of a query
type Query2FilterGroup struct {
    Filter   *Query2Filter        `json:"filter"`
    Filters  []*Query2FilterGroup `json:"filters"`
    Operator string               `json:"operator"`
    Property string               `json:"property"`
}

// Query2Filter describes the filtering of a query
type Query2Filter struct {
    Operator string      `json:"operator"`
    ID       string      `json:"id"` // not sure it is still used
    Property string      `json:"property"`
    Type     string      `json:"type"` // not sure it is still used
    Value    interface{} `json:"value"` // not typed properly
}

Update the Query type

// query_collection.go

// Query describes a query
type Query struct {
    Aggregate  []*AggregateQuery `json:"aggregate"`
    GroupBy    interface{}       `json:"group_by"`
    CalendarBy interface{}       `json:"calendar_by"`

    FilterOperator string             `json:"filter_operator"`
    Filter         *Query2FilterGroup `json:"filter"`
    Sort           []*QuerySort       `json:"sort"`
}

I am not sure if this is a definitive way to handle QueryFilters because the query is now called query2 in CollectionViews

so update the collectionview too

// collection.go

// CollectionView represents a collection view
type CollectionView struct {
    ID          string       `json:"id"`
    Version     int64        `json:"version"`
    Type        string       `json:"type"` // "table"
    Format      *FormatTable `json:"format"`
    Name        string       `json:"name"`
    ParentID    string       `json:"parent_id"`
    ParentTable string       `json:"parent_table"`
    Query       *Query       `json:"query"`
    Query2      *Query       `json:"query2"`
    Alive       bool         `json:"alive"`
    PageSort    []string     `json:"page_sort"`

    // set by us
    RawJSON map[string]interface{} `json:"-"`
}

This changes are working locally even if it is not fully typed.

client := &notionapi.Client{}
client.AuthToken = "token"

collectionID := "collectionID"
collectionViewID := "collectionViewID
preflightResp, err := client.QueryCollection(collectionID, collectionViewID, &notionapi.Query{}, &notionapi.User{})
if err != nil {
    log.Fatalf("QueryCollection() failed with %s\n", err)
}
resp, _ := client.QueryCollection(collectionID, collectionViewID, preflightResp.RecordMap.CollectionViews[collectionViewID].CollectionView.Query2, &notionapi.User{})

I post it here because of the questions

kjk commented 4 years ago

Can you describe in what way current code fails? A test case and a test page?

tattali commented 4 years ago

Hi, After further reading it seems thats older than the group filters feature : https://github.com/jamalex/notion-py/issues/94

the code do not fail but not filter

Current filter

be sure to use the current filter structure

client := &notionapi.Client{}
client.AuthToken = "token"

collectionID := "collectionID"
collectionViewID := "collectionViewID

// I am not 100% sure about this query filter
// query Pages with "ok" as title
currentQuery := &notionapi.Query{
    Filter: []*notionapi.QueryFilter{
        {
            Property:   "title",
            Comparator: "string_contains",
            Value:      "ok",
        },
    },
    FilterOperator: "and",
}

resp, err := client.QueryCollection(collectionID, collectionViewID, currentQuery, &notionapi.User{})
if err != nil {
    log.Fatalf("QueryCollection() failed with %s\n", err)
}

count := 0
for _, record := range resp.RecordMap.Blocks {
    block := record.Block

    // count only Pages
    if len(block.GetProperty("title")) > 0 && block.IsPage() && !block.IsSubPage() && !block.IsLinkToPage() {
        count++
    }
}
log.Print(count)  // output 108 for me, like if there is no filters

New filter

using the new structure to in the query_collection.go so add Query2FilterGroup and Query2Filter and update Query as it is described in the previous post

client := &notionapi.Client{}
client.AuthToken = "token"

collectionID := "collectionID"
collectionViewID := "collectionViewID

// query Pages with "ok" as title
newQuery := &notionapi.Query{
    Filter: &notionapi.Query2FilterGroup{
        Filters: []*notionapi.Query2FilterGroup{
            {
                Filter: &notionapi.Query2Filter{
                    Operator: "string_contains",
                    Value: struct {
                        Type  string `json:"type"`
                        Value string `json:"value"`
                    }{
                        Type:  "exact",
                        Value: "ok",
                    },
                },
                Property: "title",
            },
        },
        Operator: "and",
    },
}

resp, err := client.QueryCollection(collectionID, collectionViewID, newQuery, &notionapi.User{})
if err != nil {
    log.Fatalf("QueryCollection() failed with %s\n", err)
}

count := 0
for _, record := range resp.RecordMap.Blocks {
    block := record.Block

    // count only Pages
    if len(block.GetProperty("title")) > 0 && block.IsPage() && !block.IsSubPage() && !block.IsLinkToPage() {
        count++
    }
}
log.Print(count) // output 1 for me

Thanks