Skip to main content
Um segmento é uma resposta salva para “quem são essas pessoas”. Flags apontam para um, checklists são mostradas para um, posts de changelog são endereçados a um, pesquisas perguntam a um. Esse reuso é o ponto inteiro: um módulo que criasse o próprio filtro seria um segundo vocabulário para audiência, e os dois discordariam na primeira vez que um campo fosse adicionado a um deles.

O formato

Uma conjunção de grupos; dentro de um grupo, uma disjunção de condições.
(A OU B) E C, e dois níveis são a gramática inteira. Aninhamento arbitrário é uma questão de precedência, um parser e um formulário que ninguém consegue desenhar; qualquer-um E qualquer-um é o que um construtor de audiência de fato oferece, e este diz em voz alta o que não consegue expressar em vez de suportar pela metade. No máximo 8 grupos de 12 condições. Isso é uma audiência grande e uma query que o Postgres planeja sem notar — o limite existe para que uma definição não possa ficar arbitrariamente cara de colar. Um grupo vazio é recusado, nunca lido como “sempre verdadeiro”: um construtor que perdesse a última condição viraria silenciosamente “todo mundo”.

Quatro fontes

Quais operadores se aplicam é decidido pelo campo, e uma incompatibilidade é recusada: created_at contains "@" não é uma query com resposta vazia, é uma pergunta que ninguém quis fazer. Campos de texto aceitam eq/neq/contains/ not_contains/starts_with/ends_with/in/not_in/is_set/is_not_set; booleanos aceitam is_true/is_false; timestamps aceitam before/after/ within_days/not_within_days; eventos aceitam occurred/not_occurred; métricas aceitam at_least/at_most. Um operando que a condição não lê é recusado em vez de ignorado. Um days numa condição onde dias não significam nada seria uma janela que você definiu, o motor descartou, e você acredita ter. A janela de uma condição de evento é obrigatória (1 a 730 dias). Não existe occurred sem limite, porque um segmento que varre todo evento já armazenado fica mais lento a cada mês em que é deixado em paz. Se você quer dizer “alguma vez”, diga dois anos e assuma isso. Um timestamp não preenchido não casa com nenhum dos quatro operadores de tempo — not_within_days inclusive. Um valor ausente não responde pergunta nenhuma sobre si mesmo; alcance esses contatos com is_not_set.

Filtrando por uma propriedade do evento

Uma condição de evento pode olhar uma chave do que o track() mandou:
Sem isso o caminho que sobra é codificar a dimensão no nome — checkout.completed.pro ao lado de checkout.completed.free — o que transforma o namespace num produto cartesiano e leva junto o agrupamento em que o explorer é construído. Quatro regras, e cada uma existe por um motivo que aparece depois:
  • Só em occurred. “Não aconteceu com plan=pro” tem duas leituras — nunca aconteceu, ou aconteceu com outro plano — e o motor recusa em vez de escolher. Para a primeira, use not_occurred no evento inteiro.
  • Uma chave por condição. Duas seriam um E, e um grupo de condições já é como este motor escreve E.
  • A comparação é textual. Uma propriedade é comparada como o texto que ela vira: 4 é "4", true é "true". A coluna não tem schema, e qualquer outra leitura precisaria de um tipo declarado por chave. Os operadores são os de texto, menos in/not_in — que sobre uma propriedade seriam um OU, e um OU é uma segunda condição no mesmo grupo.
  • Ausente e null são o mesmo estado que vazio. Então is_not_set é como você pede os eventos que não carregam a chave, e neq não casa com eles por acidente.
O count continua valendo junto: “fez checkout com plan = pro ao menos 2 vezes” é uma condição só. Uma propriedade de evento não é um atributo do contato. Se você escrever properties.plan como attribute, a recusa aponta para cá.

O que ele recusa, e por que recusar é melhor que aproximar

Duas condições são perfeitamente razoáveis de querer, e este motor não vai formulá-las. As duas se nomeiam no erro, então você encontra uma frase em vez de uma audiência vazia. Um atributo customizado do contato. attributes.plan_tier não tem nada por trás — as colunas do contato são o perfil e a atribuição de primeiro toque, e não há armazém de atributos contra o qual casar. O único jeito de honrar isso seria casar algo quase certo. Um segmento construído sobre uma aproximação não é um erro menor que uma mensagem de erro: é uma audiência que recebe e-mail, ou uma funcionalidade que sai para o décimo errado dos seus usuários, e ninguém descobre isso lendo a definição. Um entitlement. Se um cliente detém uma funcionalidade é resolvido pelo motor de entitlements — overrides que substituem em vez de maximizar, uma expiração, uma carência de dunning, e um vocabulário de status cujo membro desconhecido responde honestamente “não dá para dizer”. Reescrever isso em SQL seria uma segunda cópia da única função que é mantida sozinha de propósito, e a cópia estaria errada justamente nos casos que mais importam. O que um segmento pode mirar é o plano, que é uma linha: plan.key e plan.subscription_status são espelhos do seu gateway, não resoluções de coisa alguma. Uma terceira recusa é mais estreita e vale saber: uma condição metric num ambiente test. O medidor de atividade nunca registra test, então a condição só poderia casar com ninguém — e uma audiência vazia por razão estrutural é idêntica a uma audiência vazia porque ainda não aconteceu nada.

Dois avaliadores, um significado

A mesma definição é compilada duas vezes: em SQL sobre os contatos de um ambiente (“quem está aqui”), e numa avaliação em processo de um contato (“essa pessoa está aqui, agora”). Uma campanha resolvendo destinatários quer o primeiro; uma flag decidindo o que mandar para um navegador no boot, ou uma pesquisa perguntando se alguém acabou de entrar, quer o segundo. Duas implementações de um significado é exatamente o formato que se descola, então cada campo carrega as duas metades num só lugar e um teste conduz contatos reais pelos dois caminhos e recusa uma discordância. Vale saber que isso existe, porque é a razão de você poder confiar no preview: a contagem que você viu é produzida pelas mesmas regras que a flag vai aplicar um carregamento de página depois.

Pertencimento, entrada e saída

Quem está num segmento é escrito, não recalculado a cada leitura — porque o que os módulos acima de fato assinam é a entrada. “Alguém entrou em trial-acaba-em-3-dias” é uma diferença entre duas avaliações, e uma diferença não tem onde morar a menos que a avaliação anterior tenha sido registrada. Duas coisas mantêm isso vivo:
  • Um fato chega e os contatos sobre os quais ele fala são re-decididos em segundos. É o caminho que carrega tudo causado por alguém ter feito algo.
  • Uma varredura noturna percorre cada definição viva e recalcula. É a única coisa capaz de notar uma condição que virou verdadeira porque o tempo passou — “criado há mais de 30 dias”, “não visto há três semanas” viram verdade sem nada ser publicado em lugar nenhum, porque nada aconteceu.
Entrar publica segment.entered e sair publica segment.left. Os dois são entregáveis como webhook, e o payload leva os ids e o nome do segmento — nunca a definição. O primeiro cálculo de uma definição não anuncia nada. Escrever uma audiência pela primeira vez é um backfill: ninguém mudou, a audiência mudou, e um webhook por contato existente por um ato que você fez no painel três segundos atrás não é um fato sobre ninguém. Editar a definição devolve o segmento a esse estado, pelo mesmo motivo — re-mirar uma audiência não é cem pessoas entrando nela. definition_version sobe quando a definição muda e nunca num rename, então qualquer coisa segurando uma audiência compilada descobre que está velha comparando um inteiro. left_at é um timestamp e não uma exclusão, porque sair também é algo sobre o qual você vai querer agir: uma audiência de reconquista é gente que saiu de “ativo nos últimos 30 dias”.

Faça o preview antes de mirar qualquer coisa

A contagem e no máximo 25 contatos, do mais novo para o mais velho. Não escreve nada e custa uma query. O hábito que vale criar é dar preview antes de uma flag, uma pesquisa ou um post de changelog nomear o segmento — todo módulo a jusante herda o que essa definição significa, e o lugar mais barato de descobrir que ela significa outra coisa é aqui.

Permissões

Ler um segmento é customers:read — o mesmo assento que lista contatos, porque um preview é ler contatos. Criar, editar e arquivar é segments:manage, que owner e admin têm por padrão. A separação é deliberada. Uma audiência é configuração de produto que todo outro módulo herda; poder olhar para uma não é o mesmo ato que poder mover aquilo para onde uma flag, uma pesquisa e uma nota de versão apontam ao mesmo tempo.