sql >> Base de Datos >  >> NoSQL >> MongoDB

Mejorar la estructura de agregación de MongoDB

MongoDB introdujo recientemente su nueva estructura de agregación. Esta estructura proporciona una solución más sencilla para calcular valores agregados en lugar de depender de estructuras potentes con un mapa reducido.

Con solo unas pocas primitivas simples, le permite calcular, agrupar, dar forma y diseñar documentos que están contenidos en una colección particular de MongoDB. El resto de este artículo describe la refactorización del algoritmo de reducción de mapas para un uso óptimo de la nueva plataforma de agregación MongoDB. El código fuente completo se puede encontrar en el repositorio de GitHub de Datablend disponible públicamente.

1. Estructura de agregación de MongoDB

La plataforma de agregación MongoDB se basa en el conocido concepto de canalización de Linux en el que la salida de un comando se transmite a través de un transportador o se redirige para usarse como entrada para el siguiente comando . En el caso de MongoDB, varios operadores se combinan en un solo transportador que se encarga de procesar el flujo de documentos.

Algunos operadores, como $ Match, $ Limit y $ Skip, aceptan el documento como entrada y generan el mismo documento si se cumple un determinado conjunto de criterios. Otros operadores, como $ project y $ unwind, aceptan un solo documento como datos de entrada y cambian su formato o forman varios documentos en función de una determinada proyección.

El operador de grupo $ finalmente acepta varios documentos como datos de entrada y los agrupa en un documento combinando los valores correspondientes. Las expresiones se pueden usar en algunos de estos operadores para calcular nuevos valores o realizar operaciones de cadena.

Varios operadores se combinan en una sola canalización, que se aplica a la lista de documentos. El transportador en sí se ejecuta como el comando MongoDB, lo que da como resultado un solo documento MongoDB, que contiene una matriz de todos los documentos que salieron al final del transportador. El siguiente párrafo describe en detalle el algoritmo de refactorización de similitud molecular como transportador de operadores. Asegúrese de (re)leer los dos artículos anteriores para comprender completamente la lógica de implementación.

2. Tuberías de similitud molecular

Al aplicar un transportador a una colección en particular, todos los documentos contenidos en esa colección se pasan como entrada al primer operador. Se recomienda que filtre esta lista lo más rápido posible para limitar la cantidad de documentos que se transfieren a través de la canalización. En nuestro caso, esto significa filtrar todo el documento que nunca cumplirá con el factor Tanimoto objetivo.

Por lo tanto, como primer paso, comparamos todos los documentos para los que el número de huellas dactilares se encuentra dentro de un cierto umbral. Si apuntamos a un factor de Tanimoto de 0,8 con una conexión de destino que contiene 40 huellas dactilares únicas, el operador de coincidencia $ se verá así:

{"$match" :
{ "fingerprint_count" : {"$gte" : 32, "$lte" : 50}}.
}

Solo las conexiones con un número de huellas dactilares de 32 a 50 se transferirán al siguiente operador de tuberías. Para realizar este filtrado, el operador de coincidencia $ puede usar el índice que definimos para la propiedad finger_count. Para calcular el coeficiente de Tanimoto, necesitamos calcular la cantidad de huellas dactilares comunes entre una determinada conexión de entrada y la conexión de destino a la que nos dirigimos.

Para trabajar a nivel de huella digital, usamos el operador $ unwind. $ unwind elimina los elementos de la matriz uno por uno, devolviendo el flujo de documentos en el que la matriz especificada se reemplaza por uno de sus elementos. En nuestro caso, aplicamos $ unwind a las huellas dactilares. En consecuencia, cada documento compuesto dará como resultado n documentos compuestos, donde n es el número de huellas dactilares únicas contenidas en un documento compuesto.

{"$unwind" :"$fingerprints"}

Para calcular la cantidad de huellas digitales comunes, comenzaremos por filtrar todos los documentos que no tengan las huellas digitales que se encuentran en la lista de huellas digitales de la conexión de destino. Para hacer esto, usamos el operador de coincidencia $ nuevamente, esta vez filtrando la propiedad de la huella digital, donde solo se admiten los documentos que contienen una huella digital que está en la lista de huellas digitales de destino.

{"$match" :
{ "fingerprints" :
{"$in" : [ 1960 , 15111 , 5186 , 5371 , 756 , 1015 , 1018 , 338 , 325 , 776 , 3900 , ..., 2473] }
}
}

Dado que solo comparamos las huellas dactilares que están en la lista de huellas dactilares de destino, la salida se puede utilizar para calcular el número total de huellas dactilares comunes.

Para hacer esto, aplicamos el operador de grupo $ a la conexión compuesta, aunque creamos un nuevo tipo de documento que contiene el número de huellas dactilares coincidentes (sumando el número de ocurrencias), el número total de huellas dactilares de conexión de entrada y emoticonos.

{"$group" :
{ "_id" : "$compound_cid". ,
"fingerprintmatches" : {"$sum" : 1} ,
"totalcount" : { "$first" : "$fingerprint_count"} ,
"smiles" : {"$first" : "$smiles"}
}
}

Ahora tenemos todos los parámetros para calcular el coeficiente de Tanimoto. Para hacer esto, usaremos el operador de proyecto $ que, además de copiar la propiedad compuesta id y smiles, también agrega una propiedad recién calculada llamada Tanimoto.

{
"$project"
:

{
"_id"
:
1
,

"tanimoto"
:
{
"$divide"
:
[
"$fingerprintmatches."
,
{
"$subtract"
:
[
{
"$add"
:
[
40
,
"$totalcount"
]
}
,
"$fingerprintmatches."
]
}
]
}
,

"smiles"
:
1

}

}

Dado que solo nos interesan las conexiones que tienen un coeficiente objetivo de Tanimoto de 0,8, usamos el operador de coincidencia $ opcional para filtrar todas aquellas que no alcanzan este coeficiente.

{"$match" :
{ "tanimoto" : { "$gte" : 0.8}
}

El comando de la canalización completa se puede encontrar a continuación.

{"aggregate" : "compounds"} ,
"pipeline" : [
{"$match" :
{ "fingerprint_count" : {"$gte" : 32, "$lte" : 50} }
},
{"$unwind" : "$fingerprints"},
{"$match" :
{ "fingerprints" :
{"$in" : [ 1960 , 15111 , 5186 , 5371 , 756 , 1015 , 1018 , 338 , 325 , 776 , 3900,... , 2473] }
}
},
{"$group" :
{ "_id" : "$compound_cid" ,
"fingerprintmatches" : {"$sum" : 1} ,
"totalcount" : { "$first" : "$fingerprint_count"} ,
"smiles" : {"$first" : "$smiles"}
}
},
{"$project" :
{ "_id" : 1 ,
"tanimoto" : {"$divide" : ["$fingerprintmatches"] , { "$subtract" : [ { "$add" : [ 89 , "$totalcount"]} , "$fingerprintmatches"] }. ] } ,
"smiles" : 1
}
},
{"$match" :
{"tanimoto" : {"$gte" : 0.05} }
} ]
}

El resultado de esta canalización contiene una lista de conexiones que tienen Tanimoto 0.8 o superior en relación con una conexión de destino en particular. A continuación se puede encontrar una representación visual de este transportador:

3. Conclusión

La nueva estructura de agregación de MongoDB proporciona un conjunto de operadores fáciles de usar que permiten a los usuarios expresar algoritmos de tipo reducción de tarjeta de forma más breve. El concepto de un transportador debajo ofrece una forma intuitiva de procesar datos.

No es sorprendente que este paradigma de canalización sea adoptado por varios enfoques NoSQL, incluido Gremlin Framework Tinkerpop en la implementación y Cypher Neo4j en la implementación.

En términos de rendimiento, la solución de tuberías es una mejora significativa en la implementación de los mapas de reducción.

Inicialmente, los operadores son compatibles con la plataforma MongoDB, lo que conduce a mejoras significativas en el rendimiento con respecto al Javascript interpretado. Dado que Aggregation Framework también puede operar en un entorno aislado, supera fácilmente el rendimiento de mi implementación original, especialmente cuando la cantidad de conexiones de entrada es alta y el objetivo de Tanimoto es bajo. ¡Excelente rendimiento del comando MongoDB!

Agregación | Mongo DB | Tutoría