Consulta flexível com Atlas Search
Ethan Steininger3 min read • Published Oct 04, 2022 • Updated Jul 12, 2024
SNIPPET
Avalie esse Tutorial
Neste tutorial, mostrarei como a flexibilidade dos índices invertidos do Atlas Search é uma opção poderosa em comparação aos índices de árvore B tradicionais quando se trata de suporte a consultas ad hoc.
Os mecanismos de query flexíveis oferecem a capacidade de executar uma query de alto desempenho que abrange vários índices em seu armazenamento de dados. Isso significa que você pode escrever queries ad-hoc e geradas dinamicamente, onde você não precisa conhecer a query, os campos ou a ordem dos campos com antecedência.
É muito raro o planejador de query do MongoDB selecionar um plano que envolva vários índices. Neste tutorial, percorreremos um cenário em que isso se torna um requisito.
Digamos que você tenha um aplicativo de filmes com documentos como:
1 { 2 "title": "Fight Club", 3 "year": 1999, 4 "imdb": { 5 "rating": 8.9, 6 "votes": 1191784, 7 "id": 137523 8 }, 9 "cast": [ 10 "Edward Norton", 11 "Brad Pitt" 12 ] 13 }
Agora, para a versão 1.0 aplicativo, você precisa consultar o título e o ano, portanto, primeiro crie um índice composto por meio de:
db.movies.createIndex( { "title": 1, "year": 1 } )
Então emita a query:
db.movies.find({"title":"Fight Club", "year":1999})
Ao executar um plano de explicação, você tem uma consulta perfeita com uma 1:1 proporção de documentos examinados para documentos retornados:
1 { 2 "executionStats": { 3 "executionSuccess": true, 4 "nReturned": 1, 5 "executionTimeMillis": 0, 6 "totalKeysExamined": 1, 7 "totalDocsExamined": 1 8 } 9 }
Agora, nossos requisitos de aplicativo evoluíram e você precisa consultar no cast e no imdb. Primeiro você cria o índice:
db.movies.createIndex( { "cast": 1, "imdb.rating": 1 } )
Então emita a query:
db.movies.find({"cast":"Edward Norton", "imdb.rating":{ $gte:9 } })
Não é a maior proporção de documentos examinados para documentos retornados, mas ainda não é ruim:
1 { 2 "executionStats": { 3 "executionSuccess": true, 4 "nReturned": 7, 5 "executionTimeMillis": 0, 6 "totalKeysExamined": 17, 7 "totalDocsExamined": 17 8 } 9 }
Agora, nosso aplicativo exige que você emita uma nova consulta, que se torna um subconjunto do original:
db.movies.find({"imdb.rating" : { $gte:9 } })
A consulta acima resulta na temida varredura de coleção, apesar de o índice composto anterior (cast_imdb.rating) incluir a chave da consulta acima. Isso ocorre porque o campo "imdb.rating" não é o prefixo do índice, e a consulta não contém condições de filtro no campo "cast"."
Observação: as varreduras de coleção devem ser evitadas porque elas não apenas instruem o cursor a examinar todos os documentos da coleção que são lentos, mas também forçam os documentos a sair da memória, resultando em aumento da pressão de E/S.
Os resultados do nosso plano de query são os seguintes:
1 { 2 "executionStats": { 3 "executionSuccess": true, 4 "nReturned": 31, 5 "executionTimeMillis": 26, 6 "totalKeysExamined": 0, 7 "totalDocsExamined": 23532 8 } 9 }
Agora você realmente poderia criar um novo índice composto apenas por imdb.rating, que retornaria uma verificação de índice para a query acima, mas são três índices diferentes nos quais o planejador de queries teria que navegar para selecionar a resposta de melhor desempenho.
Como o Lucene usa uma estrutura de dados de índice diferente (índices invertidos versus índicesde árvore B), ele foi criado especificamente para executar queries que se sobrepõem em vários índices.
Ao contrário dos índices compostos, a ordem dos campos na definição do índice do Atlas Search não é importante. Os campos podem ser definidos em qualquer ordem. Portanto, ele não está sujeito à limitação acima, em que uma consulta que esteja apenas em um campo sem prefixo de um índice composto não pode usar o índice.
Se você criar um único índice que mapeie todos os nossos quatro campos acima (titulo, ano, elenco, imdb):
1 { 2 "mappings": { 3 "dynamic": false, 4 "fields": { 5 "title": { 6 "type": "string", 7 "dynamic": false 8 }, 9 "year": { 10 "type": "number", 11 "dynamic": false 12 }, 13 "cast": { 14 "type": "string", 15 "dynamic": false 16 }, 17 "imdb.rating": { 18 "type": "number", 19 "dynamic": false 20 } 21 } 22 } 23 }
Em seguida, você emite uma query que primeiro abrange o título e o ano por meio de uma cláusula obrigatória (E), que é equivalente a
db.collection.find({"title":"Fight Club", "year":1999})
:1 [{ 2 "$search": { 3 "compound": { 4 "must": [{ 5 "text": { 6 "query": "Fight Club", 7 "path": "title" 8 } 9 }, 10 { 11 "range": { 12 "path": "year", 13 "gte": 1999, 14 "lte": 1999 15 } 16 } 17 ] 18 } 19 } 20 }]
Os resultados correspondentes do planejador de query:
1 { 2 '$_internalSearchIdLookup': {}, 3 'executionTimeMillisEstimate': 6, 4 'nReturned': 0 5 }
Então, quando você adicionar
imdb
e cast
à query, ainda poderá obter resultados de desempenho:1 [{ 2 "$search": { 3 "compound": { 4 "must": [{ 5 "text": { 6 "query": "Fight", 7 "path": "title" 8 }, 9 { 10 "range": { 11 "path": "year", 12 "gte": 1999, 13 "lte": 1999 14 }, 15 { 16 "text": { 17 "query": "Edward Norton", 18 "path": "cast" 19 } 20 }, 21 { 22 "range": { 23 "path": "year", 24 "gte": 1999, 25 "lte": 1999 26 } 27 } 28 ] 29 } 30 } 31 }]
Os resultados correspondentes do planejador de query:
1 { 2 '$_internalSearchIdLookup': {}, 3 'executionTimeMillisEstimate': 6, 4 'nReturned': 0 5 }
Os aplicativos evoluem conforme as expectativas e os requisitos de nossos usuários. Para dar suporte à evolução dos requisitos de seus aplicativos, os índices padrão de árvore B simplesmente não podem evoluir na mesma taxa que um índice invertido.
Aqui estão vários exemplos em que as estruturas de dados de índice invertido do Atlas Search podem ser úteis, com links para material de referência:
- GraphQL: se o ponto de entrada do seu banco de dados é GraphQL, onde as queries são definidas pelo cliente, então você é um candidato ideal para índices invertidos
- Pesquisa avançada: você precisa expandir os critérios de filtragem da sua barra de pesquisa além de vários campos.
- Pesquisa de curinga: pesquisa em campos que correspondem a combinações de caracteres e curingas.
- Query ad-hoc: a necessidade de gerar dinamicamente queries sob demanda por nossos clientes.
- Passo a passo de código completo por meio de um Jupyter Notebook