Uso de la búsqueda MySQL de texto completo en Entity Framework

Índice

    Introducción

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

    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 MySQL ADO.NET Entity Framework.
    Este artículo trata sobre lo siguiente:

    Búsqueda de texto completo en LINQ to Entities

    El Funciones de MySqlText clase ubicada en el pueblo Devart.Data.MySql.Entity.dll la compilación se usa para generar consultas de búsqueda de texto completo específicas en LINQ to Entities. Tiene 6 métodos, cada uno de los cuales tiene 5 sobrecargas para generar consultas de una, dos, tres, cuatro y cinco columnas.

    Método de devolución
    Doble
    Método de devolución
    Bulevar
    DescripciónSQL generado
    Partido contraPartidoContraAsBoolLenguaje natural
    búsqueda de texto completo
    Partido (columna 1, columna 2, ()
    EN CONTRA (expresión)
    CoincidenciaContraConConsulta
    Extensión
    CoincidenciaContraConConsulta
    ExpansiónAsBool
    Búsqueda de texto completo
    con una solicitud
    extensión
    Partido (columna 1, columna 2, ()
    EN CONTRA (expresión con PROHIBICIÓN DE PRÓRROGA)
    PartidoContraEnBooleano
    Modo
    PartidoContraEnBooleano
    ModoAsBool
    Texto completo booleano
    búsqueda
    Partido (columna 1, columna 2, ()
    EN CONTRA (EXPRESIÓN EN LOS MODOS DE BOUL)

    Se agregaron métodos que devuelven System.Boolean para facilitar la escritura de consultas LINQ to Entities en una forma que se parece más a la práctica habitual de escribir consultas SQL cuando se devuelve un determinado valor. PARTIDO CONTRA (...) la expresión no importa. Es decir, si necesita escribir un análogo de la consulta:

    SELECT *
      FROM MyTable
     WHERE MATCH (Column1) AGAINST ('MyExpression')

    puedes usar:

      var query = ctx.MyTables
        .Where(t => MySqlTextFunctions.MatchAgainstAsBool(t.Column1, "MyExpression"));

    Si necesitas escribir la siguiente consulta:

    SELECT *
      FROM MyTable
     WHERE MATCH (Column1) AGAINST ('MyExpression' WITH QUERY EXPANSION) > 0.5

    debe usar el método habitual de doble forma:

      var query = ctx.MyTables
        .Where(t => MySqlTextFunctions.MatchAgainstWithQueryExpansion(t.Column1, "MyExpression") > 0.5);

    Al devolver los resultados de PARTIDO CONTRA (...) ejecución requerida, luego una consulta SQL de este tipo:

    SELECT t.*, MATCH (Column1) AGAINST ('+MyExpression1 -MyExpression2' IN BOOLEAN MODE) "Score"
      FROM MyTable t
     WHERE MATCH (Column1) AGAINST ('+MyExpression1 -MyExpression2' IN BOOLEAN MODE)

    Se puede escribir como:

     var query = ctx.MyTables
        .Where(t => MySqlTextFunctions.MatchAgainstInBooleanModeAsBool(t.Column1, "+MyExpression1 -MyExpression2"))
        .Select(t => new {
           MyTable = t,
           Score = MySqlTextFunctions.MatchAgainstInBooleanMode(t.Column1, "+MyExpression1 -MyExpression2")
        });

    Índices de texto completo en guiones de código

    Para configurar la migración de Code-First para crear índices de texto completo, archivo MySqlCreateTableConfiguration y MySqlCreateIndexConfiguration clases usadas que se encuentran en el Devart.Data.MySql.Entity.Migrations.dll montaje.
    MySqlCreateIndexConfiguration diseñado para la personalización Crear un índice operación. Esta clase le permite especificar que el índice que se está creando es de texto completo ( EsTextoCompleto properties) y configure un analizador personalizado para un índice de texto completo ( analizador propiedad).
    MySqlCreateTableConfiguration diseñado para la personalización crear una tabla operación. Esta clase le permite especificar el mecanismo de almacenamiento de MySQL para la tabla que está creando ( Motor de almacenamiento property) para evitar depender del mecanismo de almacenamiento predeterminado establecido en una base de datos específica. Este es un aspecto importante de la creación de índices de texto completo, ya que son compatibles con MyISAM (todas las versiones de MySQL 5.x) e InnoDB (solo a partir de MySQL 5.6).
    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 MySqlFullTextSearch.
    2. En el proyecto, agregue enlaces a las siguientes compilaciones:

    • Devart.Datos;
    • Devart.Data.MySql;
    • Devart.Data.MySql.Entity;
    • Devart.Data.MySql.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 MySqlFullTextSearch.
    2. En el proyecto, agregue enlaces a las siguientes compilaciones:

    • Entidad.de.datos.del.sistema;
    • Devart.Datos;
    • Devart.Data.MySql;
    • Devart.Data.MySql.Entity;
    • Devart.Data.MySql.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.MySql;
    
    namespace MySqlFullTextSearch {
    
      public class MyContext: DbContext {
    
        public MyContext() {
        }
    
        public MyContext(MySqlConnection connection)
          : base(connection, false) {
        }
    
        static MyContext() {
    
          var config = Devart.Data.MySql.Entity.Configuration.MySqlEntityProviderConfig.Instance;
          config.Workarounds.DisableQuoting = true;
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    
        }
    
      }
    }

    Aquí, para no sobrecargar la muestra con comillas, también establecemos Deshabilitar cita El valor del parámetro de configuración del proveedor de EF es True para obtener ID de DDL y DML sin comillas durante la generación de SQL. En los programas personalizados, esto es opcional.
    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.MySql.Entity.Migrations.MySqlConnectionInfo.InvariantName,
                  new Devart.Data.MySql.Entity.Migrations.MySqlEntityMigrationSqlGenerator());
        }

    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=3306;Base de datos=my_database;" providerName="Devart.Data.MySql" />
    </connectionstrings>

    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 int AUTO_INCREMENT UNIQUE NOT NULL,
      Author varchar(200) NULL,
      Name varchar(300) NULL,
      Content longtext NULL,
      PRIMARY KEY (Id),
      FULLTEXT INDEX (Content)
    ) ENGINE = MyISAM

    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 MySqlFullTextSearch {
    
      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.MySql.Entity;
        using Devart.Data.MySql.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),
                        },
                    anonymousArguments: new MySqlCreateTableConfiguration() {
                            StorageEngine = MySqlStorageEngine.MyISAM,                   
                        })
                    .Index(c => c.Content,
                        anonymousArguments: new MySqlCreateIndexConfiguration() {
                            IsFulltext = true
                        })
                    .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 int AUTO_INCREMENT UNIQUE NOT NULL,
      Author varchar(200) NULL,
      Name varchar(300) NULL,
      Content longtext NULL,
      PRIMARY KEY (Id),
    ) ENGINE = MyISAM;
    
    CREATE FULLTEXT INDEX IX_Books_Text ON Books (Text);

    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 MySqlMonitor para ver las instrucciones DDL y DML ejecutadas en la aplicación Devart dbMonitor. Sin embargo, tenga en cuenta que el uso de MySqlMonitor 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 MySqlMonitor() { 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 MyContext()) {
    
      // Load information into the Books table
      ctx.Books.Add(new 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 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 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 id, name FROM books
      //  WHERE MATCH (content) AGAINST ('king palace Norfolk');
      var firstQuery = ctx.Books
        .Where(b => MySqlTextFunctions.MatchAgainstAsBool(b.Content, "king palace Norfolk"))
        .Select(b => new { b.Id, 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}", "Id", "Name");
      Console.WriteLine(new string('-', 50));
      foreach (var item in firstQueryResults)
        Console.WriteLine("{0,-5} | {1} ", item.Id, item.Name);        
    
      // Write an analogue of the SQL query:
      //   SELECT id, name FROM books
      //   WHERE MATCH (content) AGAINST ('+king +palace -Norfolk' IN BOOLEAN MODE)
      var secondQuery = ctx.Books
                .Where(b => MySqlTextFunctions.MatchAgainstInBooleanModeAsBool(b.Content, "+king +palace -Norfolk"))
                .Select(b => new { b.Id, 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}", "Id", "Name");
      Console.WriteLine(new string('-', 50));
      foreach (var item in secondQueryResults)
        Console.WriteLine("{0,-5} | {1} ", item.Id, item.Name);
    }

    Utilice un índice de texto completo para varias columnas

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

    1. Agregue un índice de texto completo de varias columnas a uno existente Libros mesa

    CREATE FULLTEXT INDEX IX_Books_Author_Name ON Books (Author, Name)

    2. No hay necesidad de realizar Actualizar desde la base de datos para el modelo EF porque el modelo XML no almacena información de índice.

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

    1. Para crear un inicio de migración vacío, ingrese el siguiente comando Consola del administrador de paquetes:

    Add-Migration CreateAdditionalIndex

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

        public partial class CreateAdditionalIndex : DbMigration
        {
            public override void Up()
            {
            }
    
            public override void Down()
            {
            }
        }

    2. Complete la migración con comandos para crear (y eliminar) un índice de texto completo:

    using Devart.Data.MySql.Entity.Migrations;
    
        public partial class CreateAdditionalIndex : DbMigration
        {
            public override void Up()
            {
              CreateIndex(
                table: "Books",
                name: "IX_Books_Author_Name",
                columns: new string[] { "Author", "Name" },
                anonymousArguments: new MySqlCreateIndexConfiguration() {
                  IsFulltext = true
                });
            }
    
            public override void Down()
            {
              DropIndex(table: "Books", name: "IX_Books_Author_Name");
            }
        }

    3. Actualice la base de datos aplicando la migración, ejecute el siguiente comando Consola del administrador de paquetes:

    Update-Base de datos -Verbose

    Como resultado, se ejecuta el siguiente comando:

    CREATE FULLTEXT INDEX IX_Books_Author_Name ON Books (Author, Name)

    y un comando para llenar Tabla de historial tabla del sistema con información sobre la migración aplicada.

    Ejemplo de búsqueda de texto completo con varias columnas

    Escriba las consultas que componen el código, en varias columnas, utilizando la consulta en lenguaje natural y lenguaje natural con extensión de consulta.

    using (var ctx = new MyContext()) {
    
      // Write an analogue of the SQL query:
      //   SELECT id, name FROM books
      //   where MATCH (Author, Name) AGAINST ('william king life death');
      var firstQuery = ctx.Books
        .Where(b => MySqlTextFunctions.MatchAgainstAsBool(b.Author, b.Name, "william king life death"))
        .Select(b => new { b.Id, b.Name });
      var firstQueryResults = firstQuery.ToList();
    
      // Output
      Console.WriteLine();
      Console.WriteLine("== Querying your multiple-column full-text index ==");
      Console.WriteLine();
      Console.WriteLine("3) Natural language");
      Console.WriteLine();
      Console.WriteLine("SQL:");
      Console.WriteLine(firstQuery.ToString());
      Console.WriteLine();
      Console.WriteLine("Data:");
      Console.WriteLine("{0,-5} | {1}", "Id", "Name");
      Console.WriteLine(new string('-', 50));
      foreach (var item in firstQueryResults)
        Console.WriteLine("{0,-5} | {1} ", item.Id, item.Name);
    
      // Write an analogue of the SQL query:
      //  SELECT b.*, MATCH (Author, Name) AGAINST ('william king life death' WITH QUERY EXPANSION)
      //  FROM books b WHERE MATCH (Author, Name) AGAINST ('william king life death' WITH QUERY EXPANSION) > 0.8
      var secondQuery = ctx.Books
                .Where(b => MySqlTextFunctions.MatchAgainstWithQueryExpansion(b.Author, b.Name, "william king life death") > 0.8)
                .Select(b => new {
                  Book = b,
                  Score = MySqlTextFunctions.MatchAgainstWithQueryExpansion(b.Author, b.Name, "william king life death")
                });
      var secondQueryResults = secondQuery.ToList();
    
      // Output
      Console.WriteLine();
      Console.WriteLine("4) Natural language with query expansion");
      Console.WriteLine();
      Console.WriteLine("SQL:");
      Console.WriteLine(secondQuery.ToString());
      Console.WriteLine();
      Console.WriteLine("Data:");
      Console.WriteLine("{0,-5} | {1,-40} | {2}", "Id", "Name", "Score");
      Console.WriteLine(new string('-', 70));
      foreach (var item in secondQueryResults)
        Console.WriteLine("{0,-5} | {1,-40} | {2}", item.Book.Id, item.Book.Name, item.Score);
    
    }

    Conclusión

    Por lo tanto, hemos mostrado cómo usar la funcionalidad de búsqueda de MySQL de texto completo en aplicaciones de Entity Framework, incluido un proceso de desarrollo basado en Code-First Migrations. Esperamos recibir sus comentarios a través de 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 aquí.

    Artículos de interés

    Subir