Para construir um índice invertido em Elixir, associe cada termo aos documentos em que aparece e guarde, para cada documento, a frequência desse termo. Depois, use essa estrutura para encontrar candidatos e ordená-los com TF-IDF. Neste tutorial, a convenção de pontuação será tf × log(N / df): simples de calcular e explicar, mas apenas uma das variantes possíveis.
O que o índice invertido armazena
Uma coleção de documentos costuma ser vista como documentos que contêm termos. O índice invertido muda a direção: para cada termo, guarda os documentos que o contêm. A documentação do Elasticsearch resume: “An inverted index is a data structure that maps each token to the documents that contain it.” (documentação de índice invertido).
Para busca booleana simples, cada termo poderia apontar apenas para uma lista de IDs. Para classificar resultados, é útil guardar também a frequência do termo em cada documento. Índices podem ainda armazenar posições, necessárias para recursos como busca de frases. Esses metadados não são a mesma coisa: a frequência do termo, ou tf, conta ocorrências dentro de um documento; a frequência documental, ou df, conta quantos documentos do corpus contêm o termo.
Defina uma coleção e a normalização
Comece com documentos identificados de forma única. Para um exemplo pequeno, uma lista em memória é suficiente:
#1 Best Overall
documents = [
%{id: 1, text: "Elixir constrói ferramentas de busca"},
%{id: 2, text: "Busca textual usa termos e índices"},
%{id: 3, text: "Elixir usa índices invertidos"}
]
A tokenização precisa ser igual na indexação e na consulta. Esta versão didática converte texto para minúsculas e separa por sequências que não sejam letras ou números, preservando letras Unicode e algarismos:
def tokenize(text) do
text
|> String.downcase()
|> String.split(~r/[^p{L}p{N}]+/u, trim: true)
end
Essa regra é uma simplificação, não um analisador linguístico de português. Ela não remove palavras comuns, não reduz flexões à mesma raiz e não decide como tratar equivalências de acentos. Hífens são separadores; stemming, stop words, normalização Unicode e outras convenções podem exigir regras próprias. Se adaptar a regex ou a normalização, use exatamente a mesma versão na consulta.
Construa postings com frequências
Use um mapa com o formato %{termo => %{id_documento => frequência}}. O mapa interno é a posting list do termo: cada ID aparece no máximo uma vez por termo, com sua frequência como valor. Assim, contar quantos documentos contêm um termo é tão simples quanto obter o tamanho desse mapa.
def index_document(%{id: id, text: text}, index) do
text
|> tokenize()
|> Enum.frequencies()
|> Enum.reduce(index, fn {term, tf}, acc ->
postings = Map.get(acc, term, %{})
Map.put(acc, term, Map.put(postings, id, tf))
end)
end
def build_index(documents) do
Enum.reduce(documents, %{}, fn document, index ->
index_document(document, index)
end)
end
Enum.frequencies/1 transforma os tokens de um documento em pares de termo e frequência; a redução externa combina esses pares no índice. A documentação do Apache Lucene descreve a relação entre termos e documentos e as estatísticas armazenadas para busca baseada em termos (Index File Formats, Lucene 3.0.3). Essa página é antiga: serve aqui como referência conceitual, não como especificação do formato atual do Lucene.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →IDs devem ser únicos. Se a coleção contiver dois documentos com o mesmo ID, o segundo substituirá a frequência do primeiro naquele termo; valide a entrada se esse caso for possível. Uma coleção vazia produz %{}, sem postings.
Use o índice para recuperar candidatos
Recuperação e classificação são etapas distintas. Primeiro determine quais documentos podem ser resultado; depois some os pesos dos termos da consulta para ordenar esses candidatos. A escolha entre OR e AND muda o conjunto recuperado, não apenas sua ordem.
Rank #3
Consulta OR: qualquer termo pode corresponder
Na consulta OR, inclua um documento se ele aparecer nas postings de pelo menos um termo. Ao percorrer os termos normalizados da consulta, some a contribuição de cada termo encontrado:
def score_or(index, query, document_count) do
query
|> tokenize()
|> Enum.reduce(%{}, fn term, scores ->
postings = Map.get(index, term, %{})
df = map_size(postings)
if df == 0 do
scores
else
idf = :math.log(document_count / df)
Enum.reduce(postings, scores, fn {id, tf}, acc ->
Map.update(acc, id, tf * idf, &(&1 + tf * idf))
end)
end
end)
|> Enum.sort_by(fn {_id, score} -> score end, :desc)
end
Query vazia, texto que só gera separadores ou termos desconhecidos resulta em uma lista vazia. O teste df == 0 evita uma divisão por zero: um termo sem posting não tem documentos candidatos e não contribui para a pontuação. Para construir uma função de consulta reutilizável, passe também a regra OR/AND explicitamente, em vez de deixar a semântica implícita.
Consulta AND: todos os termos precisam corresponder
Para AND, intersecte os conjuntos de IDs das postings dos termos normalizados antes de pontuar. Se qualquer termo não tiver posting, a interseção é vazia. Depois da interseção, some as contribuições TF-IDF dos termos da consulta somente para os IDs restantes. Essa separação torna explícito que um documento pode ter uma pontuação alta para um termo, mas ainda assim ser excluído por não conter outro termo obrigatório.
Escolha e interprete a fórmula TF-IDF
Neste exemplo, a contribuição de um termo t para um documento d é:
tf(t,d) × log(N / df(t))
tf(t,d)é a frequência armazenada na posting daquele documento.Né a quantidade de documentos no corpus indexado.df(t)é o número de documentos com posting para o termo.logé o logaritmo natural usado por:math.log/1no exemplo.
Un documento que repite el término aumenta su contribución, mientras que un término presente en muchos documentos recibe menos peso. Si el término aparece en todos los documentos, log(N / df) = 0 y no distingue documentos en esta variante. En una consulta con varios términos, la función suma las contribuciones; la magnitud final depende de la normalización elegida para tf y de si se normaliza por longitud del documento.
Esta es una convención didáctica, no la única fórmula de TF-IDF ni una afirmación sobre el cálculo actual de Lucene. La API TFIDFSimilarity de Apache Lucene 7.2.0 documenta una variante con frecuencia transformada por raíz cuadrada, IDF suavizado basado en docCount y docFreq, y un factor de normalización por longitud (API TFIDFSimilarity 7.2.0). Las convenciones y versiones importan: no traslade esa fórmula concreta a este ejemplo sin cambiar y explicar el cálculo.
Recommended Free Tools
Best Value
Compruebe la puntuación con cálculos manuales
Con los tres documentos de ejemplo, las postings relevantes son:
elixir: documentos 1 y 3;df = 2.busca: documentos 1 y 2;df = 2.índices: documentos 2 y 3;df = 2.usa: documentos 2 y 3;df = 2.
Para esta colección, cada uno de esos términos tiene peso IDF log(3 / 2). La consulta Elixir normaliza al término elixir y devuelve los documentos 1 y 3; como tf = 1 en ambos, empatan. La consulta busca elixir bajo OR devuelve los documentos 1, 2 y 3. El documento 1 contiene ambos términos y suma dos contribuciones; los documentos 2 y 3 contienen uno cada uno y reciben una. Bajo AND, solo el documento 1 contiene ambos términos.
Enum, Stream y el tamaño de la colección
Para unos pocos documentos en memoria, Enum mantiene el recorrido directo: consume enumerables y produce resultados de forma inmediata. Stream compone transformaciones perezosas y puede evitar materializar etapas intermedias si el origen es grande. La documentación oficial de Elixir explica ambos comportamientos y el uso de streams con recursos (módulo Enum y funciones relacionadas).
La pereza no convierte por sí sola este mapa en un índice apto para colecciones ilimitadas: el índice final sigue ocupando memoria. Para documentos leídos de archivos, elija APIs que cierren el recurso correctamente, incluso si el procesamiento se interrumpe. También tendrá que decidir dónde persistir el índice, cómo actualizar postings y qué hacer con eliminaciones; esas decisiones quedan fuera del ejemplo en memoria.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteDónde encaja frente a BM25
TF-IDF es una buena forma de aprender la relación entre frecuencia local y rareza en el corpus, pero no debe presentarse como el patrón predeterminado de producción. La documentación de Elasticsearch indica que BM25 es su similitud por defecto y lo describe como una variación de TF-IDF (configuración de similitud de Elasticsearch). El comportamiento efectivo puede depender de la versión y de la configuración del índice.
| Aspecto | TF-IDF del ejemplo | BM25 según la documentación de Elasticsearch |
|---|---|---|
| Frecuencia del término | La contribución crece linealmente con el tf sin transformación. |
La frecuencia se satura: repetir un término sigue influyendo, pero cada repetición adicional aporta menos. |
| Longitud del documento | La fórmula elegida no normaliza por longitud. | Incluye normalización por longitud, con ajustes configurables. |
| Uso por defecto | Convención pedagógica de este tutorial; no se afirma como valor por defecto de un motor. | La documentación actual de Elasticsearch lo identifica como predeterminado; una configuración o versión concreta puede diferir. |
El siguiente paso, si se necesita un buscador real, no es solo cambiar una fórmula: hacen falta decisiones sobre analizadores de texto, persistencia, actualizaciones, recuperación OR/AND, relevancia y configuración de la plataforma de búsqueda.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




