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

Índice

Introducción

La base de datos de SQLite es compatible con la búsqueda avanzada de texto completo (FTS) y la función de indexación de texto completo, que se detallan en la documentación de SQLite: extensiones de SQLite FTS3 y FTS4.
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 SQLite ADO.NET Entity Framework.
Este artículo trata sobre lo siguiente:

Búsqueda de texto completo en LINQ to Entities

Implementar una búsqueda de SQLite de texto completo tiene sus propias características, por lo que las entidades para las que desea utilizar una búsqueda de texto completo deben cumplir con los siguientes requisitos:

  • Dado que SQLite realiza una búsqueda de texto completo no en las columnas de la tabla, sino en una columna virtual que tiene el mismo nombre que la tabla, un objeto de texto completo debe tener una propiedad de fila asignada a una columna con el mismo nombre que la tabla. mesa. No debe asignar nada a esta propiedad ni solicitar esta propiedad. Se usa solo como un argumento de función de búsqueda de texto completo.
  • Las tablas de búsqueda de texto completo de SQLite solo tienen columnas de fila, por lo que los objetos asignados a ellas deben tener solo propiedades de cadena. Cualquier modificador, como el atributo MaxLength, se ignorará al crear una tabla mediante migraciones de Code-First. La única excepción es toda la columna de ID de fila, que SQLite genera automáticamente. Por lo tanto, si necesita la clave del objeto generado por la base de datos int, puede crear una propiedad asignada a la columna de ID de fila con el atributo DatabaseGenerated (DatabaseGeneratedOption.Identity). Tenga en cuenta que la columna "rowid" debe estar en minúsculas.

Búsqueda de texto completo en LINQ to Entities

El Funciones SQLiteText clase ubicada en el pueblo Devart.Data.SQLite.Entity.dll la compilación se usa para generar consultas de búsqueda de texto completo específicas en LINQ to Entities. Tiene los siguientes métodos para usar la función de búsqueda de SQLite de texto completo:

  • Coincidir - realiza la búsqueda de texto completo real por datos
  • Información del partido - devuelve información estadística sobre las coincidencias de la operación de búsqueda de texto completo realizada en esta consulta
  • conveniencias - devuelve la ubicación de los tokens requeridos
  • Fragmento - devuelve fragmentos de texto alrededor de las coincidencias encontradas.

Por ejemplo, la siguiente consulta:

  var firstQuery = ctx.Books
                  .Where(b => SQLiteTextFunctions.Match(b.Fts, "search expression"))
                  .Select(b => new { b.Rowid, b.Name });

Se traducirá al siguiente SQL:

SELECT 
Extent1."rowid",
Extent1.Name
FROM Books AS Extent1
WHERE Extent1.Books MATCH 'search expression'

Búsqueda de texto completo de tablas en guiones de código

Para configurar la migración de Code-First para crear tablas de búsqueda de texto completo, Configuración de SQLiteCreateTable usó la clase que está en Devart.Data.SQLite.Entity.Migrations.dll montaje.
Configuración de SQLiteCreateTable diseñado para la personalización crear una tabla operación. Esta clase le permite establecer los parámetros de búsqueda de SQLite de texto completo para la tabla que está creando ( Tipo de tabla y FtsTokenizador propiedades). Para habilitar la búsqueda de texto completo en una tabla, debe especificar Tipo de tabla como SQLiteTableType.Fts3 o SQLiteTableType.Fts4.

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.

requisitos previos

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

  • Entidad.de.datos.del.sistema;
  • Devart.Datos;
  • Devart.Data.SQLite;
  • Devart.Data.SQLite.Entity;
  • Devart.Data.SQLite.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 y poner 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.SQLite;

namespace SQLiteFullTextSearch {

  public class MyContext: DbContext {

    public MyContext() {
    }

    public MyContext(SQLiteConnection 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.SQLite.Entity.Migrations.SQLiteConnectionInfo.InvariantName,
              new Devart.Data.SQLite.Entity.Migrations.SQLiteEntityMigrationSqlGenerator());
    }

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="Data Source=d:Test.db;FailIfMissing=false" 
      providerName="Devart.Data.SQLite" />
</connectionStrings>

Usar búsqueda de texto completo

1. Agregar Libro.cs archivo de proyecto:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;

namespace SQLiteFullTextSearch {

  public class Book {

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Column("rowid")]
    public int Rowid { get; set; }

    public string Author { get; set; }

    public string Name { get; set; }

    public string Content { get; set; }

    [Column("Books")]
    public string Fts { get; set; }

  }
}

Aquí usamos una columna de ID de fila que genera automáticamente SQLite para todas las tablas para mostrar la clave de entidad.

La propiedad Fts se crea para realizar una búsqueda de texto completo. Se asigna a una columna virtual que tiene el mismo nombre que la tabla.
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
                    {
                        Rowid = c.Int(nullable: false, identity: true),
                        Author = c.String(maxLength: 200),
                        Name = c.String(maxLength: 300),
                        Content = c.String(),
                        Books = c.String(),
                    })
                .PrimaryKey(t => t.Rowid);

        }

        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.SQLite.Entity;
    using Devart.Data.SQLite.Entity.Migrations;

    public partial class AddBook : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "Books",
                c => new
                    {
                        Rowid = c.Int(nullable: false, identity: true),
                        Author = c.String(maxLength: 200),
                        Name = c.String(maxLength: 300),
                        Content = c.String(),
                        Books = c.String(),
                    },
                    anonymousArguments: new SQLiteCreateTableConfiguration()
                    {
                        TableType = SQLiteTableType.Fts4,
                        FtsTokenizer = SQLiteFtsTokenizer.Porter
                    })
                .PrimaryKey(t => t.Rowid);

        }

        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 VIRTUAL TABLE Books USING fts4(Author, Name, Content, tokenize=porter)

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 de frases

En este tutorial, hemos habilitado SQLiteMonitor para ver sentencias DDL y DML ejecutables en la aplicación Devart dbMonitor. Sin embargo, tenga en cuenta que el uso de SQLiteMonitor es posible con fines de prueba 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 SQLiteMonitor() { 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 SQLiteFullTextSearch.MyContext()) {

  // Load information into the Books table
  ctx.Books.Add(new SQLiteFullTextSearch.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 SQLiteFullTextSearch.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 SQLiteFullTextSearch.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 rowid, name FROM Books
  //  WHERE Books MATCH 'king palace Norfolk';
  var firstQuery = ctx.Books
    .Where(b => SQLiteTextFunctions.Match(b.Fts, "king palace Norfolk"))
    .Select(b => new { b.Rowid, b.Name } );
  var firstQueryResults = firstQuery.ToList();

  // Output:
  Console.WriteLine();
  Console.WriteLine("== Querying the table ==");
  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}", "Rowid", "Name");
  Console.WriteLine(new string('-', 50));
  foreach (var item in firstQueryResults)
    Console.WriteLine("{0,-5} | {1} ", item.Rowid, item.Name);        

  // Write an analogue of the SQL query:
  //   SELECT rowid, name FROM books
  //   WHERE Books MATCH '"king''s palace"'
  var secondQuery = ctx.Books
      .Where(b => SQLiteTextFunctions.Match(b.Fts, ""king's palace""))
      .Select(b => new { b.Rowid, b.Name });
  var secondQueryResults = secondQuery.ToList();

  // Output:
  Console.WriteLine();
  Console.WriteLine("2) Phrase query");

  Console.WriteLine();
  Console.WriteLine("SQL:");
  Console.WriteLine(secondQuery.ToString());
  Console.WriteLine();
  Console.WriteLine("Data:");
  Console.WriteLine("{0,-5} | {1}", "Rowid", "Name");
  Console.WriteLine(new string('-', 50));
  foreach (var item in secondQueryResults)
    Console.WriteLine("{0,-5} | {1} ", item.Rowid, item.Name);
}

Conclusión

Por lo tanto, hemos mostrado cómo utilizar la función de búsqueda de texto completo de SQLite en las 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