mongoose-fuzzy-searching é um plugin simples e leve que permite pesquisas difusas em documentos no MongoDB. Este código é baseado neste artigo.
Instalar usando npm
$ npm i mongoose-fuzzy-searching
ou usando fio
$ yarn add mongoose-fuzzy-searching
Antes de começar, para melhores práticas e evitar quaisquer problemas, trate corretamente todos os Avisos de Depreciação.
Para permitir que o plugin crie os índices, você precisa definir useCreateIndex
como true. O exemplo abaixo demonstra como se conectar ao banco de dados.
const options = {
useNewUrlParser : true ,
useUnifiedTopology : true ,
useFindAndModify : false ,
useCreateIndex : true ,
} ;
mongoose . Promise = global . Promise ;
return mongoose . connect ( URL , options ) ;
No exemplo abaixo, temos uma coleção User
e queremos fazer uma pesquisa difusa em firstName
e lastName
.
const { Schema } = require ( 'mongoose' ) ;
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
email : String ,
age : Number ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , { fields : [ 'firstName' , 'lastName' ] } ) ;
const User = mongoose . model ( 'User' , UserSchema ) ;
module . exports = { User } ;
const user = new User ( { firstName : 'Joe' , lastName : 'Doe' , email : '[email protected]' , age : 30 } ) ;
try {
await user . save ( ) ; // mongodb: { ..., firstName_fuzzy: [String], lastName_fuzzy: [String] }
const users = await User . fuzzySearch ( 'jo' ) ;
console . log ( users ) ;
// each user object will not contain the fuzzy keys:
// Eg.
// {
// "firstName": "Joe",
// "lastName": "Doe",
// "email": "[email protected]",
// "age": 30,
// "confidenceScore": 34.3 ($text meta score)
// }
} catch ( e ) {
console . error ( e ) ;
}
Os resultados são classificados pela chave confidenceScore
. Você pode substituir esta opção.
try {
const users = await User . fuzzySearch ( 'jo' ) . sort ( { age : - 1 } ) . exec ( ) ;
console . log ( users ) ;
} catch ( e ) {
console . error ( e ) ;
}
As opções podem conter fields
e middlewares
.
O atributo Fields é obrigatório e deve ser um array de Strings
ou um array de Objects
.
Se quiser usar as opções padrão para todos os seus campos, basta passá-las como uma string.
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
email : String ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , { fields : [ 'firstName' , 'lastName' ] } ) ;
Caso queira substituir qualquer uma das opções padrão para seus argumentos, você pode adicioná-los como um objeto e substituir qualquer um dos valores que desejar. A tabela abaixo contém as chaves esperadas para este objeto.
chave | tipo | padrão | descrição |
---|---|---|---|
nome | Corda | nulo | Nome da chave de coleção |
tamanho mínimo | Inteiro | 2 | Tamanho mínimo de N gramas. Saiba mais sobre N-gramas |
peso | Inteiro | 1 | Denota a importância do campo em relação aos outros campos indexados em termos da pontuação da pesquisa de texto. Saiba mais sobre pesos de índice |
somente prefixo | Booleano | falso | Retorna apenas ngrams do início do Word. (Dá resultados mais precisos) |
escaparPersonagens Especiais | Booleano | verdadeiro | Remova caracteres especiais de N-gramas. |
chaves | Matriz[String] | nulo | Se o tipo do atributo da coleção for Object ou [Object] (ver exemplo), você pode definir quais atributos serão usados para pesquisa difusa |
Exemplo:
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
email : String ,
content : {
en : String ,
de : String ,
it : String
}
text : [
{
title : String ,
description : String ,
language : String ,
} ,
] ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , {
fields : [
{
name : 'firstName' ,
minSize : 2 ,
weight : 5 ,
} ,
{
name : 'lastName' ,
minSize : 3 ,
prefixOnly : true ,
} ,
{
name : 'email' ,
escapeSpecialCharacters : false ,
} ,
{
name : 'content' ,
keys : [ 'en' , 'de' , 'it' ] ,
} ,
{
name : 'text' ,
keys : [ 'title' , 'language' ] ,
} ,
] ,
} ) ;
Middlewares é um Object
opcional que pode conter pre
-middlewares customizados. Este plugin está usando esses middlewares para criar ou atualizar os elementos difusos. Isso significa que se você adicionar pre
-middlewares, eles nunca serão chamados, pois o plugin os substitui. Para evitar esse problema, você pode passar seus midlewares personalizados para o plugin. Seus middlewares serão chamados primeiro . Os middlewares que você pode passar são:
schema.pre("save", ...)
schema.pre("insertMany", ...)
schema.pre("update", ...)
schema.pre("updateOne", ...)
schema.pre("findOneAndUpdate", ...)
schema.pre("updateMany", ...)
Se quiser adicionar algum dos middlewares acima, você pode adicioná-lo diretamente no plugin.
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , {
fields : [ 'firstName' ] ,
middlewares : {
preSave : function ( ) {
// do something before the object is saved
} ,
} ,
} ) ;
Middlewares também podem ser funções assíncronas:
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , {
fields : [ 'firstName' ] ,
middlewares : {
preUpdateOne : async function {
// do something before the object is updated (asynchronous)
}
}
} ) ;
A consulta de pesquisa difusa pode ser usada como função static
ou como helper
, o que permite encadear várias consultas. O nome da função em ambos os casos é surpresa, surpresa, fuzzySearch
.
O método de instância pode aceitar até três parâmetros. A primeira é a consulta, que pode ser String
ou Object
. Este parâmetro é obrigatório . O segundo parâmetro pode ser um Object
que contém quaisquer consultas adicionais (por exemplo age: { $gt: 18 }
) ou uma função de retorno de chamada. Se o segundo parâmetro forem as consultas, o terceiro parâmetro será a função de retorno de chamada. Se você não definir uma função de retorno de chamada, os resultados serão retornados dentro de uma promessa.
A tabela abaixo contém as chaves esperadas para o primeiro parâmetro (se for um objeto)
chave | tipo | surdo | descrição |
---|---|---|---|
consulta | Corda | nulo | String para pesquisar |
tamanho mínimo | Inteiro | 2 | Tamanho mínimo de N gramas. |
somente prefixo | Booleano | falso | Retorna apenas ngrams do início do Word. (Dá resultados mais precisos) o prefixo |
exato | Booleano | falso | Corresponde a uma frase, em oposição a termos individuais |
Exemplo:
/* With string that returns a Promise */
User . fuzzySearch ( 'jo' ) . then ( console . log ) . catch ( console . error ) ;
/* With additional options that returns a Promise */
User . fuzzySearch ( { query : 'jo' , prefixOnly : true , minSize : 4 } )
. then ( console . log )
. catch ( console . error ) ;
/* With additional queries that returns a Promise */
User . fuzzySearch ( 'jo' , { age : { $gt : 18 } } )
. then ( console . log )
. catch ( console . error ) ;
/* With string and a callback */
User . fuzzySearch ( 'jo' , ( err , doc ) => {
if ( err ) {
console . error ( err ) ;
} else {
console . log ( doc ) ;
}
} ) ;
/* With additional queries and callback */
User . fuzzySearch ( 'jo' , { age : { $gt : 18 } } , ( err , doc ) => {
if ( err ) {
console . error ( err ) ;
} else {
console . log ( doc ) ;
}
} ) ;
Você também pode usar a consulta como uma função auxiliar, que é como métodos de instância, mas para consultas mongoose. Os métodos auxiliares de consulta permitem estender a API do construtor de consultas encadeada do mongoose.
O auxiliar de consulta pode aceitar até dois parâmetros. A primeira é a consulta, que pode ser String
ou Object
. Este parâmetro é obrigatório . O segundo parâmetro pode ser um Object
que contém quaisquer consultas adicionais (por exemplo, age: { $gt: 18 }
), que é opcional. Esses auxiliares não aceitam uma função de retorno de chamada. Se você passar uma função, ocorrerá um erro. Mais sobre ajudantes de consulta.
Exemplo:
const user = await User . find ( { age : { $gte : 30 } } )
. fuzzySearch ( 'jo' )
. exec ( ) ;
O plugin cria índices para os campos selecionados. No exemplo abaixo, os novos índices serão firstName_fuzzy
e lastName_fuzzy
. Além disso, cada documento terá os campos firstName_fuzzy
[String] e lastName_fuzzy
[String]. Essas matrizes conterão os anagramas dos campos selecionados.
const mongoose_fuzzy_searching = require ( 'mongoose-fuzzy-searching' ) ;
const UserSchema = new Schema ( {
firstName : String ,
lastName : String ,
email : String ,
age : Number ,
} ) ;
UserSchema . plugin ( mongoose_fuzzy_searching , { fields : [ 'firstName' , 'lastName' ] } ) ;
Em outras palavras, este plugin cria anagramas quando você cria ou atualiza um documento. Todos os documentos pré-existentes não conterão essas matrizes difusas, portanto, a função fuzzySearch
não será capaz de encontrá-los.
Para criar anagramas para documentos pré-existentes, você deve atualizar cada documento. O exemplo abaixo atualiza o atributo firstName
para todos os documentos da coleção User
.
const cursor = Model . find ( ) . cursor ( ) ;
cursor . next ( function ( error , doc ) {
const obj = attrs . reduce ( ( acc , attr ) => ( { ... acc , [ attr ] : doc [ attr ] } ) , { } ) ;
return Model . findByIdAndUpdate ( doc . _id , obj ) ;
} ) ;
No exemplo anterior, definimos firstName
e lastName
como atributos difusos. Se você remover o firstName
dos campos difusos, o array firstName_fuzzy
não será removido pela coleção. Se você quiser remover a matriz de cada documento, será necessário cancelar a definição desse valor.
const cursor = Model . find ( ) . cursor ( ) ;
cursor . next ( function ( error , doc ) {
const $unset = attrs . reduce ( ( acc , attr ) => ( { ... acc , [ ` ${ attr } _fuzzy` ] : 1 } ) , { } ) ;
return Model . findByIdAndUpdate ( data . _id , { $unset } , { new : true , strict : false } ) ;
} ) ;
Usamos jest para todos os nossos testes unitários e de integração.
$ npm test
Nota: isso executará todos os conjuntos em série para evitar múltiplas conexões simultâneas no banco de dados.
Isso executará os testes usando um banco de dados de memória. Se você desejar, por algum motivo, executar os testes usando uma conexão real em uma instância mongo, adicione a variável de ambiente MONGO_DB
:
$ docker run --name mongo_fuzzy_test -p 27017:27017 -d mongo
$ MONGO_DB=true npm test
$ npm run test:unit
$ npm run test:integration
Licença MIT
Copyright (c) 2019 Vassilis Pallas
É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e dos arquivos de documentação associados (o "Software"), para negociar o Software sem restrições, incluindo, sem limitação, os direitos de usar, copiar, modificar, mesclar , publicar, distribuir, sublicenciar e/ou vender cópias do Software e permitir que as pessoas a quem o Software seja fornecido o façam, sujeito às seguintes condições:
O aviso de direitos autorais acima e este aviso de permissão serão incluídos em todas as cópias ou partes substanciais do Software.
O SOFTWARE É FORNECIDO "COMO ESTÁ", SEM GARANTIA DE QUALQUER TIPO, EXPRESSA OU IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIALIZAÇÃO, ADEQUAÇÃO A UM DETERMINADO FIM E NÃO VIOLAÇÃO. EM HIPÓTESE ALGUMA OS AUTORES OU DETENTORES DE DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER RECLAMAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, ATO ILÍCITO OU DE OUTRA FORMA, DECORRENTE DE, OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTRAS NEGOCIAÇÕES NO SOFTWARE.