Не стесняйтесь захватывать любые кусочки кода, которые вы найдете полезными. Или, что еще лучше, вы более чем можете стать сопровождающим! Мои самые глубокие искренние извинения за то, что отпустили этот проект. Я надеюсь, что это не доставит вам неудобств.
Полнотекстовый поиск Postgres в node.js с использованием Scentize в качестве его ORM.
Проверьте пример кода.
В этой библиотеке используется полнотекстовый поиск Postgres и материализованные представления для запуска быстрых, мощных и простых поисковых запросов. Сначала он создает материализованный взгляд, объединяя все значения в полях, которые вы хотите сделать, и преобразует их в ts_vector . Затем он добавляет методы searchByText и search в вашу модель Develize, которая позволяет запускать поиск только из строки запроса, или строку запроса и объект параметров JSON. Он также добавляет метод класса refresh , чтобы обновить материализованный представление, когда обновляются в вашу модель или всякий раз, когда вы захотите.
Например, давайте предположим, что у нас есть таблица баз данных фильмов, которая имеет:
| идентификатор | имя | описание | город | Дата выпуска | рейтинг |
|---|---|---|---|---|---|
| 1 | Беглец | Другой парень убегает из тюрьмы | Чикаго | 1993-08-06 | 8 |
| 2 | Красивый ум | Фильм о математике | Нью-Йорк | 2001-12-05 | 8 |
| 3 | Чикаго | Хороший старый американский мюзикл | Торонто | 2002-12-10 | 7 |
| 4 | Вечное солнце чистого разума | Джим Керри, когда он не комик | Нью-Йорк | 2004-03-19 | 8 |
Мы хотим разрешить поиск по имени, описанию и городу и фильтрации по рейтингу и release_date. Мы также хотим сортировать результаты по релевантности, придавая поле именем более высокий вес, чем описание и город. Так что, если вы запускаете поиск:
Film . searchByText ( "Chicago" ) ; // Returns [ Chicago, The Fugitive ]Результаты со словом «Чикаго» в заголовке появляются в том же словом в описании или городе. Таким образом, фильм Чикаго появится первым, а беглец будет вторым.
Мы также можем добавить фильтрацию и упорядочение определенным полем вместо того, чтобы заказать, релевантность, сделав поисковый запрос:
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 ]Вы также не ограничиваются только областями одной модели; Вы также можете включить поля из ассоциированных моделей. Например, если у нас была другая модель в нашей базе данных под названием Actor, которая связана с фильмом иностранным ключом, мы можем включить поля из модели актера, чтобы быть в материализованном представлении фильма. Это позволяет нам запускать поиски, такие как:
Film . searchByText ( "Tom Hanks" ) ; // Returns Tom Hanks moviesЕсть еще кое -что, что вы можете сделать с этой библиотекой. Для получения дополнительной информации о том, как его установить и использовать, ознакомьтесь с разделами установки и использования. Для документации ознакомьтесь с разделом документации.
NPM Установите пакет pg-search-sequelize
npm i --save pg-search-sequelizeЗатем потребуйте его в вашем файле определения Materalized View Model и передайте его модели Scentize, чтобы сделать ее доступной для поиска:
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.Пожалуйста, обратитесь к использованию, чтобы определить вашу модель и как создать материализованный вид.
Теперь, когда у вас появится, что позволяет эта библиотека в конце, давайте перейдем к шагам настройки:
Если вы используете инструмент Secuteelize Migrations, вы можете использовать вспомогательную функцию createMaterializedView(name, referenceModel, attributes, options) предоставленная классом 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 )
}Если вы не используете инструмент миграции Scentize, не стесняйтесь создавать материализованный вид, любым способом, который вы предпочитаете.
Определите модель вашего материализованного представления так же, как вы определяете любые другие модели «Стипение». Единственное отличие состоит в том, что вам нужно добавить свойство referenceModel в варианты определения модели. Затем просто построите SearchModel модель из только что определили, которую вы только что определили.
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. Теперь вы можете позвонить в materializedViewModel.search(query, options) или materializedViewModel.searchByText(query) чтобы запустить полнотекстовый поиск по вашей модели и ее ассоциациям.
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" ] ]
}Не забудьте обновить материализованный вид, чтобы обновить его с помощью последних изменений, внесенных в вашу модель. Один из способов сделать это - создать последующее Create, AfterUpdate, и крючок после выполнения вашей модели, чтобы обновить материализованный вид:
sequelize . define ( 'Film' , attributes , {
hooks : {
afterCreate : ( ) => FilmMaterializedView . refresh ( ) ,
afterUpdate : ( ) => FilmMaterializedView . refresh ( ) ,
afterDelete : ( ) => FilmMaterializedView . refresh ( )
}
} ) ;В качестве альтернативы, у вас может быть планировщик работы, который обновляет ваш материализованный просмотр каждые X количество времени.
Перейдите в раздел документации, чтобы узнать, какие поиски вы можете сделать.
PG Search - Scentize имеет 2 класса, SearchModel и QueryInterface .
Класс SearchModel - это то, что вы бы использовали для добавления методов поиска и обновления класса в вашу модель «Материализированное представление». Чтобы получить доступ к классу SearchModel require("pg-search-sequelize") . Следующие функции могут быть вызваны из модели, которую вы строите с классом SearchModel MyModel = new SearchModel(MyModel)
Поиск материализованной модели представления с использованием строки поискового запроса и объекта параметров.
query - строка поискового запроса.optionswhere - фильтры, чтобы ограничить результаты. /*
Format:
options.where = {
attribute: {
operator: ">, <, =, >=, <=, @@, ilike, etc.",
value: "some value"
}
*/
// Example:
options . where = {
releaseDate : {
operator : ">" ,
value : "2012-01-01"
}
}attributes - массив атрибутов для возврата. бывший. options . attributes = [ "name" , "releaseDate" , "rating" ]order - массив массивов с первым значением - имя атрибута, а второе - направление. Например: options . order = [
[ "name" , "ASC" ] ,
[ "releaseDate" , "DESC" ] ,
[ "rating" , "DESC" ]
] Promise - массив экземпляров результатов поиска с атрибутами, указанными в объекте «Параметры», defaultScope модели материализованного представления или все атрибуты в определении модели материализованного представления.
Поиск материализованную модель просмотра только с текстовым запросом. Это особенно полезно для выявления конечной точки API поиска вашей модели, чтобы вам не приходилось беспокоиться о анализе строки поискового запроса.
query - строка текста запроса, фильтров и поля на заказ. // --------------
// 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 - массив экземпляров результатов поиска с атрибутами defaultScope материализованной модели представления или всех атрибутов в определении модели материализованного представления.
Обновляет материализованный вид. бывший. models.Film.afterCreate(() => MaterializedViews.Film.refresh())
Класс QueryInterface предназначен для запуска миграций; т.е. Создание и отбрасывание материализованного представления. Чтобы получить доступ к требованию QueryInterface Class require("pg-search-sequelize").QueryInterface queryInterface ваших функциях up и down
let QueryInterface = require ( "pg-search-sequelize" ) . QueryInterface ;
module . exports = {
up : queryInterface => new QueryInterface ( queryInterface ) . createMaterializedView ( ... ) ,
down : queryInterface => new QueryInterface ( queryInterface ) . dropMaterializedView ( ... ) ,
} ; Создает новый материализованный представление в базе данных, которая имеет два поля; удостоверение личности и документ. Поле документа является ts_vector конкатенированного текста всех указанных атрибутов/полей, которые можно найти для поиска
name - Имя материализованного представленияmodel - модель таблицы для создания материализованного представления для.attributes - объект пары клавиш -значения с ключом, являющимся именем поля и значением веса поля. attributes = {
name : "A" // "A" is the highest weight
description : "B" ,
release_date : "C"
city : "D" // "D" is the lowest possible weight
}options tableName - если предоставлено, оно переопределяет tableName пройденной модели
primaryKeyField - Если предоставлено, он переопределяет primaryKeyField прошедшей модели
include - массив объектов, которые определяют атрибуты связанных моделей, которые включают в документ «Материализованный представление».
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 - модель для включенияforeignKey - иностранный, который указывает на связанную модель. Обратите внимание, что на основе типа ассоциации внешний ключ может быть на эталонной модели (модель пленки в примере выше) или на другой модели (модель актера).targetKey - ключ, на который ссылается иностранный.associationType - тип ассоциации с точки зрения справочной модели (фильма). Это должно быть hasOne , hasMany или belongsTo .attributes - атрибуты включают из модели.include - включайте массив моделей, связанных с включенной моделью (Ex. Модели, связанные с актером) Отбрасывает материализованный вид.
name - Имя материализованного представления