Normalmente las búsquedas de texto completo son insensibles a las mayúsculas, es decir, no importa si las letras de una palabra están escritas en mayúsculas o minúsculas para ser consideradas equivalentes. La ventaja de esto es que podemos obtener la mayor cantidad de resultados posibles, aunque no siempre es lo más adecuado, y por ello debemos aprender a reconocer la diferencia de comportamiento que tenemos al cambiarlo para que sea sensible a mayúsculas.

La sensibilidad a las mayúsculas (case sensitivity) es una característica cuya implementación podría depender del sistema operativo, de la aplicación o del contexto.

Puedes determinar la sensibilidad de tu sistema operativo con una prueba sencilla, agregando los siguientes directorios: wiki, WiKi, wIkI. Linux los creará sin problemas pues es case-sensitive, pero en algunas versiones de Windows quizás falle al crear el segundo directorio, dado que es case-insensitive y el sistema operativo va a considerarlo como un nombre de directorio que existe.

Hay áreas donde la sensibilidad a las mayúsculas es muy importante, como por ejemplo al realizar comparaciones de contraseñas. Sin embargo, en las búsquedas de texto completo los resultados se pueden ver afectados por el contexto, y es lo que explicaremos a continuación.

Búsqueda de texto completo insensitiva a mayúsculas

En una búsqueda insensitiva (case-insensitive), podemos escribir una palabra usando cualquier combinación de letras mayúsculas y minúsculas sin que se vea afectado el resultado. De esta manera hemos estado realizando las búsquedas en modo de lenguaje natural, booleano y de expansión.

Por lo que podríamos buscar la frase base de datos escribiéndola como: «Base de Datos», «base de datos», «BASE DE DATOS», «BaSe de dATOS» y obtendríamos el mismo resultado:

  • http://127.0.0.1:8000/?search=Base+De+Datos
  • http://127.0.0.1:8000/?search=base+de+datos
  • http://127.0.0.1:8000/?search=BASE+DE+DATOS
  • http://127.0.0.1:8000/?search=BaSe+de+dATOS

No importa el lugar en donde intercalemos una letra mayúscula, todas las consultas anteriores devolverán el mismo resultado.

La buena razón que hay detrás de esto es que el usuario tenga flexibilidad al momento de escribir las palabras, sin que un error de letra mayúscula o minúscula altere los resultados.

La interpretación de los caracteres especiales

En español las palabras se clasifican en agudas, graves o esdrújulas utilizando la tilde, lo que hace que adquieran un significado diferente. En las búsquedas insensitivas, los caracteres especiales de las vocales tampoco afectan para considerar una palabra como distinta.

Por ejemplo, al ejecutar esta consulta: http://127.0.0.1:8000/?search=basé+de+datós.

El resultado devuelto es el mismo que el anterior, pero es realmente sorpresivo. La razón de ello es que en un idioma como el nuestro lo importante es comunicarse, incluso en sacrificio de la exactitud de las palabras. Pero esto puede ocasionarnos serios problemas en los resultados, ya que estamos acostumbrados a darle una interpretación exacta a las palabras y para nosotros no significan lo mismo.

No te preocupes si no encerramos la frase entre comillas simples ' ', el símbolo + es un estándar que surgió desde el primer navegador y se usa como separador entre palabras.

Aprende diversos tips para optimizar el rendimiento de tu consultas SQL con Laravel con nuestro Curso de Optimización con Eloquent.

Ver más

Las búsquedas sensibles a mayúsculas

Las búsquedas sensitivas dentro de un documento no son tan desconocidas como aparentan ser, la podemos encontrar como una opción de búsqueda en cualquier editor de texto y en los procesadores de palabras, con nombres de opción como Case sensitive, Match Case, etc. Además, pueden ayudarnos a buscar información sin ambigüedad dentro del texto.

Aprovechando las reglas de ortografía, si buscas por el nombre de una persona ya sabes de antemano que debe estar capitalizado, por ejemplo «Miguel González». Sabemos que lo mismo sucede cuando buscamos por el nombre de una ciudad, por ejemplo «Madrid» o por marcas comerciales y otros nombre propios como los de las organizaciones, ejemplo «Union Europea», los cuales pueden ser identificados sin ambigüedad.

También podemos encontrarnos con nombres propios que son utilizados como sustantivos para las cosas y esto puede derivar en confusiones. Por ejemplo, si en el documento aparecen las palabras «Margarita» y «margarita» donde la primera se refiere a un nombre de mujer y la segunda al nombre de una flor, haciendo uso del modo case-sensitive desparece cualquier duda.

¡Pero no todo llega hasta aquí!

Aprende a manejar tus bases de datos con el ORM de Laravel en nuestro Curso de Eloquent.

Ver más

La estructura de un documento

Normalmente, el uso de letras mayúsculas es muy importante para destacar -con diversos grados de énfasis- las diferentes partes que conforman la estructura de un documento escrito. Por ejemplo, podríamos utilizar la siguiente convención:

  • El título principal puede estar escrito completamente en mayúsculas porque es el titular, es decir, lo más importante. Ejemplo: «CONCEPTOS DE LA ARQUITECTURA».
  • Los títulos secundarios deben utilizar la primera letra de cada palabra en mayúscula y el resto en minúscula, tal como esto: «Ciclo De Vida De La Solicitud HTTP», «El Contenedor De Servicios» o «Los Proveedores De Servicios».
  • Los párrafos deben ir en minúsculas, respetando las mayúsculas según las reglas de puntuación. Aquí se escribe la primera letra en mayúscula en la palabra que va al principio de un párrafo o después de un punto y seguido, tal como lo ves en este párrafo.

Si sabemos la convención podemos distinguir más fácilmente lo que andamos buscando. Como ejemplo, cuando la frase sea «CONCEPTOS ARQUITECTURA» sabremos inmediatamente que buscamos el título principal del documento que coincida, pues conocemos de antemano que éstos deben ir en mayúsculas.

Siguiendo con el ejemplo, si decidimos buscar una frase como «Ciclo Vida», con seguridad estamos buscando un subtítulo que nos lleva directo al tópico dentro del documento. Y por otra parte, si la frase es «contenedor servicio» no cabe duda que se refiere a cualquier párrafo donde aparezca, exceptuando el título principal y los subtítulos.

Configurando un índice FULLTEXT para  utilizar la sensibilidad a las mayúsculas

Primero debes entender que es una colación (COLLATION): Una colación o cotejo no es más que un conjunto de reglas para un conjunto de caracteres (CHARSET) que definen el ordenamiento y comparación entre las cadenas de caracteres, bien sea binarias (los tipos BINARY, VARBINARY y BLOB) o no binarias (los tipos CHAR, VARCHAR y TEXT). Es decir, sirven para cotejar o comparar las cadenas.

Un conjunto de caracteres puede tener varias colaciones, pero una colación puede ser utilizada por un solo conjunto de caracteres.

Para ejecutar una búsqueda de texto completo sensitiva a mayúsculas, debes usar una colación binaria o cualquier otra sensible a mayúsculas en la definición de la(s) columna(s) que forman el índice FULLTEXT. Las binarias usan el sufijo _bin  y las sensibles se reconocen por el sufijo _cs (case sensitive) en el nombre de la colación.

Por ejemplo, en MySQL una columna que usa un conjunto de caracteres utf8mb4 le puede ser asignada una colación utf8mb4_bin para hacer que las búsquedas de texto completo sean sensibles a mayúsculas.

Aunque en una tabla pueden coexistir distintos conjuntos de caracteres y colaciones, se requiere que las columnas de texto que forman el índice posean el mismo conjunto de caracteres y colación.

Vamos a modificar la migración donde se crea la tabla chapters, para especificar una colación binaria en la definición de las columnas que conforman el índice FULLTEXT:

// database/migrations/2020_12_14_055343_create_chapters_table.php

public function up()
{
    Schema::create('chapters', function (Blueprint $table) {
        $table->id();

        $table->string('title')->collation('utf8mb4_bin');
        $table->longText('content')->collation('utf8mb4_bin');

        $table->string('translator');
        $table->timestamps();
    });

    DB::statement('
        create fulltext index chapters_title_content_fulltext
        on chapters(title, content);
    ');
}

No olvides ejecutar las migraciones y los seeders con el comando php artisan migrate:fresh --seed, porque el cambio en la colación afecta al índice FULLTEXT. Puedes ver este cambio en el repositorio.

Búsquedas de texto completo sensibles a mayúsculas

Para hacer la interpretación de los resultados menos complicada para ti, vamos a ajustar ligeramente el código en el archivo routes/web.php para sustituir la búsqueda en expansión que hemos estado usando por la búsqueda en lenguage natural, cuyos resultados son más simples de visualizar:

// routes/web.php

Route::get('/', function () {
    return Chapter::query()
        ->when(request('search'), function ($query, $search) {
            $query->select('id', 'title', 'content')
                ->selectRaw(
                    'match(title,content) against(? in natural language mode) as score',
                    [$search]
                )
                ->whereRaw(
                    'match(title,content) against(? in natural language mode) > 0.0000001',
                    [$search]
                );
        })
        ->get();
});

Ahora, si deseamos buscar la frase «BASES DE DATOS» en el título solamente (pensando en la convención anterior) haríamos una búsqueda como: http://127.0.0.1:8000/?search=BASE+DE+DATOS, la cual devuelve un resultado vacío, ya que no estamos utilizando el estilo para destacar el título principal en un documento.

Por otra parte, los títulos de cada capítulo tampoco están utilizando la convención para los títulos secundarios, así que el resultado debe ser vacío si buscamos por: http://127.0.0.1:8000/?search=Bases+De+Datos.

No te preocupes, lo que queremos que notes es que el índice es case-sensitive y las letras mayúsculas están siendo diferenciadas.

Para buscar dentro de los párrafos debemos pensar que la primera palabra puede estar al principio o en un lugar intermedio, así que destacaremos esto en dos consultas.

La primera, buscando por ocurrencias que aparezcan al principio de un párrafo:

http://127.0.0.1:8000/?search=Bases+de+datos
[
  {
    "id": 1,
    "title": "Primeros pasos con bases de datos",
    "content": "El manejo de bases de datos se hace utilizando Eloquent, el constructor de consultas, o SQL puro",
    "score": 1.2110387086868286
  }
]

La segunda, suponiendo que no está al comienzo sino que está en cualquier parte del párrafo:

http://127.0.0.1:8000/?search=bases+de+datos
[
  {
    "id": 1,
    "title": "Primeros pasos con bases de datos",
    "content": "El manejo de bases de datos se hace utilizando Eloquent, el constructor de consultas, o SQL puro",
    "score": 2.4220774173736572
  }
]

Aparentemente, ambas consultas devuelven el mismo resultado. Sin embargo, si te fijas más detalladamente en el score hay una gran diferencia.

En el primer caso hay coincidencia únicamente con la palabra datos, en tanto que la palabra Bases no aplica por la mayúscula y la palabra de es demasiado corta y se descarta. En el segundo caso, coinciden dos palabras bases y datos, por eso se refleja un aumento de la relevancia.

Aprende diversos tips para optimizar el rendimiento de tu consultas SQL con Laravel con nuestro Curso de Optimización con Eloquent.

Ver más

Búsqueda de nombres propios en el modo case-sensitive

En nuestra colección de estudio podemos encontrar varios nombres propios, entre los que destaca Eloquent. En este caso, puede aparecer en cualquier parte del documento, bien en un título secundario o dentro de un párrafo, pero siempre con la primera letra en mayúscula por lo que necesitaremos nuevamente las búsquedas sensible a mayúsculas (case-sensitive).

Vamos a realizar la siguiente consulta: http://127.0.0.1:8000/?search=Eloquent

[
  {
    "id": 1,
    "title": "Primeros pasos con bases de datos",
    "content": "El manejo de bases de datos se hace utilizando Eloquent, el constructor de consultas, o SQL puro",
    "score": 0.0906190574169159
  },
  {
    "id": 3,
    "title": "Eloquent",
    "content": "Es una interaz active record incluida con Laravel donde un modelo es definido para acceder a una tabla.",
    "score": 0.0906190574169159
  },
  {
    "id": 4,
    "title": "Relationships",
    "content": "Eloquent permite que podamos accesar la información de los vínculos entre las tablas.",
    "score": 0.0906190574169159
  }
]

Como puedes apreciar, encontramos coincidencias en los párrafos del contenido para los registros con ID 1 y 4, mientras que en el documento con ID 3 la coincidencia es en el campo title.

Cada conjunto de caracteres posee al menos una colación binaria que podemos utilizar, excepto por el conjunto utf8mb4 que tiene dos posibilidades.

Material relacionado

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Búsquedas de texto completo con el modo Query Expansion en Laravel Lección siguiente Habilitar opción para activar la sensibilidad a mayúsculas (Match-case) en búsquedas FULLTEXT