When we added support for complex filtering in Buttondown, I spent a long time trying to come up with a schema for filters that felt sufficiently ergonomic and future-proof. I had a few constraints, all of which were reasonable:

  1. It needed to be JSON-serializable, and trivially parsable by both the front-end and back-end.
  2. It needed to be arbitrarily extendible across a number of domains (you could filter subscribers, but also you might want to filter emails or other models.)
  3. It needed to be able to handle both and and or logic (folks tagged foo and bar as well as folded tagged foo or bar).
  4. It needed to handle nested logic (folks tagged foo and folks tagged bar or baz.)

The solution I landed upon is not, I’m sure, a novel one, but googling “recursive filter schema” was unsuccessful and I am really happy with the result so here it is in case you need something like this:

@dataclass
class FilterGroup:
  filters: list[Filter]
  groups: list[FilterGroup]
  predicate: "and" | "or"
  

@dataclass
class Filter:
  field: str
  operator: "less_than" | "greater_than" | "equals" | "not_equals" | "contains" | "not_contains"
  value: str

And there you have it. Simple, easily serializable/type-safe, can handle everything you throw at it.

For example, a filter for all folks younger than 18 or older than 60 and retired:

FilterGroup(
  predicate="or",
  filters=[
    Field(
      field="age",
      operator="less_than",
      value="18"
    )
  ],
  groups=[
	  FilterGroup(
	    predicate="and",
	    filters=[
		    Field(
			    field="age",
			    operator="greater_than",
			    value="60"
		    ),
		    Field(
			    field="status",
			    operator="equals",
			    value="retired"
		    )
	    ]
	    groups=[],
	  )
  ]
)

Lightning bolt
About the author

I'm Justin Duke — a software engineer, writer, and founder. I currently work as the CEO of Buttondown, the best way to start and grow your newsletter, and as a partner at Third South Capital.

Lightning bolt
Greatest hits

Lightning bolt
Elsewhere

Lightning bolt
Don't miss the next essay

Get a monthly roundup of everything I've written: no ads, no nonsense.