Duda query mongodb con arrays

Lecherito

Buenas, ando liado con mongodb ahora y a ver si me podeis ayudar con una query que ya estoy hasta los huevos y no tengo ni idea.

Veamos, la collección tiene miles de documentos, y cada documento tiene una ID, y una SUB-ID por así llamarlo, del tipo

{
    id: 1,
    subid: 2,
    jugadores: [
        {
            monigote: "Pepito",
            raza: "Alienigena"
        },
        {
            monigote: "Juanito",
            raza: "Terrestre"
        },
        {
            monigote: "Mongo",
            raza: "Alienigena"
        }
    ],
    idpartida: 1234
}

Bueno, me dan la "idpartida" (o la que sea para encontrar el documento) y el caso es que he de buscar de esos jugadores, el que la raza sea "Terrestre" y sacar el NOMBRE.

He probado cosas del tipo:

db.coll.find($and [ { idpartida: 1234 }, { jugadores: { $elemMatch: { raza: "Terrestre" } } } ], { "jugadores.nombre" : 1})

Pero nanai, alguna idea?

S

Si lo he entendido bien deberia de ser asi:

db.collection.find({idpartida:miid, 'jugadores.raza':'Terrestre'},{'jugadores.monigote':1,_id:0})

1 respuesta
Lecherito

#2 El caso es que me salen todas las razas para esa id de la partida.

Aunque lo de _id:0 muy buen apunte, eso no lo sabía xD

Por ejemplo Sarwaz, si le añado al print { 'jugadores.raza' : 1 }, me salen todas las razas, cada una de su padre y de su madre xD

S

Creo que ahora entiendo mejor el problema, te esta devolviendo los documentos que tengan algún jugador con la raza 'Terrestre' y en ese documento vienen todos los jugadores pero tu solo quieres los que sean 'Terrestre'. Hasta donde yo se mongodb no permite devolver parcialmente un documento, asi que me parece que vas a tener que filtrar el resultado.

1 respuesta
Lecherito

#4 Vaya putada, de todas maneras al final por espacio creo que me va a tocar una base de datos relacional ya que la estructura de los documentos que tengo ahora mismo ocupa como el 50% de toda la base de datos y eso es un poco inaceptable xD

tOWERR

#1 ¿Por qué no lo haces mediante aggregate?

http://docs.mongodb.org/manual/reference/sql-aggregation-comparison/

db.collection.aggregate([
{$unwind: "$jugadores"},
{$group: {nombre_jugador: "$jugadores.monigote"}},
{$match: {{jugadores.raza: "Terrestre"},{id: 1}}}
]);

No sé si esto funcionará.

JuAn4k4

Con $unwind + $match en aggregation te vale, no tienes por qué agruparlos ya que los filtras por el id de la partida, donde el jugador solo estaría 1 vez (en teoría).

Si te ocupan mucho los documentos, prueba con nombres de atributos más cortos.

db.collection.aggregate([
{$unwind: "$jugadores"},
{$match: {idpartida: 1234, jugadores.raza: "Terrestre"}}
]);

Y el resultado sería algo así (2 resultados):

{id: 1,
  subid: 2,
  jugadores: 
        {
            monigote: "Juanito",
            raza: "Terrestre"
        },
  idpartida: 1234}


{id: 1,
  subid: 2,
  jugadores: 
        {
            monigote: "Menganito",
            raza: "Terrestre"
        },
  idpartida: 1234}

Si quieres luego le metes un $project para cambiar de "jugadores" a "jugador"

PD: Cuanto es el 50% ?

1 respuesta
Lecherito

#7 http://euw.leagueoflegends.com/tribunal/en/get_reform_game/3231/3/ <- este es el documento que estoy guardando.

Cosas del tipo "association_to_offender" creo que ocupan 1 byte por char si no estoy equivocado, los arrays se repiten mucho etc y no tengo ni idea de optimizar este tipo de bases de datos, así que quizá haga la conversión a una relacional.

17.7K de casos, 55k partidas ocupan 8GB, y eso me parece demasiado

Spacelord

Me parece mucho espacio para esa cantidad de registros, ¿no?

Y, de todas formas, la ventaja de MongoDB es el sharding (entre otras cosas), porque velocidad de acceso, para menos de 100.000 registros, no creo que sea determinante. Si no vas a shardear la BBDD no sé si te valdría mucho la pena usar MongoDB.

1 1 respuesta
Lecherito

#9 Claro, yo no sabía que me iba a ocupar tantísimo

1 respuesta
Spacelord

#10 Si no recuerdo mal (hace un año que no toco nada de Mongo) el tamaño default de la BBDD, si no lo especificas al crearla, es bastante grande. No recuerdo si eran 10GB o me estoy columpiando mucho, la verdad.

Si ves que tal, prueba esto a ver si te redimensiona el tablespace: http://stackoverflow.com/questions/2966687/reducing-mongodb-database-file-size

1 respuesta
B

#11

Al leer lo de los 10gb me he acojonado y he buscado información sobre ese tema, al parecer el pre-allocated son 64MB y el .ns inicial es de 16MB default.

Cada vez que se supere el limite del pre-allocated(64MB) que sería un .0, el siguiente .x se crearía con dos veces el tamaño del .0 hasta un limite de 2GB, si se supera este, todas las siguientes .x tendrían un tamaño de 2GB.

He encontrado la info en stackoverflow, cuando llegue a casa lo comprobaré para ver si no es una columpiada porque si es como tu dices me voy a plantear muy seriamente migrar a un mariaDB.

edit:

http://docs.mongodb.org/manual/faq/storage/#faq-disk-size

2 respuestas
Lecherito

#12 Es así, yo ya tengo 4x2GB 1x1GB 1x512MB etc.

Hice el repair y el compact, estuvo más de 2 horas de operaciones para dejarla como estaba xDD

1 respuesta
Spacelord

#12 Vale, pues entonces lo de los 10GB era columpiada mía. Sí que recuerdo que muchas quejas de MongoDB venían de gente que la usaba sin shardear, y por eso terminaban con un tablespace gordísimo.

#13 Si cuando digo que este SGBD es una mierda... XDDD

1 respuesta
B

#14 Si ahora mismo el espacio en HD es barato en las maquinas, pero después de descubrir esto ya toca valorar si merece la pena el supuesto rendimiento respecto a otras tecnologías para tener ese pedazo de monstruo comiendo disco duro para una cantidad de información que con otros SGBD ocuparía menos de la mitad.

1 respuesta
Spacelord

#15 El problema es que el NoSQL es la buzzword de moda y ahora todo el mundo quiere una DB NoSQL para su proyecto aunque no haga falta ni remotamente. NoSQL es para tablas con millones o decenas de millones de registros (de ahí para arriba), cientos de millones de peticiones concurrentes y tablespaces de tamaños terroríficos. Y no por el espacio, ojo: el problema es en la velocidad de proceso, en el tiempo que se tarda en procesar una query y entregar el resultado (imagina el tiempo que se tarda en procesar un Select * en una tabla con quince millones de tuplas y cincuenta dominios por tupla). Cosas como Twitter, Facebook, etc., no para una aplicación normal que se puede solucionar con una DB SQL de toda la vida. Y como MongoDB es la más conocida, tanto por verla en foros especializados como por gente que la despliega porque "es el SGBD de Twitter", blanco y en botella.

Ojo, no digo que no se pueda hacer por probar a ver como es, sino que hay gente que la exige para sus proyectos sin saber lo que es y no darse por aludido cuando se le dice que no es la mejor solución y que va a dar mogollón de problemas.

Lo que pasa es que MongoDB no garantiza ACID, con lo que puedes perder (y perderás) datos, coherencia interna y un montón de cosas más a no ser que monitorices la DB de una forma mucho más nazi que si tuvieras una SQL, que no necesita ni una décima parte del control. Si ya para un proyecto que necesite NoSQL la cosa va mal (haz la prueba: vete a tu muro de FB, pulsa F5 unas cuantas veces y verás como cambia el orden de las publicaciones una y otra vez), para uno que no lo necesita, y que depende vitalmente de la integridad referencial y la persistencia de datos, el desastre es completo. De ahí que mucha gente se queje sin motivo: si despliegas una DB que necesita ACID en un SGBD NoSQL te estás buscando tú mismo los problemas. Además, la mayor parte de gente que lo hace no sabe programar los tests de integridad que necesita una DB NoSQL y se encuentra con pérdidas irreparables de datos.

Resumiendo: NoSQL funciona (de aquella manera) en proyectos muy, muy grandes. Para cosas normales, SGDB relacional de toda la vida y a correr.

1 3 respuestas
Lecherito

#16 Realmente lo cogí por que es object oriented y json, y el response de la web es json por lo que no tendría que manipular para nada los datos. Otra cosa por lo que la elegí es por que son 1.4M * numerodepartidas de registros y no sé yo hasta qué punto pueda una MySQL aguantar tanto, además de que con los Players, habrá 1.4M10numerodepartidas.

1 respuesta
Spacelord

#17 Si ya te digo que por probar a ver no pasa nada, eso es saludable. En GW2, por ejemplo, la API oficial también usa JSON y mucha gente ha intentado montarse una app usando MongoDB (con erótico resultado). Pero, vamos, es lo normal.

Otra cosa es un desarrollo tocho en el que ya no puedes dar marcha atrás o del que no aprendes nada.

B

#16 Cojonuda información :)

Me ha preocupado bastante lo que has comentado de los tests de integridad y me ha empezado a entrar un dolor escrotal bastante serio solo de imaginarmelo.

Realmente el proyecto en cuestión en el que estamos utilizando mongo es una pequeña API montada con node.js que se encarga de extraer datos de diferentes APIS repartidas por ahí para luego recogerlos con un Rails que si utiliza MySql, la parte de node(la que va con mongo) recibirá una buena cantidad de peticiones concurrentes, ya había oído que el rendimiento del mongo se notaba a partir de una cantidad brutal de registros como comentas. Lo que me preocupa es si realmente merecerá la pena para lo que queremos hacer.

Esta tarde me voy a dedicar a hacer el cafre y voy a tratar de estresar un mysql y un mongo con cantidades de información similares a las que utilizaría la aplicación en producción a ver si las diferencias de rendimiento son tan diferentes como para que merezca la pena todos los contras que veo que están saliendo con la mierda del nosql.

Espero poder tener una especie de informe cuando termine y ponerlo aquí.

2 respuestas
Spacelord

#19 Pues se agradecerá que pongas los resultados, que siempre viene bien saber mejor a partir de qué momento rinde un NoSQL.

Echale un ojo a esto a ver si te viene bien: http://www.cloudcomputingdevelopment.net/mongodb-vs-rdbms-schema-design/

B

He realizado las pruebas con una pequeña aplicación rails (no me apetecía usar Sinatra) con ruby 2.0.0p247

Para los modelos que iban a MySQL he utilizado el active_record clásico de rails y para los que iban a mongo he utilizado mongo_mapper

Las pruebas las he lanzado con rakes y he medido el tiempo de ejecución de estas antes y después de lanzar las consultas.

Los modelos son prácticamente iguales: una string name y 50 columnas integer que se rellenan con enteros aleatorios del 1 al 500 al crear los objetos.

Las primeras pruebas las he realizado con 100 registros:

Mysql en creación: 0.75 Segundos
Mongodb en creación: 0.34 Segundos

Mysql en listado (Select *): 0.37 Segundos
mongodb en listado: 0.5 Segundos

100,000 Registros:

Mysql en creación: 412 Segundos
Mongodb en creación: 287 Segundos

Mysql en listado (Select *): 70 Segundos
Mongodb en listado: 418 Segundos

Mongo ha acabado creando: .0(64MB) 1.(128MB) .2(256MB) .ns(16MB)

Se que el haber corrido esto sobre rails ha podido influir bastante en estos tiempos, pero es lo más parecido que he podido hacer a una situación real en un entorno de desarrollo, no he seguido hasta el millón de filas porque me da miedo lo que puede alargar el mongo los ficheros y no quiero que se me llene el disco duro del macbook (porque es con lo que trabajo xD).

Tenéis la mierderaplicación en github: https://github.com/luismiramirez/cafretest-mysql-mongo por si quiere alguien echarle un vistazo, sugerir, cambiar lo que os salga del pijo si no tenéis nada mejor que hacer.

discuss?

1 1 respuesta
elkaoD

#16 Twitter no usa MongoDB que yo sepa. En 2010 estaban migrando a Cassandra y usan su propio FlockDB como BD de grafos.

#16 #19 pues en mi humilde opinión MongoDB apesta para una alta cantidad de registros. Necesitas tener el "working set" en memoria, así que solo es útil si tienes muchos registros no utilizados y pocos relevantes de los que poder tirar por índice. Este índice también debe estar en memoria y va creciendo con la BD.

El caso de uso más habitual para muchos registros es mapReduce... y MongoDB tampoco está preparada para eso. MongoDB para ser NoSQL es lenta. Su gran ventaja es su simpleza en el uso y tratamiento de datos cuando no requieres ACID.

1 respuesta
Spacelord

#21 Joder, la diferencia de listado cuando aumentan los registros es salvajísima.

#22 Me ha fallado la memoria, sabía que en Twitter usaban NoSQL y pensaba que era MongoDB.

Los problemas que mencionaba arriba de MongoDB (lentitud de proceso de queries, tamaño de las BD, etc) son endémicos no tando de MongoDB en particular como de NoSQL en general. Pero no sabía yo que aumentaba tanto el tiempo de respuesta cuando subía el número de tuplas. Se supone que es un SGBD que se usa como solución a tablas muy grandes, ¿no?

2 respuestas
B

#23 Fijate que he tenido que repetir pruebas porque no me lo creía, tenía planeado probar la de 1 millón de registros y visto lo visto no he tenido huevos. Probablemente mañana pueda probarlo en una máquina de brightbox que tengo muerta de risa en el curro pero no creo que cambie mucho la cosa.

Quiero creer que la diferencia de tiempos tan basta tenga que ver con el rails, pero aun así si dentro de lo que sería una aplicación web por ejemplo va a tener este rendimiento le pueden ir dando por culo al mongo.

No digo al nosql en general porque para cachear con Redis estoy ultraenamorado aunque tampoco se si Redis entraría en el saco nosql :$

1 respuesta
elkaoD

#23 ya te digo que tengo experiencia con MongoDB para tratar grandes BDs de forma masiva y es pésima.

  • Si quieres usar tablas muy grandes para hacer mapReduce... mejor usar tecnología MapReduce.
  • Si quieres usar tablas muy grandes y no quieres hacer mapReduce, o tus accesos son siempre a los primeros registros del índice o estás jodido.

#24 si no recuerdo mal el rendimiento decrece exponencialmente en Mongo cuantos más registros uses (especialmente si llegas al límite en que no caben en memoria en accesos concurrentes). Un SELECT * es de lo peor que puedes hacer en Mongo (junto a accesos aleatorios).

1 respuesta
B

#25 Se me acaban de caer todos los mitos que había con mongodb. Si hasta juraría que hace tiempo vi el tipico gráfico de barras que comparaba precisamente el SELECT * de un mongo frente a un sql y el mongo salía con una diferencia gigante de velocidad respecto a mysql.

1 respuesta
elkaoD

#26 Mongo es rápido escribiendo, no leyendo... y sólo si te la sopla ACID y prefieres writes "de fe" (MongoDB no espera a guardar nada para dar el "ok" ).

2 respuestas
Spacelord

#27 Es mejor que eso: MongoDB da la transacción por buena cuando la orden sale del terminal, no cuando el server devuelve la confirmación. Es decir, que si haces un commit a una DB remota tú lo ves como OK no significa que el commit se haya realizado. Es más: la DB podría estar muerta, con el server apagado, y seguirías viendo el commit correcto; el único problema vendría cuando intentases hacer otra query y te saltase un error de conexión.

Más risas: http://www.reddit.com/r/programming/comments/1fg4sp/mongodb_java_driver_uses_mathrandom_to_decide/

Cuando pides un log no te muestra todas las transacciones, sino que un algoritmo (usando un random, con dos cojones) decide qué líneas del log te muestra. Es la hostia, un log que no devuelve todos los eventos. Como para depurar un error.

1 respuesta
B

#28 Si lanzas cualquier rake de lo que he subido a github y en otro terminal tiras un tail -f al log del mongo verás que efectivamente es así. Me he preocupado en una de las pruebas porque estaba tardando demasiado me ha dado por mirar el log y estaba soltando lo que le daba la gana. Ahora que leo eso me queda mas claro xD

JuAn4k4

#27 Eso no es del todo cierto, en si el que espera es el driver que se conecta a mongo, y con el WriteConcern especificas cuanto esperas

1 respuesta