2012-09-19 29 views
7

Tener una colección:Ordenar por colación en MongoDB

{"name": "a"}, 
{"name": "B"},  
{"name": "b"},  
{"name": "c"},  
{"name": "á"},  
{"name": "A"} 

ex. cómo ordenarlo en español caso insensible?

He intentado esto:

var abc = [{"name": "a"}, {"name": "B"}, {"name": "b"}, {"name": "c"}, {"name": "á"}, {"name": "A"}]; 
for (i in abc) db.abc.save(abc[i]); 

db.abc.find({},{"_id":0}).sort({"name":1}); 

salida es:

[ 
    { "name" : "A" }, 
    { "name" : "B" }, 
    { "name" : "a" }, 
    { "name" : "b" }, 
    { "name" : "c" }, 
    { "name" : "á" }, 
] 

resultado deseado:

[ 
    { "name" : "a" }, 
    { "name" : "á" }, 
    { "name" : "A" }, 
    { "name" : "b" }, 
    { "name" : "B" }, 
    { "name" : "c" } 
] 

Respuesta

10

Sé que este es un viejo hilo, pero creo que sería útil para responder de todos modos.

Definitivamente no desea hacer la clasificación en su aplicación, porque eso significa que tiene que obtener todos los documentos de la colección en la memoria para ordenarlos y devolver la ventana que desee. Si tu colección es enorme, entonces esto es extremadamente ineficiente. La base de datos debería hacer la clasificación y devolverle la ventana.

Pero, MongoDB no admite la ordenación sensible a la configuración regional, usted dice. ¿Cómo resuelves el problema? La magia es el concepto de "claves de clasificación".

Básicamente, digamos que tiene el alfabeto inglés/latino regular de "a" a "z". Lo que haría sería crear una asignación de clave de clasificación de "a" a "01" y de "b" a "02", etc., a "z" a "26". Es decir, asigna cada letra a un número en el orden de clasificación para ese idioma y luego codifica ese número como una cadena. Luego, asigna la cadena que deseas ordenar a este tipo de clave de orden. Por ejemplo, "abc" se convertiría en "010203". A continuación, añadir una propiedad a su documento con la clave de ordenación para una propiedad, y añadir el nombre de la propiedad con el nombre de la la configuración regional:

{ 
    name: "abc", 
    name_en: "010203" 
} 

Ahora se puede ordenar en el lenguaje "es" simplemente por la indexación en la propiedad "name_en" y utilice la ordenación simple MongoDB basada en inglés para selectores y rangos en lugar de la propiedad "nombre".

Ahora, digamos que tiene otra lengua loca "xx" donde el orden del alfabeto es "acb" en lugar de "abc".(Sí, hay lenguas que meterse con el orden del alfabeto latino de esa manera!) La clave de ordenación sería así:

{ 
    name: "abc", 
    name_en: "010203", 
    name_xx: "010302" 
} 

Ahora, todo lo que tiene que hacer es crear índices en Suite Sencilla y name_xx y utilice la clasificación MongoDB regular para ordenar correctamente en esas configuraciones regionales. Básicamente, las propiedades adicionales son proxies para clasificar en diferentes lugares.

Entonces, ¿dónde obtienes estas asignaciones, preguntas? Después de todo, no eres un experto en globalización, ¿verdad?

Bueno, si usa Java, C o C++, existen clases ya preparadas que hacen esta asignación por usted. En Java, use la clase Collator estándar o use la clase icu4j Collator. Si está utilizando C/C++, use la versión C/C++ de la función/clase Collator de la ICU. Para otros idiomas, estás fuera de suerte a menos que puedas encontrar una biblioteca que ya lo haga.

Éstos son algunos enlaces para ayudarle a encontrarlos:

El estándar Compaginadora biblioteca de Java: http://docs.oracle.com/javase/7/docs/api/java/text/Collator.html#getCollationKey(java.lang.String)

La clase C++ Compaginadora: http://icu-project.org/apiref/icu4c/classicu_1_1Collator.html#ae0bc68d37c4a88d1cb731adaa5a85e95

También puede hacer diferentes criterios de ordenación que le permiten ordene las mayúsculas y minúsculas según la configuración regional (sí, la asignación de mayúsculas y minúsculas es sensible a la configuración regional) y la insensibilidad a los acentos, y la variante Unicode insensible, o cualquier combinación de las anteriores. El único problema es que ahora tiene muchas propiedades que son paralelas a cada propiedad ordenable, y debe mantenerlas sincronizadas cuando actualice la propiedad de "nombre" base. Es un dolor en el que usted sabe qué, pero aún así, es mejor que hacer la clasificación en la aplicación o la capa de lógica de negocios.

También tenga cuidado con los cursores con rangos. En inglés, por ejemplo, simplemente ignoramos acentos en los personajes. Entonces, un "Ö" se ordena de la misma manera que "O" y aparecerá en el rango "M" a "Z". Pero, en sueco, los caracteres acentuados se ordenan después de "Z". Entonces, si haces un rango "M" - "Z", incluirás un montón de registros que comienzan con "Ö" que deberían estar allí en inglés, pero no en sueco.

Esto también tiene implicaciones en la fragmentación si divide en una propiedad de texto de un documento. Tenga cuidado con los rangos dentro de qué fragmento. Sería mejor fragmentar cosas que no son sensibles a la configuración regional, como los hashes.

+0

Entonces, con este ejemplo, ¿se detendría después de 5 o 6 caracteres y 0-fill en palabras más cortas? – Stephane

+0

No, una cadena más corta siempre "gana" la comparación. "abc" ordena antes de "abcdef" aunque tengan el mismo prefijo, por lo que "010203" debe ordenar antes de "010203040506". Las propiedades de la clave de clasificación se deben comparar como cadenas, no como números. –

+0

Ah, y las claves de clasificación que los collaters Java y C++ devuelven parecen muy diferentes a los ejemplos que di aquí. Acabo de usar "01", "02", etc. porque eran fáciles de entender. En Java, por ejemplo, el método getCollationKey() devuelve una matriz de enteros que contiene los elementos de intercalación de bits. Sugiero transformar esta matriz en una cadena de dígitos hexadecimales para que MongoDB pueda compararlos como cadenas con las reglas de comparación en inglés predeterminadas que funcionan bien en hexadecimal. –

1

Por desgracia no se puede hacer un caso tipo insensible embargo, a la derecha ahora ordena los retornos en orden "índice". Hay un billete abierto:

https://jira.mongodb.org/browse/SERVER-90

usted podría considerar saltarse el tipo de mongo, y hacerlo en su aplicación.

3

En este momento, MongoDB no implementa la intercalación.

Implementar el Unicode collation standard es la mejor manera de resolver eso.

Pero esto haría que la ordenación fuera más lenta y los índices más grandes. Entonces, por ahora, lo mejor es ordenar su aplicación.

2

Una solución fácil es crear un nuevo campo con texto convertido en caracteres ascii simples.

{ "name": "Ánfora", "name_sort": "anfora" } 
{ "name": "Óscar", "name_sort": "oscar" } 
{ "name": "Barça", "name_sort": "barc~a" } 
{ "name": "Niño", "name_sort": "nin~o" } 
{ "name": "¡Hola!", "name_sort": "hola!" } 
{ "name": "¿qué?", "name_sort": "que?" } 

Después, simplemente ordenar por 'name_sort'

+0

Algo así podría funcionar. Ordenar en la aplicación, como otros sugirieron, no es una alternativa viable cuando tienes millones de filas, por lo que uno debe crear un campo ordenable para una solución real. –

8

Aunque las otras respuestas aquí son correctas para las versiones 3.2.x y anteriores de MongoDB, comenzando en 3.4.0 puede "especificar intercalaciones para una colección o una vista, un índice u operaciones específicas que admitan la intercalación".

Full documentation for the feature is here.

+0

Esta sería la respuesta correcta ahora. Como MongoDB permite definir la intercalación al crear colecciones o crear vistas con la intercalación deseada. Por favor, eche un vistazo a esto: https://docs.mongodb.com/manual/reference/method/db.createCollection/#createcollection-collation-example –

+0

Definitivamente me rendiría en MongoDB ya que pensé que no había una manera fácil para usarlo con mi lengua materna, que es portugués (de Brasil). Pero esta parece una muy buena solución. –