Relações
Duas consultas para cem registros — e por que a relação não carrega sozinha.
O problema do N+1#
Buscar cem usuários e ler usuario.pedidos de cada um faz cento e uma consultas. Isso não aparece em desenvolvimento, com três linhas na tabela, e derruba a produção com dez mil.
É o bug de ORM mais comum que existe. No Forge, a relação não carrega sozinha ao ser lida — ou você pede, ou ela não vem:
dataforge
adopt Forge
db := Forge.conectar(":memory:")
Usuario := Forge.modelo("Usuario", {
"id": {"tipo": "Serial"},
"nome": {"tipo": "Texto", "obrigatorio": yes}
}, {"conexao": db})
Pedido := Forge.modelo("Pedido", {
"id": {"tipo": "Serial"},
"usuario_id": {"tipo": "Inteiro", "indice": yes},
"total": {"tipo": "Real", "padrao": 0}
}, {"conexao": db})
Usuario.tem_muitos("pedidos", "Pedido")
Pedido.pertence_a("usuario", "Usuario")
Forge.migrar_tudo(db)
ana := Usuario.criar({"nome": "Ana"})
bia := Usuario.criar({"nome": "Bia"})
Pedido.criar({"usuario_id": ana["id"], "total": 100.0})
Pedido.criar({"usuario_id": ana["id"], "total": 50.0})
Pedido.criar({"usuario_id": bia["id"], "total": 20.0})
// DUAS consultas, para qualquer quantidade de usuários
com_pedidos := Usuario.com(Usuario.todos(), "pedidos")
assert len(com_pedidos[0]["pedidos"]) is 2
assert len(com_pedidos[1]["pedidos"]) is 1Os quatro tipos#
dataforge
// um usuário tem muitos pedidos
Usuario.tem_muitos("pedidos", "Pedido")
// um pedido pertence a um usuário
Pedido.pertence_a("usuario", "Usuario")
// um usuário tem um perfil
Usuario.tem_um("perfil", "Perfil")
// um post tem muitas etiquetas, e uma etiqueta muitos posts
Post.muitos_para_muitos("etiquetas", "Etiqueta")| Relação | A chave fica em | Padrão do nome |
|---|---|---|
tem_muitos | na tabela do outro | <modelo>_id |
tem_um | na tabela do outro | <modelo>_id |
pertence_a | nesta tabela | <nome>_id |
muitos_para_muitos | numa tabela ponte | as duas chaves |
Do outro lado#
dataforge
adopt Forge
db := Forge.conectar(":memory:")
U := Forge.modelo("Usuario", {
"id": {"tipo": "Serial"}, "nome": {"tipo": "Texto"}
}, {"conexao": db})
P := Forge.modelo("Pedido", {
"id": {"tipo": "Serial"}, "usuario_id": {"tipo": "Inteiro"},
"total": {"tipo": "Real", "padrao": 0}
}, {"conexao": db})
P.pertence_a("usuario", "Usuario")
Forge.migrar_tudo(db)
ana := U.criar({"nome": "Ana"})
P.criar({"usuario_id": ana["id"], "total": 10.0})
com_dono := P.com(P.todos(), "usuario")
assert com_dono[0]["usuario"]["nome"] is "Ana"Relação desconhecida avisa#
dataforge
adopt Forge
db := Forge.conectar(":memory:")
U := Forge.modelo("Usuario", {"id": {"tipo": "Serial"}}, {"conexao": db})
U.tem_muitos("pedidos", "Pedido")
Forge.migrar_tudo(db)
monitor:
U.com(U.todos(), "inventada")
handle SchemaError as e:
assert "inventada" in e.messageA mensagem lista as relações que existem — é mais útil que "não encontrada".