package entutils import ( "entgo.io/ent/dialect/sql" "github.com/openmeterio/openmeter/pkg/slicesx" ) // JSONBIn returns a function that filters the given JSONB field by the given key and value // Caveats: // - PostgreSQL only // - The field must be a JSONB field // - The value must be a string (no support for other types, ->> converts all values to string) // - This might not work if there's a join involved in the query, so add unit tests func JSONBIn(field string, key string, values []string) func(*sql.Selector) { return func(s *sql.Selector) { // This is just a safeguard, it should never happen, but if it's not in place, then if // len(values) == 0, then generated SQL query will be field->>'key' IN (), which is invalid in SQL if len(values) == 0 { s.Where(sql.P(func(b *sql.Builder) { b.WriteString("false") })) return } s.Where(sql.P(func(b *sql.Builder) { b.WriteString("(") b.WriteString(field) b.WriteString("->>'") b.WriteString(key) b.WriteString("' IN (") b.Args(slicesx.Map(values, func(f string) any { return f })...) b.WriteString(")") b.WriteString(")") })) } } // JSONBKeyExistsInObject returns a function that filters the given JSONB field by mandating that a key exists in // a specifc object. // // Example: // given the field value of // // {"failed": false, "immutable": false, "availableActions": {"delete": {"resultingState": "deleted"}, "advance": {"resultingState": "draft.waiting_auto_approval"}}} // // JSONBKeyExistsInObject("status_details_cache", "availableActions", "advance") // // filters for such records that have the advance as an available action. // // Resulting condition: // // status_details_cache -> 'availableActions' ? 'advance' func JSONBKeyExistsInObject(field string, member string, expectedKey string) func(*sql.Selector) { return func(s *sql.Selector) { s.Where(sql.P(func(b *sql.Builder) { b.WriteString("(") b.WriteString(field) b.WriteString("->'") b.WriteString(member) b.WriteString("' ? '") b.WriteString(expectedKey) b.WriteString("')") })) } }