Visão geral
Neste guia, você pode aprender como usar agrupamentos com MongoDB para ordenar sua query ou resultados de operação de agregação por valores de string. Um agrupamento é um conjunto de regras de ordenação e correspondência de caracteres que se aplicam a um idioma e uma localidade específicos.
Você pode aprender mais sobre agrupamentos nas seguintes seções deste guia:
Importante
Biblioteca do Reator do Projeto
Este guia usa a biblioteca Project Reactor para consumir instâncias do Publisher retornadas pelos métodos de driver Java Reactive Streams. Para saber mais sobre a biblioteca do Projeto Reactor e como usá-la, consulte Introdução na documentação do Reactor. Para saber mais sobre como usamos os métodos da biblioteca do Project Reactor neste guia, consulte o guia Gravar dados no MongoDB.
Agrupamentos no MongoDB
O MongoDB classifica strings utilizando agrupamento binário por padrão. O agrupamento binário usa o padrão ASCII de valores de caracteres para comparar e ordenar strings. Determinados idiomas e localidades possuem convenções específicas de ordenação de caracteres que diferem dos valores de caracteres ASCII.
Por exemplo, no francês canadense, o caractere mais acentuado à direita (diacrítico) determina a ordem das strings quando todos os caracteres anteriores são iguais. Considere as seguintes palavras em francês canadense:
cote
coté
côte
côté
Ao usar o agrupamento binário, o MongoDB os classifica na seguinte ordem:
cote coté côte côté
Ao usar o agrupamento francês canadense, o MongoDB os classifica na seguinte ordem:
cote côte coté côté
Como especificar agrupamentos
O MongoDB suporta agrupamentos na maioria das operações CRUD e agregações. Para obter uma lista completa das operações suportadas, consulte operação que suporta agrupamentos no manual do MongoDB Server.
Você pode especificar o código de localidade e a variante opcional no seguinte formato de string:
"<locale code>@collation=<variant code>"
O exemplo a seguir especifica o código de locale "de" e o código de variante "phonebook":
"de@collation=phonebook"
Se você não especificar uma variante, use apenas o código de locale.
Para obter uma lista completa de locale suportados, consulte linguagem e locale suportados no manual do MongoDB Server.
As seções a seguir mostram maneiras diferentes de aplicar agrupamentos no MongoDB:
collection
Você só pode definir um agrupamento padrão para uma coleção durante a criação. No entanto, você pode especificar um agrupamento em um novo índice em uma coleção existente. Todas as operações compatíveis que verificam a coleção então aplicam o agrupamento padrão. Consulte a seção Índice deste guia para obter mais informações.
O exemplo a seguir mostra como especificar o agrupamento de localidade "en_US" ao criar uma nova coleção chamada items:
Mono.from(database.createCollection( "items", new CreateCollectionOptions().collation( Collation.builder().locale("en_US").build()))) .block();
Para verificar se você criou o agrupamento com sucesso, recupere uma lista dos índices nessa coleção da seguinte maneira:
List<Document> indexes = Flux.from(itemsCollection.listIndexes()) .collectList().block(); if (indexes != null) { indexes.forEach(idx -> System.out.println(idx.toJson())); }
A saída do código anterior deve conter o seguinte:
{ ... "collation": { "locale": "en_US", ... } ... }
Index
Você pode especificar um agrupamento ao criar um novo índice em uma coleção. O índice armazena documentos na ordem especificada, eliminando a necessidade de classificação na memória durante as query. Para usar o índice, a operação deve usar o mesmo agrupamento especificado no índice e ser coberta por esse índice.
O exemplo a seguir mostra como criar um índice no campo "nome" com o agrupamento de localidade "en_US" em ordem crescente:
IndexOptions idxOptions = new IndexOptions(); idxOptions.collation(Collation.builder().locale("en_US").build()); Mono.from(itemsCollection.createIndex( Indexes.ascending("name"), idxOptions)).block();
Para verificar se você criou o agrupamento com sucesso, recupere uma lista dos índices nessa coleção da seguinte maneira:
List<Document> indexes = Flux.from(itemsCollection.listIndexes()) .collectList().block(); if (indexes != null) { indexes.forEach(idx -> System.out.println(idx.toJson())); }
A saída do código anterior deve conter o seguinte:
{ ... "collation": { "locale": "en_US", ... } ... }
O exemplo a seguir mostra uma operação que especifica o mesmo agrupamento e é coberta pelo índice criado no exemplo anterior:
FindPublisher<Document> indexPublisher = itemsCollection.find() .collation(Collation.builder().locale("en_US").build()) .sort(Sorts.ascending("name")); Flux.from(indexPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
(operação)
Você pode substituir o agrupamento padrão passando um novo agrupamento para uma operação suportada. No entanto, sem um índice, sua query executa a classificação na memória, o que é mais lento do que usar um agrupamento indexado. Para obter mais informações sobre as desvantagens das operações de classificação não cobertas por um índice, consulte Usar índices para classificar resultados de query no manual do MongoDB Server.
O exemplo a seguir mostra uma operação de query com as seguintes características:
A coleção referenciada tem um índice de agrupamentos
"en_US"padrão, semelhante ao especificado na seção Coleção.A query especifica o agrupamento islandês (
"is"). Como isso difere do agrupamento de índice, a query não usa o índice e, em vez disso, executa uma classificação na memória.
FindPublisher<Document> customPublisher = itemsCollection.find() .collation(Collation.builder().locale("is").build()) .sort(Sorts.ascending("name")); Flux.from(customPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
Tipos de índice que não suportam agrupamentos
A maioria dos tipos de índice do MongoDB oferece suporte a agrupamentos. No entanto, os seguintes tipos oferecem suporte apenas à comparação binária e não oferecem suporte a agrupamentos:
Opções de agrupamento
Esta seção aborda várias opções de agrupamento e como especificá-las para refinar ainda mais o comportamento de ordenação e correspondência.
Opção de agrupamento | Descrição |
|---|---|
localidade | Obrigatório. O código de locale ICU para linguagem e variante. |
de trás para frente | Especifica se os diacríticos devem ser considerados a partir do final da string primeiro. |
Sensibilidade a maiúsculas e minúsculas | Especifica se deve considerar maiúsculas e minúsculas como valores diferentes. |
alternar | Especifica se os espaços e a pontuação devem ser considerados. |
caseFirst | Especifica se deve considerar primeiro maiúsculas ou minúsculas. |
Variável máxima | Especifica se deve ignorar espaços em branco ou espaços em branco e pontuação. Essa configuração só é válida quando a configuração alternativa é "deslocada". |
força | Especifica o nível de comparação da ICU. O valor padrão é "terciário". Para obter mais informações sobre cada nível, consulte os níveis de comparação da ICU. |
normalização | Especifica se a normalização unicode deve ser executada no texto conforme necessário. Para obter mais informações sobre a normalização unicode, consulte Formulários de normalização Unicode. |
numericOrdering | Especifica se os números devem ser ordenados de acordo com o valor numérico em vez da ordem de agrupamento. |
Você pode usar a classe Collation.Builder para especificar valores para as opções de agrupamentos anteriores. Chame o método build() para construir um objeto Collation conforme mostrado no exemplo a seguir:
Collation.builder() .caseLevel(true) .collationAlternate(CollationAlternate.SHIFTED) .collationCaseFirst(CollationCaseFirst.UPPER) .collationMaxVariable(CollationMaxVariable.SPACE) .collationStrength(CollationStrength.SECONDARY) .locale("en_US") .normalization(false) .numericOrdering(true) .build();
Para obter mais informações sobre os métodos e parâmetros correspondentes, consulte a documentação da API para agrupamentos.construtor.
Exemplos de agrupamento
Esta seção contém exemplos de como usar operações do MongoDB que suportam agrupamentos. Para cada exemplo, suponha que você comece com a seguinte coleção de documentos:
{ "_id" : 1, "first_name" : "Klara" } { "_id" : 2, "first_name" : "Gunter" } { "_id" : 3, "first_name" : "Günter" } { "_id" : 4, "first_name" : "Jürgen" } { "_id" : 5, "first_name" : "Hannah" }
Os exemplos a seguir usam o locale "de@collation=phonebook" e os agrupamentos de variantes. A parte "de" do agrupamento especifica a locale alemã e a parte "collation=phonebook" especifica uma variante. O agrupamento de locale "de" contém regras para priorizar nomes próprios, identificados pela capitalização da primeira letra. Na variante "collation=phonebook", os caracteres com umlauts são ordenados antes dos mesmos caracteres sem eles em uma classificação crescente.
Exemplo de find() e sort()
O exemplo a seguir mostra como aplicar um agrupamento ao recuperar resultados classificados de uma coleção. Para executar esta operação, chame find() na coleção de exemplo e encadeie os métodos collation() e sort() para especificar a ordem na qual você deseja receber os resultados.
FindPublisher<Document> findPublisher = phonebookCollection.find() .collation(Collation.builder() .locale("de@collation=phonebook").build()) .sort(Sorts.ascending("first_name")); Flux.from(findPublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
Quando você executa esta operação na coleção de exemplo, a saída se assemelha ao seguinte:
{"_id": 3, "first_name": "Günter"} {"_id": 2, "first_name": "Gunter"} {"_id": 5, "first_name": "Hannah"} {"_id": 4, "first_name": "Jürgen"} {"_id": 1, "first_name": "Klara"}
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:
Exemplo de findOneAndUpdate ()
O exemplo a seguir especifica um agrupamento em uma operação findOneAndUpdate() instanciando um objeto FindOneAndUpdateOptions e passando-o como um parâmetro. O exemplo executa as seguintes operações:
Recupera o primeiro documento na coleção de exemplo que precede "Gunter" em ordem crescente.
Define as opções para a operação, incluindo o agrupamento
"de@collation=phonebook".Adiciona um novo campo "verified" com o valor "true".
Recupera e imprime o documento atualizado.
Document updatedDoc = Mono.from( phonebookCollection.findOneAndUpdate( Filters.lt("first_name", "Gunter"), Updates.set("verified", true), new FindOneAndUpdateOptions() .collation(Collation.builder() .locale("de@collation=phonebook") .build()) .sort(Sorts.ascending("first_name")) .returnDocument(ReturnDocument.AFTER))) .block(); if (updatedDoc != null) { System.out.println("Updated document: " + updatedDoc.toJson()); }
Como "Günter" vem lexicalmente antes de "Gunter" usando o agrupamento de@collation=phonebook em ordem crescente, a operação anterior retorna o seguinte documento:
Updated document: {"_id": 3, "first_name": "Günter", "verified": true}
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:
Exemplo de findOneAndDelete()
O exemplo a seguir especifica um agrupamento de ordenação numérica em uma operação findOneAndDelete() instanciando um objeto FindOneAndDeleteOptions e passando-o como parâmetro. A coleção contém os seguintes documentos:
{ "_id" : 1, "a" : "16 apples" } { "_id" : 2, "a" : "84 oranges" } { "_id" : 3, "a" : "179 bananas" }
O agrupamento define a opção locale como "en" e a opção numericOrdering como "true" para classificar as strings com base em seu valor numérico.
Document deletedDoc = Mono.from( numericalCollection.findOneAndDelete( Filters.gt("a", "100"), new FindOneAndDeleteOptions() .collation(Collation.builder() .locale("en") .numericOrdering(true) .build()) .sort(Sorts.ascending("a")))) .block(); if (deletedDoc != null) { System.out.println("Deleted document: " + deletedDoc.toJson()); }
Depois de executar a operação anterior, seu resultado será semelhante ao seguinte:
Deleted document: {"_id": 3, "a": "179 bananas"}
O valor numérico da string "179" é maior que 100, portanto, o documento anterior é a única correspondência. Sem ordem numérica, a agrupamentos binária classifica "100" antes de "16", "84" e "179", portanto, o filtro corresponde a todos os documentos.
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API:
Exemplo de agregação
O exemplo a seguir mostra como especificar um agrupamento em uma operação de agregação. Para executar uma agregação, chame o método aggregate() em um objeto MongoCollection.
Para especificar um agrupamento para uma operação de agregação, chame o método collation() no AggregatePublisher retornado pela operação de agregação. Especifique um estágio de agregação de classificação em seu pipeline para aplicar o agrupamento.
O exemplo a seguir constrói um pipeline de agregação na coleção de exemplo e aplica um agrupamento especificando o seguinte:
Um estágio de agregação de grupo usando
Aggregates.group()para identificar cada documento pelo campofirst_namee usar esse valor como_iddo resultado.Um acumulador no estágio de grupo para somar o número de instâncias de valores correspondentes no campo
first_name.Uma classificação ascendente no campo
_iddos documentos de saída.Um objeto de agrupamento especificando a locale alemã e uma força de agrupamento que ignora acentos e umlauts.
Bson groupStage = Aggregates.group( "$first_name", Accumulators.sum("nameCount", 1)); Bson sortStage = Aggregates.sort(Sorts.ascending("_id")); AggregatePublisher<Document> aggregatePublisher = phonebookCollection .aggregate(Arrays.asList(groupStage, sortStage)) .collation(Collation.builder() .locale("de") .collationStrength(CollationStrength.PRIMARY) .build()); Flux.from(aggregatePublisher) .doOnNext(doc -> System.out.println(doc.toJson())) .blockLast();
O código anterior gera os seguintes documentos:
{"_id": "Gunter", "nameCount": 2} {"_id": "Hannah", "nameCount": 1} {"_id": "Jürgen", "nameCount": 1} {"_id": "Klara", "nameCount": 1}
Para obter mais informações sobre os métodos e as classes mencionadas nesta seção, consulte a seguinte documentação da API: