No dude en tomar cualquier pieza de código que le resulte útil. O mejor aún, ¡eres más que bienvenido a convertirte en un mantenedor! Mis más profundas disculpas sinceras por dejar ir este proyecto. Espero que no te cause ningún inconveniente.
Búsqueda de texto completo de Postgres en Node.js usando Sequelize como su ORM.
Mira el ejemplo del código.
Esta biblioteca utiliza la búsqueda de texto completo de Postgres y las vistas materializadas para ejecutar consultas de búsqueda rápidas, potentes y simples. Primero crea una visión materializada concatenando todos los valores en los campos que desea hacer de búsqueda y los convierte en un ts_vector . Luego agrega un searchByText y Métodos de clase search a su modelo de secuela, que le permite ejecutar búsquedas desde una cadena de consulta solamente, o una cadena de consulta y un objeto JSON opciones. También agrega un método de clase de refresh para actualizar la vista materializada cuando se realizan actualizaciones a su modelo o cuando lo desee.
Por ejemplo, supongamos que tenemos una tabla de base de datos de películas que tiene:
| identificación | nombre | descripción | ciudad | liberar_date | clasificación |
|---|---|---|---|---|---|
| 1 | El fugitivo | Otro chico escapa de la prisión | Chicago | 1993-08-06 | 8 |
| 2 | Una mente hermosa | Una película sobre un matemático | Nueva York | 2001-12-05 | 8 |
| 3 | Chicago | Un buen musical americano | Toronto | 2002-12-10 | 7 |
| 4 | Sunshina eterna de la mente impecable | Jim Carrey cuando no es comediante | Nueva York | 2004-03-19 | 8 |
Queremos permitir la búsqueda por nombre, descripción y ciudad, y filtrado por calificación y liberación_date. También queremos ordenar los resultados por relevancia dando al campo del nombre un peso más alto que la descripción y la ciudad. Entonces, si ejecuta la búsqueda:
Film . searchByText ( "Chicago" ) ; // Returns [ Chicago, The Fugitive ]Los resultados con la palabra "Chicago" en el título aparecen ante aquellos con la misma palabra en la descripción o la ciudad. Así, la película Chicago aparecería primero, y el fugitivo sería el segundo.
También podemos agregar filtrado y pedido mediante un cierto campo en lugar de ordenar por relevancia haciendo la consulta de búsqueda:
Film . searchByText ( "Mind order:releaseDate" ) ; // Returns [ A Beautiful Mind, Eternal Sunshine of the Spotless Mind ]
// or
Film . searchByText ( "Mind releaseDate:<2002-01-01" ) ; // Returns [ A Beautiful Mind ]Tampoco se limita solo a los campos de un modelo; También puede incluir campos de modelos asociados. Por ejemplo, si tuviéramos otro modelo en nuestra base de datos llamada actor que está asociado a la película por una clave extranjera, podemos incluir campos del modelo de actor para estar en la vista de la película. Esto nos permite ejecutar búsquedas como:
Film . searchByText ( "Tom Hanks" ) ; // Returns Tom Hanks moviesHay más que puedes hacer con esta biblioteca. Para obtener más detalles sobre cómo instalarlo y usarlo, consulte las secciones de instalación y uso. Para la documentación, consulte la sección de documentación.
NPM Instale el paquete pg-search-sequelize
npm i --save pg-search-sequelizeLuego requiera que en su archivo de definición de modelo de vista materializado y pase a él el modelo de secuelas para que se pueda buscar:
let MyModel = sequelize . define ( ... ) ; // your sequelize model definition
let SearchModel = require ( 'pg-search-sequelize' ) ; // Require the pg-search-sequelize library
MyModel = new SearchModel ( MyModel ) ; // Construct a SearchModel out of the model you defined above. This adds `search`, `searchByText`, and `refresh` class methods to your model.Consulte el uso de cómo definir su modelo y cómo crear la vista materializada.
Ahora que obtienes un adelanto de lo que esta biblioteca te permite al final, vamos a los pasos de configuración:
Si utiliza la herramienta Seculize Migrations, puede usar la función de ayuda createMaterializedView(name, referenceModel, attributes, options) proporcionada por la clase QueryInterface :
const QueryInterface = require ( "pg-search-sequelize" ) . QueryInterface ;
const models = require ( "../models" ) ;
// The model we're creating the materialized view for
const referenceModel = models . Film ;
const materializedViewName = "film_materialized_view" ;
const attributes = { // field: weight. Every field has a weight to calculate how relevant the search results are.
name : "A" , // name has the highest weight.
description : "B" ,
city : "C" // city has a lower weight than title and description
}
const options = {
include : [ // You can also include fields from associated models
{
model : models . Actor ,
foreignKey : "actor_id" ,
targetKey : "id" ,
associationType : "hasMany" , // association types are: belongsTo, hasOne, or hasMany
attributes : { // Those attributes get added to the materialized view's search document and will also be searched just like the other fields
first_name : "D" ,
last_name : "D" ,
date_of_birth : "D"
}
}
]
}
module . exports : {
up : queryInterface => new QueryInterface ( queryInterface ) . createMaterializedView ( materializedViewName , referenceModel , attributes , options ) ,
down : queryInterface => new QueryInterface ( queryInterface ) . dropMaterializedView ( materializedViewName )
}Si no usa la herramienta de migración de secuelas, no dude en crear la vista materializada de la manera que prefiera.
Defina el modelo de su vista materializada de la misma manera que define cualquier otro modelos de secuelas. La única diferencia es que debe agregar la propiedad referenceModel a las opciones de definición de su modelo. Luego, simplemente construya un SearchModel del modelo de vista materializado que acaba de definir.
let SearchModel = require ( "pg-search-sequelize" ) ;
let FilmMaterializedView = sequelize . define ( 'FilmMaterializedView' , {
name : DataTypes . STRING ,
rating : DataTypes . INTEGER ,
document : DataTypes . TEXT
} , {
referenceModel : models . Film // The model for which we're defining the materialized view
} ) ;
FilmMaterializedView = new SearchModel ( FilmMaterializedView ) ; // Adds search, searchByText, and refresh class methods to the model. Ahora puede llamar materializedViewModel.search(query, options) o materializedViewModel.searchByText(query) para ejecutar una búsqueda de texto completo en su modelo y sus asociaciones.
models . Film . searchByText ( "Mind" ) ; // Returns [ A Beautiful Mind, Eternal Sunshine of the Spotless Mind ]
// The following command searches for instances that match the search query,
// filters by those with releaseDate later than 2002, and orders the results by name field
models . Film . searchByText ( "Mind releaseDate:>2002 order:name" ) ; // Returns [ Eternal Sunshine of the Spotless Mind ]
// Or if you don't like strings, you can pass those properties in a JSON object
// The following returns the same as the code above; i.e. [ Eternal Sunshine of the Spotless Mind ]
models . Film . search ( "Mind" , {
where : {
releaseDate : { operator : ">" , value : "2002-01-01" }
} ,
order : [ [ "name" , "ASC" ] ]
}No olvide actualizar la vista materializada para actualizarlo con los últimos cambios realizados en su modelo. Una forma de hacerlo es crear un gancho AfterCreate, After Update y After Delete en su modelo para actualizar la vista materializada:
sequelize . define ( 'Film' , attributes , {
hooks : {
afterCreate : ( ) => FilmMaterializedView . refresh ( ) ,
afterUpdate : ( ) => FilmMaterializedView . refresh ( ) ,
afterDelete : ( ) => FilmMaterializedView . refresh ( )
}
} ) ;Alternativamente, puede tener un planificador de trabajo que actualice su vista materializada cada x cantidad de tiempo.
Dirígete a la sección de documentación para conocer qué tipo de búsquedas puedes hacer.
PG Search: Secelize tiene 2 clases, SearchModel y QueryInterface .
La clase SearchModel es lo que usaría para agregar los métodos de clase de búsqueda y actualización a su modelo de secuela de vista materializada. Para acceder a la clase SearchModel require("pg-search-sequelize") . Las siguientes funciones se pueden llamar al modelo que construye con la clase SearchModel MyModel = new SearchModel(MyModel)
Busque el modelo de vista materializado utilizando una cadena de consulta de búsqueda y un objeto Opciones.
query : la cadena de consulta de búsqueda.optionswhere - filtros para limitar los resultados por. /*
Format:
options.where = {
attribute: {
operator: ">, <, =, >=, <=, @@, ilike, etc.",
value: "some value"
}
*/
// Example:
options . where = {
releaseDate : {
operator : ">" ,
value : "2012-01-01"
}
}attributes : una matriz de los atributos para devolver. ex. options . attributes = [ "name" , "releaseDate" , "rating" ]order : una matriz de matrices con el primer valor es el nombre del atributo y la segunda es la dirección. Por ejemplo: options . order = [
[ "name" , "ASC" ] ,
[ "releaseDate" , "DESC" ] ,
[ "rating" , "DESC" ]
] Promise : una matriz de las instancias de resultados de búsqueda con los atributos especificados en el objeto Opciones, el defaultScope del modelo de vista materializado o todos los atributos en la definición del modelo de vista materializado.
Busque el modelo de vista materializado solo con una consulta de texto. Esto es especialmente útil para exponer un punto final de la API de búsqueda a su modelo para que no tenga que preocuparse por analizar la cadena de consulta de búsqueda.
query : una cadena del texto de consulta, filtros y campo para ordenar por. // --------------
// Simple search
// --------------
Film . searchByText ( "Beautiful" ) ; // WHERE to_tsquery('Beautiful') @@ document
// --------------
// Ordering
// --------------
// Search and order the results by rating in ascending order
Film . search ( "Beautiful order:rating" ) ; // WHERE to_tsquery('Beatiful') @@ document ORDER BY ts_rank(document, to_tsquery('Beautiful')), rating ASC
// Or to invert the order to descending order, prepend the field name by an exclamation point
Film . searchByText ( "Beautiful order:!rating" ) ; // WHERE to_tsquery('Beatiful') @@ document ORDER BY ts_rank(document, to_tsquery('Beautiful')), rating DESC
// --------------
// Filtering
// --------------
// Searches for a movie that has the text "Beautiful" in any of its fields and "brilliant mathematician" in the description.
Film . searchByText ( "Beatiful description:brilliant mathematician" ) ; // WHERE to_tsquery('Beatiful') @@ document AND description ILIKE %brilliant mathematician%
// You can also use comparison operators: =, >, <. >=, <=
Film . searchByText ( "Beautiful rating:>=7" ) // WHERE to_tsquery('Beautiful') @@ document AND rating >= 7
// If no operator is passed to the filter, an ILIKE operator is used. Just as seen in the first filtering example.
// If the field's type doesn't work with ILIKE, it is cast to TEXT.
Film . searchByText ( "Beautiful releaseDate:200" ) // WHERE to_tsquery('Beautiful') @@ document AND release_date::TEXT ILIKE 200 Promise : una matriz de las instancias de resultados de búsqueda con los atributos de Valor defaultScope del modelo de vista materializado, o todos los atributos en la definición del modelo de vista materializado.
Actualiza la vista materializada. ex. models.Film.afterCreate(() => MaterializedViews.Film.refresh())
La clase QueryInterface está destinada a ejecutar migraciones; es decir, creando y dejando caer la vista materializada. Para acceder a la clase de QueryInterface require("pg-search-sequelize").QueryInterface en sus funciones up y down , construya una instancia y pase a ella la secuela queryInterface :
let QueryInterface = require ( "pg-search-sequelize" ) . QueryInterface ;
module . exports = {
up : queryInterface => new QueryInterface ( queryInterface ) . createMaterializedView ( ... ) ,
down : queryInterface => new QueryInterface ( queryInterface ) . dropMaterializedView ( ... ) ,
} ; Crea una nueva vista materializada en la base de datos que tiene dos campos; ID y documento. El campo de documento es un ts_vector del texto concatenado de todos los atributos/campos especificados a buscar
name : el nombre de la vista materializadamodel : el modelo de la tabla para crear la vista materializada para.attributes : objeto de par de valor clave con la clave es el nombre del campo y el valor del peso del campo. attributes = {
name : "A" // "A" is the highest weight
description : "B" ,
release_date : "C"
city : "D" // "D" is the lowest possible weight
}options tableName : si se proporciona, anula el tableName del modelo aprobado
primaryKeyField : si se proporciona, anula el primaryKeyField del modelo aprobado
include : una variedad de objetos que definen los atributos de los modelos asociados para incluir en el documento de la vista materializada.
include = [
{
model : models . Actor ,
foreignKey : "actor_id" ,
target_key : "id" ,
associationType : "hasMany" ,
attribtues : {
first_name : "C" ,
last_name : "C"
} ,
include : [ ... ] // optionally you can include models associated to the Actor model
} ,
// ...
// Other associated models
]model : el modelo a incluirforeignKey : el KeyKey que apunta al modelo asociado. Tenga en cuenta que, según el tipo de asociación, la clave extranjera podría estar en el modelo de referencia (el modelo de película en el ejemplo anterior) o en el otro modelo (el modelo de actor).targetKey : la clave a la que hace referencia a Foreignkey.associationType : el tipo de asociación desde la perspectiva del modelo de referencia (película). Debe ser hasOne , hasMany o belongsTo .attributes : los atributos a incluir del modelo.include : una matriz de incluido de modelos asociados al modelo incluido (por ejemplo, modelos asociados al actor) Cae la vista materializada.
name : el nombre de la vista materializada