MeliGraph
Motor de Recomendação baseado em Grafos para Elixir.
Inspirado nos sistemas WTF (Who to Follow) e GraphJet do Twitter, e nos padrões OTP do Oban.
Features
- Grafo em memória com segmentação temporal (GraphJet-style)
- PageRank Personalizado via Monte Carlo random walks
- SALSA para grafos bipartidos (hubs/authorities)
- SimilarItems — co-ocorrência 2-hop com normalização Jaccard/Cosine
- GlobalRank — ranking global por in-degree para cold start
- Single-writer / multi-reader via GenServer + ETS
- Múltiplas instâncias isoladas via Registry
- Telemetry-first — todas as operações emitem eventos
- Modo de testing
:sync— testes determinísticos sem processos async - Plugin system — Pruner, CacheCleaner extensíveis
Instalação
def deps do
[
{:meli_graph, "~> 0.1.0"}
]
endQuick Start
# Iniciar uma instância
{:ok, _} = MeliGraph.start_link(
name: :my_graph,
graph_type: :bipartite,
testing: :sync
)
# Inserir arestas (usuário → conteúdo)
MeliGraph.insert_edge(:my_graph, "user:1", "post:a", :like)
MeliGraph.insert_edge(:my_graph, "user:2", "post:a", :like)
MeliGraph.insert_edge(:my_graph, "user:2", "post:b", :like)
# Recomendações
{:ok, recs} = MeliGraph.recommend(:my_graph, "user:1", :content,
algorithm: :salsa, seed_size: 10, top_k: 5)
# Explorar o grafo
MeliGraph.neighbors(:my_graph, "user:1", :outgoing)
# => ["post:a"]
MeliGraph.edge_count(:my_graph)
# => 3Múltiplas Instâncias
# Em uma Application supervision tree
children = [
{MeliGraph, name: :follows, graph_type: :directed},
{MeliGraph, name: :interactions, graph_type: :bipartite}
]
Supervisor.start_link(children, strategy: :one_for_one)
# Cada instância é isolada
MeliGraph.insert_edge(:follows, "user:1", "user:2", :follow)
MeliGraph.insert_edge(:interactions, "user:1", "post:a", :like)Algoritmos
| Algoritmo | Tipo de Grafo | Caso de Uso |
|---|---|---|
| PageRank | Dirigido | "Who to Follow", Circle of Trust |
| SALSA | Bipartido | "Posts para você", recomendação de conteúdo |
| SimilarItems | Bipartido | "Professores similares a X", itens co-consumidos |
| GlobalRank | Qualquer | Top itens para anônimos, cold start |
Algoritmos customizados podem ser adicionados implementando o behaviour MeliGraph.Algorithm.
Configuração
MeliGraph.start_link(
name: :my_graph, # obrigatório
graph_type: :bipartite, # obrigatório (:directed | :bipartite)
segment_max_edges: 1_000_000, # arestas por segmento
segment_ttl: :timer.hours(24), # TTL dos segmentos
result_ttl: :timer.minutes(30), # TTL do cache
testing: :disabled, # :disabled | :sync
plugins: [
{MeliGraph.Plugins.Pruner, interval: :timer.minutes(5)},
{MeliGraph.Plugins.CacheCleaner, interval: :timer.minutes(1)}
]
)Telemetry
Eventos emitidos para observabilidade:
[:meli_graph, :ingestion, :insert_edge, :start | :stop | :exception]
[:meli_graph, :query, :recommend, :start | :stop | :exception]
[:meli_graph, :graph, :create_segment, :start | :stop | :exception]
[:meli_graph, :plugin, :prune, :start | :stop | :exception]
[:meli_graph, :plugin, :cache_clean, :start | :stop | :exception]Testes
# Testes unitários (sem dependências externas)
mix test94 tests, 0 failuresVeja docs/testing.md para testes de integração com dados reais.
Documentação Técnica
- Arquitetura — Camadas, supervision tree e fluxo de dados
- Graph Storage — Segmentação temporal, ETS e ID mapping
- Estruturas de Dados — Cada estrutura usada, justificativa e trade-offs
- Algoritmos — PageRank, SALSA, extensibilidade
- Padrões OTP — Config struct, Registry, plugins, telemetry
- Testing — Modo
:sync, helpers, exemplos de testes - API Reference — Referência completa da API pública
Estrutura do Projeto
lib/
├── meli_graph.ex # API pública
├── meli_graph/
│ ├── config.ex # Config struct centralizado
│ ├── config_holder.ex # Registra config no Registry
│ ├── registry.ex # Registry helpers
│ ├── supervisor.ex # Supervision tree
│ ├── telemetry.ex # Telemetry spans
│ ├── graph/
│ │ ├── edge.ex # Struct de aresta
│ │ ├── id_map.ex # Mapeamento de IDs
│ │ ├── segment.ex # Segmento temporal
│ │ └── segment_manager.ex # Gerenciador de segmentos
│ ├── ingestion/
│ │ └── writer.ex # Single-writer + graceful shutdown
│ ├── algorithm/
│ │ ├── algorithm.ex # Behaviour genérico
│ │ ├── pagerank.ex # Monte Carlo random walks
│ │ ├── salsa.ex # Subgraph SALSA
│ │ ├── similar_items.ex # Co-ocorrência 2-hop (Jaccard/Cosine)
│ │ └── global_rank.ex # Ranking global por in-degree
│ ├── query/
│ │ └── query.ex # Cache-first, respeita testing mode
│ ├── store/
│ │ ├── store.ex # Store behaviour
│ │ └── ets.ex # ETS + TTL
│ └── plugins/
│ ├── plugin.ex # Plugin behaviour
│ ├── pruner.ex # Remove segmentos expirados
│ ├── cache_cleaner.ex # TTL cleanup
│ └── supervisor.ex # Supervisor dos plugins
test/
├── meli_graph_test.exs # Testes de integração da API pública
├── meli_graph/ # Testes unitários por módulo
│ ├── config_test.exs
│ ├── registry_test.exs
│ ├── telemetry_test.exs
│ ├── graph/
│ ├── ingestion/
│ ├── algorithm/
│ ├── query/
│ ├── store/
│ └── plugins/
├── integration/ # Testes com dados reais (tag :integration)
│ ├── dataset_stats_test.exs # Valida integridade dos CSVs
│ ├── follows_graph_test.exs # Who to Follow com dados reais
│ ├── likes_graph_test.exs # Feed de recomendações com dados reais
│ └── professors_graph_test.exs # Recomendação de professores (SALSA, SimilarItems, GlobalRank)
└── support/
├── graph_helpers.ex # Helpers para testes unitários
└── dataset_loader.ex # Carrega CSVs de produção
tmp/
├── meli_graph_follows.csv # Exportado do banco (não versionado)
├── meli_graph_likes.csv
├── meli_graph_posts.csv
├── meli_graph_professors.csv # Metadados dos professores
├── meli_graph_professor_ratings.csv # Avaliações profile → professor
└── meli_graph_professor_posts.csv # Posts sobre professoresRoadmap
v0.1 (atual)
- Config struct + Registry + Supervisor
- Graph storage com ETS + segmentação temporal
- ID mapping global
- Single-writer ingestion + graceful shutdown
- PageRank Personalizado (Monte Carlo)
- SALSA (Subgraph)
- SimilarItems (co-ocorrência 2-hop com Jaccard/Cosine)
- GlobalRank (ranking global por in-degree)
- Store ETS com TTL
- Query layer (sync + cache) com suporte a algoritmos globais
- Plugin system (Pruner + CacheCleaner)
- Telemetry spans
-
Modo de testing
:sync - 94 testes unitários + 37 testes de integração com dados reais
v0.2 (planejado)
-
Pesos nas arestas (
:avalioupesar mais que:postou) - PageRank via Nx power method
- Sonar (health check do Writer)
- Precomputer plugin
- Backpressure no Writer
v0.3 (futuro)
- Peer behaviour + distribuição
- Pesos nas arestas
- Algoritmos adicionais (Node2Vec)
Referências
- Gupta et al., "WTF: The Who to Follow Service at Twitter", WWW 2013
- Sharma et al., "GraphJet: Real-Time Content Recommendations at Twitter", VLDB 2016
- Lempel & Moran, "SALSA: The Stochastic Approach for Link-Structure Analysis", ACM TOIS 2001
- Fogaras et al., "Towards Scaling Fully Personalized PageRank", Internet Mathematics 2005
- Page et al., "The PageRank Citation Ranking", Stanford 1999
Licença
Veja LICENSE.