Use la búsqueda de texto completo de PostgreSQL en Entity Framework

Índice

    Introducción

    La base de datos de PostgreSQL admite la búsqueda avanzada de texto completo (FTS) y la funcionalidad de indexación de texto completo, que se describe detalladamente en la documentación:

    Búsqueda de texto completo

    Hemos decidido satisfacer las necesidades de nuestros usuarios que desean aprovechar la búsqueda de texto completo en Entity Framework y hemos implementado la funcionalidad de búsqueda de texto completo en nuestro proveedor Devart dotConnect para PostgreSQL ADO.NET Entity Framework.
    Este artículo trata sobre lo siguiente:

    Búsqueda de texto completo en LINQ to Entities

    El Funciones de texto de PgSql clase ubicada en el pueblo Devart.Data.PostgreSql.Entity.dll la compilación se usa para generar consultas de búsqueda de texto completo específicas en LINQ to Entities. Tiene una serie de métodos de generación de consultas.

    MétodoDescripciónSQL generado
    CoincidirRealiza una búsqueda de texto completotextoOVector @@ textoOConsulta
    AsTsQueryConvierte texto a tsquery. No normaliza las palabras.'texto' :: tsquery
    comoVectorConvierte texto a vector. No normaliza las palabras.'texto' :: tsvector
    PlainToTsQueryConvierte texto a tsquery. Se puede utilizar para columnas de tabla. Tiene una sobrecarga que le permite establecer la configuración de búsqueda de texto para usar.plainto_tsquery (configuración, texto)
    ToTsQueryConvierte texto a tsquery. Se puede utilizar para columnas de tabla. Tiene una sobrecarga que le permite establecer la configuración de búsqueda de texto para usar. Le permite usar operadores lógicos y (&) o (|) en lugar de (!) en el texto de la consulta y le permite dar peso a los tokens.to_tsquery (configuración, texto)
    ToTsVectorConvierte texto a vector. Se puede utilizar para columnas de tabla. Tiene una sobrecarga que le permite establecer la configuración de búsqueda de texto para usar.to_tsvector (configuración, texto)
    ToTsVectorConvierte texto a vector. Se puede utilizar para columnas de tabla. Tiene una sobrecarga que le permite establecer la configuración de búsqueda de texto para usar.to_tsvector (configuración, texto)
    LongitudDevuelve el número de tokens en un vector dado.longitud (vectorial)
    PreguntaPesoIndica el vector del vector por un peso dado. No hace nada por los floristas desollados.peso (vector, peso)
    ManivelaDevuelve la clasificación de un resultado de búsqueda de texto completo. Tiene varias sobrecargas que le permiten establecer valores de peso adicionales para asignar muestras a palabras en función de sus etiquetas de peso y establecer un tipo adicional de normalización.ts_rank (pesoD, pesoC, pesoB, pesoA, vector, consulta, normalización)
    ЦРанкКдDevuelve la clasificación de un resultado de búsqueda de texto completo. Tiene varias sobrecargas que le permiten establecer valores de peso adicionales para asignar muestras a palabras en función de sus etiquetas de peso y establecer un tipo adicional de normalización. A diferencia de TsRank, calcula la calificación de densidad de cobertura para un documento y un vector de consulta determinados.ts_rank_cd (pesoD, pesoC, pesoB, pesoA, vector, consulta, normalización)
    consulta yCombina dos consultas de búsqueda de texto completo con el operador ANDprimera consulta && segunda consulta
    ConsultaOCombina dos consultas de búsqueda de texto completo con el operador ORprimera consulta || segunda consulta
    QueryNotSolicitud de denegación de devoluciones!! solicitud
    NumNodoDevuelve el número de nodos (tokens más operadores) en tsquery. Devuelve 0 si el texto convertido a tsquery contiene solo palabras vacías.número de nodo (solicitud)
    El árbol de consultasDevuelve la parte de tsquery que puede usar para buscar en el índice.árbol de consulta (consulta)
    TítuloDevuelve fragmentos de texto de búsqueda que contienen términos de búsqueda. Los términos de búsqueda están resaltados. Contiene múltiples sobrecargas que le permiten especificar opciones adicionales de configuración y búsqueda de texto, como el número de fragmento de palabra y la selección.ts_headline (configuración, documento, solicitud, parámetros)
    Volver a escribirBusca el tsquery dado en el tsquery de destino y reemplaza su ocurrencia con un reemplazo para tsquery.ts_rewrite (solicitud, propósito, reemplazo)

    Entonces, si necesita escribir un análogo de la siguiente consulta:

    SELECT *
      FROM MyTable
     WHERE to_tsvector(Column1) @@ plainto_tsquery('english','MyExpression')

    puedes usar:

    var query = ctx.MyTables
      .Where(t => PgSqlTextFunctions.Match(
        PgSqlTextFunctions.ToTsVector(t.Column1),
        PgSqlTextFunctions.PlainToTsQuery("english","MyExpression")
      );

    Índices de texto completo en guiones de código

    Para configurar la migración de Code-First para crear índices de texto completo, archivo PgSqlCreateIndexConfiguration usó la clase que está en Devart.Data.PostgreSql.Entity.Migrations.dll montaje.
    PgSqlCreateIndexConfiguration diseñado para la personalización Crear un índice operación. Esta clase le permite especificar los siguientes parámetros:

    • Vista de índice para crear ( Tipo de índice property): use los valores de PgSqlIndexType.Gin o PgSqlIndexType.Gist para índices de búsqueda de texto completo.
    • O aplique la función to_tsvector a una columna indexada (esto puede no ser necesario si la columna ya tiene un tipo tsvector) - UseToTsVectorFunction propiedad.
    • El nombre de la configuración de búsqueda de texto que se usará para la función to_tsvector es el ToTsVectorConfigName propiedad.
    • Puede crear no solo índices de búsqueda de texto completo basados ​​en columnas, sino también índices basados ​​en expresiones. La expresión se da en la forma Expresión propiedad c PgSqlCreateIndexConfiguration clase.

    En el tutorial a continuación se dan ejemplos de configuraciones.

    revisión de libros de texto

    En este tutorial aprenderá a crear una aplicación que utilizará las siguientes tecnologías:

    Siga el enlace al final de este artículo para descargar la aplicación de muestra completa.
    Aunque en este ejemplo mostramos cómo trabajar con Code-First, las consultas de búsqueda de texto completo se pueden escribir de manera similar para el mapeo XML regular usando modelos EDMX/EDML desarrollados bajo los enfoques Base de datos-First o Model-First. Por lo tanto, también consideraremos el enfoque de base de datos primero en este tutorial.

    requisitos previos

    Preconfigurar la base de datos y el modelo como parte del enfoque de la base de datos

    1. Cree un proyecto de consola C# en Visual Studio 2010 SP1 y asígnele el nombre PostgreSqlFullTextSearch.
    2. En el proyecto, agregue enlaces a las siguientes compilaciones:

    • Devart.Datos;
    • Devart.Data.PostgreSql;
    • Devart.Data.PostgreSql.Entity;
    • Devart.Data.PostgreSql.Entity.Migrations.

    3. Cree un modelo de Entity Framework vacío a través de Entity Developer. En la ventana Explorador de soluciones, abra el menú contextual del proyecto y luego seleccione Agregar -> Nuevo elemento -> Modelo de esencia de Devart. Siga las instrucciones del asistente y especifique una cadena de conexión válida. El archivo del modelo y el código generado se agregarán al proyecto.

    Preconfigure la base de datos y el modelo de acuerdo con el enfoque Code-First

    1. Cree un proyecto de consola C# en Visual Studio 2010 SP1 y asígnele el nombre PostgreSqlFullTextSearch.
    2. En el proyecto, agregue enlaces a las siguientes compilaciones:

    • Entidad.de.datos.del.sistema;
    • Devart.Datos;
    • Devart.Data.PostgreSql;
    • Devart.Data.PostgreSql.Entity;
    • Devart.Data.PostgreSql.Entity.Migrations.

    3. Agregue la última versión pública de EF Code-First: ejecute el proyecto Consola del administrador de paquetes a través del menú Visual Studio Tools -> Library Package Manager -> Package Manager Console, y en la ventana que aparece, ejecute el siguiente comando:

    Install-Package EntityFramework

    Como resultado, la compilación EntityFramework.dll se agrega al proyecto, complementada con entradas de App.config, y se crea el archivo packages.config.
    4. Agregar uno nuevo MiContexto.cs archivo en el proyecto, ponga en él un modelo vacío:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using Devart.Data.PostgreSql;
    
    namespace PostgreSqlFullTextSearch {
    
      public class MyContext: DbContext {
    
        public MyContext() {
        }
    
        public MyContext(PgSqlConnection connection)
          : base(connection, false) {
        }
    
        static MyContext() {
    
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    
        }
    
      }
    }

    5. Ejecute el siguiente comando en Consola del administrador de paquetes:

    Enable-Migrations

    Como resultado, Configuración Clase añadida al proyecto.
    6. Complementar lo existente Configuración diseñador de clases

        public Configuration()
        {
          AutomaticMigrationsEnabled = false;
        }

    especificando el generador de SQL:

        public Configuration()
        {
          AutomaticMigrationsEnabled = false;
    
          SetSqlGenerator(Devart.Data.PostgreSql.Entity.Migrations.PgSqlConnectionInfo.InvariantName,
                  new Devart.Data.PostgreSql.Entity.Migrations.PgSqlEntityMigrationSqlGenerator());
        }

    7. Configurando una cadena de conexión para nuestra clase mi contexto el modelo se ejecutará en el archivo de configuración de la aplicación. Agregar una cadena de conexión a Aplicación.config Archivo:

    <connectionstrings>
      <add name="MyContext" connectionString="User Id=my_user;Password=user_password;Host=my_server;Port=5432;Base de datos=my_database;" providerName="Devart.Data.PostgreSql" />
    </connectionstrings>

    Reemplace los valores de los parámetros de la cadena de conexión con los valores correctos para su entorno.

    Usar índice de texto completo en una columna

    Preconfigurar la base de datos y el modelo como parte del enfoque de la base de datos

    1. Crear Libros puntero de tabla y texto completo a Texto columna:

    CREATE TABLE Books ( 
      Id serial NOT NULL,
      Author varchar(200) NULL,
      Name varchar(300) NULL,
      Content text NULL,
      PRIMARY KEY (Id)
    )
    CREATE INDEX IX_Books_Content ON Books USING GIN(to_tsvector('english', Content))

    2. Usar Actualizar asistente desde la base de datos agregar Libros tabla al modelo EF.

    Preconfigure la base de datos y el modelo de acuerdo con el enfoque Code-First

    1. Agregar Libro.cs archivo de proyecto:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Text;
    
    namespace PostgreSQLFullTextSearch {
    
      public class Book {
    
        //[Key]
        public int Id { get; set; }
    
        [MaxLength(200)]
        public string Author { get; set; }
    
        [MaxLength(300)]
        public string Name { get; set; }
    
        public string Content { get; set; }
    
      }
    }

    Aquí no definimos ningún atributo para IDENTIFICACIÓN propiedad, ya que es una propiedad Int32 con un nombre 'identificación', se asignará automáticamente como una clave principal que se genera automáticamente.
    Para Autor properties establecemos el tipo de datos en VARCHAR (200).
    Para Nombre properties establecemos el tipo de datos en VARCHAR (300).
    Para Contenido propiedad no establecemos ninguna restricción de longitud, por lo que esta propiedad de cadena se asignará a LONGTEXT.
    2. Agregue la siguiente línea a mi contexto clase:

    public DbSet<Book> Books { get; set; }

    3. Para crear una migración, ejecute el siguiente comando Consola del administrador de paquetes:

    Add-Migration AddBook

    Como resultado, se agrega una clase de migración al proyecto con el siguiente contenido:

        public partial class AddBook : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "Books",
                    c => new
                        {
                            Id = c.Int(nullable: false, identity: true),
                            Author = c.String(maxLength: 200),
                            Name = c.String(maxLength: 300),
                            Content = c.String(unicode: false),
                        })
                    .PrimaryKey(t => t.Id);
            }
    
            public override void Down()
            {
                DropTable("Books");
            }
        }

    4. Complete la migración especificando el tipo de sistema de almacenamiento para la tabla y creando un índice de texto completo en Contenido columna, sin especificar el nombre explícito del índice generado:

        using Devart.Data.PostgreSql.Entity;
        using Devart.Data.PostgreSql.Entity.Migrations;
    
        public partial class AddBook : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "Books",
                    c => new
                        {
                            Id = c.Int(nullable: false, identity: true),
                            Author = c.String(maxLength: 200),
                            Name = c.String(maxLength: 300),
                            Content = c.String(unicode: false),
                        },
                    .Index(c => c.Content,
                    anonymousArguments: new PgSqlCreateIndexConfiguration()
                    {
                        IndexType = PgSqlIndexType.Gin,
                        UseToTsVectorFunction = true,
                        ToTsVectorConfigName = "english"
                    })
                   .PrimaryKey(t => t.Id);
            }
    
            public override void Down()
            {
                DropTable("Books");
            }
        }

    5. Actualice la base de datos mediante la migración ejecutando el siguiente comando Consola del administrador de paquetes:

    Update-Base de datos -Verbose

    Como resultado, se ejecutarán comandos para crear objetos modelo.

    CREATE TABLE Books ( 
      Id serial NOT NULL,
      Author varchar(200) NULL,
      Name varchar(300) NULL,
      Content text NULL,
      PRIMARY KEY (Id)
    )
    CREATE INDEX IX_Books_Content ON Books USING GIN(to_tsvector('english', Content))

    También comandos para construir el sistema. Historia de la migración se ejecutará la tabla y se llenarán sus datos de migración.

    Ejemplo de búsqueda de texto completo utilizando lenguaje natural y consultas en modo booleano

    En este tutorial, hemos habilitado PgSqlMonitor para ver las instrucciones DDL y DML ejecutadas en la aplicación Devart dbMonitor. Sin embargo, tenga en cuenta que el uso de PgSqlMonitor es posible para pruebas y depuración, pero debe limitarse en el entorno de producción, ya que la supervisión puede reducir el rendimiento de su aplicación.
    Para usar el monitoreo, agregue este código a su programa

    var monitor = new PgSqlMonitor() { IsActive = true };

    y ejecute dbMonitor.
    Escribamos el código llenando la tabla con datos y generando consultas de búsqueda de texto completo.

                using (var ctx = new PostgreSQLFullTextSearch.MyContext())
                {
    
                    foreach (Book b in ctx.Books)
                        ctx.Books.Remove(b);
                    ctx.SaveChanges();
    
                    // Load information into the Books table
                    ctx.Books.Add(new PostgreSQLFullTextSearch.Book()
                    {
                        Author = "William Shakespeare",
                        Name = "The Tragedy of King Richard the Second",
                        Content = @"ACT 1
    SCENE I. London. A Room in the palace.
    [Enter KING RICHARD, attended; JOHN OF GAUNT, with other NOBLES.]
    KING RICHARD.
      Old John of Gaunt, time-honoured Lancaster,
      Hast thou, according to thy oath and band,
      Brought hither Henry Hereford thy bold son,
      Here to make good the boisterous late appeal,
      Which then our leisure would not let us hear,
      Against the Duke of Norfolk, Thomas Mowbray?
    ..."
                    });
                    ctx.Books.Add(new PostgreSQLFullTextSearch.Book()
                    {
                        Author = "William Shakespeare",
                        Name = "The Life and Death of King John",
                        Content = @"ACT 1.
    SCENE I. Northampton. A Room of State in the Palace.
    [Enter KING JOHN, QUEEN ELINOR, PEMBROKE, ESSEX, SALISBURY, and others, with CHATILLON.]
    KING JOHN.
      Now, say, Chatillon, what would France with us?
    CHATILLON.
      Thus, after greeting, speaks the King of France,
      In my behaviour, to the majesty,
      The borrow'd majesty of England here.
    ..."
                    });
                    ctx.Books.Add(new PostgreSQLFullTextSearch.Book()
                    {
                        Author = "William Shakespeare",
                        Name = "The Life of Henry the Fifth",
                        Content = @"ACT 1.
    SCENE I. London. An ante-chamber in the King's palace.
    [Enter the Archbishop of Canterbury and the Bishop of Ely.]
    CANTERBURY.
      My lord, I'll tell you: that self bill is urg'd,
      Which in the eleventh year of the last king's reign
      Was like, and had indeed against us pass'd,
      But that the scambling and unquiet time
      Did push it out of farther question.
    ..."
                    });
                    ctx.SaveChanges();
    
                    // Write an analogue of the SQL query:
                    //  SELECT author, name FROM books
                    //  WHERE to_tsvector(content) @@ plainto_tsquery('king palace Norfolk')
                    var firstQuery = ctx.Books
                        .Where(b => PgSqlTextFunctions.Match(
                            PgSqlTextFunctions.ToTsVector(b.Content), 
                            PgSqlTextFunctions.PlainToTsQuery("king palace Norfolk"))
                        )
                        .Select(b => new { b.Author, b.Name });
                    var firstQueryResults = firstQuery.ToList();
    
                    // Output:
                    Console.WriteLine();
                    Console.WriteLine("== Querying your single column full-text index ==");
                    Console.WriteLine();
                    Console.WriteLine("1) Natural language");
                    Console.WriteLine();
                    Console.WriteLine("SQL:");
                    Console.WriteLine(firstQuery.ToString());
                    Console.WriteLine();
                    Console.WriteLine("Data:");
                    Console.WriteLine("{0,-5} | {1}", "Author", "Name");
                    Console.WriteLine(new string('-', 50));
                    foreach (var item in firstQueryResults)
                        Console.WriteLine("{0,-5} | {1} ", item.Author, item.Name);
    
                    // Write an analogue of the SQL query:
                    //   SELECT author, name FROM books
                    //   WHERE to_tsvector(content) @@ to_tsquery('king & palace & !Norfolk')
                    var secondQuery = ctx.Books
                        .Where(b => PgSqlTextFunctions.Match(
                            PgSqlTextFunctions.ToTsVector(b.Content), 
                            PgSqlTextFunctions.ToTsQuery("king & palace & !Norfolk"))
                        )
                        .Select(b => new { b.Author, b.Name });
                    var secondQueryResults = secondQuery.ToList();
    
                    // Output:
                    Console.WriteLine();
                    Console.WriteLine("2) Boolean mode");
                    Console.WriteLine();
                    Console.WriteLine("SQL:");
                    Console.WriteLine(secondQuery.ToString());
                    Console.WriteLine();
                    Console.WriteLine("Data:");
                    Console.WriteLine("{0,-5} | {1}", "Author", "Name");
                    Console.WriteLine(new string('-', 50));
                    foreach (var item in secondQueryResults)
                        Console.WriteLine("{0,-5} | {1} ", item.Author, item.Name);
    
                    Console.ReadLine();
    
                }

    Conclusión

    Por lo tanto, hemos mostrado cómo usar las funciones de búsqueda de texto completo de PostgreSQL en las aplicaciones de Entity Framework, incluido un proceso de desarrollo basado en Code-First Migrations. Esperamos saber de usted a través de los comentarios, el foro de Entity Framework y nuestro formulario de contacto si necesita rediseñar o mejorar el soporte para la búsqueda e indexación de texto completo en nuestro proveedor.
    Puede descargar el archivo con la muestra completa de este tutorial.

    Artículos de interés

    Subir