NOTICIAS DE LA ACADEMIA

Construcción de búsqueda vectorial en .NET con PgVector, la guía del desarrollador

Si estás desarrollando aplicaciones .NET que manejan grandes volúmenes de datos, documentos, catálogos de productos, registros de clientes o archivos escaneados, la búsqueda siempre es un desafío. La búsqueda por palabras clave pierde el contexto. La búsqueda de texto completo tiene sus limitaciones. Pero la búsqueda por vectores cambia completamente la ecuación.

Recientemente nos encontramos con una excelente guía de Milan Jovanović, una de las voces más respetadas en la comunidad .NET y un MVP de Microsoft, que explica exactamente cómo implementar la búsqueda por vectores en .NET usando PgVector, una extensión de PostgreSQL que lleva las capacidades de búsqueda semántica directamente a tu base de datos existente.

¿Qué es la búsqueda por vectores y cómo funciona?

La búsqueda tradicional hace coincidir palabras o frases exactas. Si un usuario busca "factura pendiente", una búsqueda por palabras clave solo encontrará documentos que contengan esas palabras exactas. No revelará un documento que diga "pago pendiente" o "saldo debido", aunque signifiquen lo mismo.

La búsqueda por vectores funciona de manera diferente. En lugar de hacer coincidir palabras, hace coincidir significados.

Así es como funciona el pipeline en la práctica:

Primero, el texto se convierte en una representación numérica llamada embedding, un array de alta dimensión de números flotantes que captura el significado semántico del contenido. Por ejemplo, las frases "factura pendiente" y "pago pendiente" producirían embeddings que están matemáticamente cerca en el espacio vectorial, aunque no compartan palabras comunes.

Estos embeddings son generados por un modelo de aprendizaje automático, típicamente a través de una API como los modelos de embeddings de texto de OpenAI y se almacenan junto con tus datos en la base de datos.

Imagen 1

Cuando un usuario ejecuta una consulta de búsqueda, esa consulta también se convierte en un embedding utilizando el mismo modelo. La base de datos luego calcula la distancia entre el embedding de la consulta y cada embedding almacenado, devolviendo los resultados que están más cerca en el espacio vectorial, es decir, los más similares semánticamente, no solo por coincidencia de palabras clave.

PgVector permite esto directamente dentro de PostgreSQL, soportando búsquedas de similitud eficientes justo al lado de tus datos relacionales, sin requerir una base de datos vectorial dedicada.

Inicializando la Base de Datos

Antes de almacenar vectores, habilita la extensión PgVector y configura la tabla.


var builder = DistributedApplication.CreateBuilder(args);

var ollama = builder.AddOllama("ollama")
    .WithLifetime(ContainerLifetime.Persistent)
    .WithDataVolume()
    .WithGPUSupport();

var embeddingModel = ollama.AddModel("qwen3-embedding:0.6b");

var postgres = builder.AddPostgres("postgres", port: 6432)
    .WithLifetime(ContainerLifetime.Persistent)
    .WithDataVolume()
    .WithImage("pgvector/pgvector", "pg17")
    .AddDatabase("articles");

builder.AddProject<Projects.PgVector_Articles>("pgvector-articles")
    .WithReference(embeddingModel)
    .WithReference(postgres)
    .WaitFor(embeddingModel)
    .WaitFor(postgres);

builder.Build().Run();

var builder = DistributedApplication.CreateBuilder(args);

var ollama = builder.AddOllama("ollama")
    .WithLifetime(ContainerLifetime.Persistent)
    .WithDataVolume()
    .WithGPUSupport();

var embeddingModel = ollama.AddModel("qwen3-embedding:0.6b");

var postgres = builder.AddPostgres("postgres", port: 6432)
    .WithLifetime(ContainerLifetime.Persistent)
    .WithDataVolume()
    .WithImage("pgvector/pgvector", "pg17")
    .AddDatabase("articles");

builder.AddProject<Projects.PgVector_Articles>("pgvector-articles")
    .WithReference(embeddingModel)
    .WithReference(postgres)
    .WaitFor(embeddingModel)
    .WaitFor(postgres);

builder.Build().Run();

Si no estás usando Aspire, puedes ejecutar la misma imagen pgvector/pgvector:pg17 a través de docker-compose y señalarla con una cadena de conexión regular.

Esta sección se basa en el artículo original de Milan Jovanović. Ejemplos de código completo y detalles de implementación están disponibles allí.

Por qué esto es importante para los clientes de Iron Software

Muchos de nuestros clientes usan IronPDF, IronOCR, y IronBarcode para procesar grandes volúmenes de documentos; facturas, informes, registros escaneados, etiquetas de envío.

Un flujo de trabajo práctico que combine las bibliotecas de Iron Software con PgVector podría verse así:

  1. Extract – Usa IronOCR para extraer texto de PDFs escaneados o imágenes
  2. Embed – Envía el texto extraído a un modelo de incrustación para generar representaciones vectoriales
  3. Store – Ahorra incrustaciones junto con los metadatos del documento en PostgreSQL usando Pgvector
  4. Search – Consulta por significado, devolviendo los documentos más relevantes semánticamente en lugar de coincidencias exactas de palabras clave

El resultado es un sistema de búsqueda de documentos más inteligente construido completamente dentro de tu infraestructura existente de .NET y PostgreSQL, sin requerir infraestructura adicional.

Qué cubre la guía de Milan

El artículo de Milan recorre la implementación completa en C#: configuración de la extensión PgVector en PostgreSQL, configuración de Entity Framework Core con Npgsql, generación de embeddings, creación de índices vectoriales para el rendimiento, y ejecución de consultas de similitud. Es práctica, orientada a la producción, y aplicable de inmediato para cualquier desarrollador .NET.

El equipo de desarrollo de Iron Software comparte regularmente recursos .NET, tutoriales e ideas de ingeniería con nuestra comunidad.

¿Construyendo flujos de trabajo con documentos en .NET? Iron Suite tiene todo lo que necesitas.