dominar las funciones básicas de mongodb
- llenar MongoDB con datos;
- escribir varias consultas para obtener y actualizar datos
- cree índices y compare el rendimiento.
Llenado del almacén de datos
En busca de datos suficientes para estudiar las capacidades básicas de MongoDB, me decidí por el conjunto de datos de aterrizajes de meteoritos terrestres de la NASA (1000 líneas con información sobre meteoritos que cayeron a la Tierra, del repositorio https://github.com/jdorfman/awesome- json-datasets .
Nota : se encontró información más completa (45.7K) https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh , pero exportar a JSON a través de su API solo da 1000 registros (incomprensible) , es necesario para palear los datos completos del archivo CSV exportado https://data.nasa.gov/api/views/gh4g-9sfh/rows.csv?accessType=DOWNLOAD ?
Nota: Ups, es posible obtener datos completos en JSON, pero esto es un truco. Sinceramente espero que esto no sea inyección SQL
https :? //Data.nasa.gov/api/id/gh4g-9sfh.json $ select = ` name`,` id`, `nametype`,` recclass`, ` mass`, `fall`,` year`, `reclat`,` reclong`, `geolocation` & $ order =`: id` + ASC & $ limit = 46000 & $ offset = 0
wc ./gh4g-9sfh.json
45716 128491 10441343 ./gh4g-9sfh.json
Para interactuar con el servidor en la red, instalé solo el cliente y las herramientas en la máquina local:
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org-shell
sudo apt-get install -y mongodb-org-tools
Comprobación de conexión:
mongo nosql-2020.otus --port 32789
MongoDB shell version v4.4.1
connecting to: mongodb://nosql-2020.otus:32789/test?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("5ff24788-0710-4a1a-821f-7acb2eddfb4f") }
MongoDB server version: 4.4.1
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
https://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forums
https://community.mongodb.com
Importar datos de una máquina local a una remota:
mongoimport --host nosql-2020.otus --db "otus_003" --port 32789 --collection eml45k --jsonArray --file ./003_MONGODB.files/gh4g-9sfh.json
2020-10-12T01:01:13.826+0100 connected to: mongodb://nosql-2020.otus:32789/
2020-10-12T01:01:16.827+0100 [#######.................] otus_003.eml45k 2.99MB/9.96MB (30.0%)
2020-10-12T01:01:19.827+0100 [###############.........] otus_003.eml45k 6.44MB/9.96MB (64.6%)
2020-10-12T01:01:22.827+0100 [#######################.] otus_003.eml45k 9.81MB/9.96MB (98.5%)
2020-10-12T01:01:23.035+0100 [########################] otus_003.eml45k 9.96MB/9.96MB (100.0%)
2020-10-12T01:01:23.035+0100 45716 document(s) imported successfully. 0 document(s) failed to import.
Nota : 10 segundos para todo, búsqueda de datos divertidos
> show databases
admin 0.000GB
config 0.000GB
local 0.000GB
otus_003 0.000GB
test 0.000GB
> use otus_003
switched to db otus_003
> show collections
em
l
Buscamos un meteorito con un nombre conocido:
> db.eml45k.find({name:"Bjelaja Zerkov"})
{ "_id" : ObjectId("5f8380a91c0ab84b54bfe394"), "name" : "Bjelaja Zerkov", "id" : "5063", "nametype" : "Valid", "recclass" : "H6", "mass" : "1850", "fall" : "Fell", "year" : "1796-01-01T00:00:00.000", "reclat" : "49.783330", "reclong" : "30.166670", "geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" } }
Buscamos un meteorito usando coordenadas conocidas:
> db.eml45k.find({ "geolocation" : { "latitude" : "44.83333" , "longitude" : "95.16667" } })
{ "_id" : ObjectId("5f8380a91c0ab84b54bfe322"), "name" : "Adzhi-Bogdo (stone)", "id" : "390", "nametype" : "Valid", "recclass" : "LL3-6", "mass" : "910", "fall" : "Fell", "year" : "1949-01-01T00:00:00.000", "reclat" : "44.833330", "reclong" : "95.166670", "geolocation" : { "latitude" : "44.83333", "longitude" : "95.16667" } }
Obteniendo una lista de meteoritos caídos ordenados por año de otoño (me pregunto por qué la NASA no tiene un tiempo de caída específico en la hora media de Greenwich) y con una lista limitada de campos seleccionables:
> db.eml45k.find( { }, {year: 1, id: 1, name: 1, _id: 0 }).sort( { year: -1 } )
{ "name" : "Northwest Africa 7701", "id" : "57150", "year" : "2101-01-01T00:00:00.000" }
{ "name" : "Chelyabinsk", "id" : "57165", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7755", "id" : "57166", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7812", "id" : "57258", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7822", "id" : "57268", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7856", "id" : "57421", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7855", "id" : "57420", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7857", "id" : "57422", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7858", "id" : "57423", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7861", "id" : "57425", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7862", "id" : "57426", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Northwest Africa 7863", "id" : "57427", "year" : "2013-01-01T00:00:00.000" }
{ "name" : "Battle Mountain", "id" : "56133", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Sutter's Mill", "id" : "55529", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Antelope", "id" : "57455", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Catalina 009", "id" : "57173", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Jiddat al Harasis 799", "id" : "57428", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Johannesburg", "id" : "55765", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Ksar Ghilane 011", "id" : "55606", "year" : "2012-01-01T00:00:00.000" }
{ "name" : "Ksar Ghilane 010", "id" : "55605", "year" : "2012-01-01T00:00:00.000" }
Type "it" for more
>
¿Algo que no he encontrado? cómo combinar dos campos directamente en la selección:
"geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" }
hasta cierto punto (debe tenerse en cuenta que el pedido se cambió de acuerdo con la documentación ( https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy , es decir
< field >: [< longitude >, < latitude >]):
"geolocation" : { "type" : "Point", "coordinates" : [ 30.16667 , 49.78333 ] } }
directamente cuando se le pide que haga (¿quién sabe?) algo como esto:
> db.eml45k.find({
[
{$toDouble: "$geolocation.longitude"} ,
{$toDouble: "$geolocation.latitude"}
] : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
... ,
]]
}
}
}
})
Por lo tanto, creé un campo artificial en la colección:
db.eml45k.updateMany(
{},
[{
$set: {
"pointed_geolocation.type" : "Point",
"pointed_geolocation.coordinates" : [
{ $toDouble : "$geolocation.longitude" } ,
{ $toDouble: "$geolocation.latitude" }
]
}
}]
);
{ "acknowledged" : true, "matchedCount" : 45716, "modifiedCount" : 45716 }
y finalmente podemos ir en busca de meteoritos no detectados que cayeron en una determinada zona:
> db.eml45k.find({
"pointed_geolocation.coordinates" : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
[ 47.0 , 33.0 ],
[ 47.0 , 65.0 ],
[ 169.0 , 65.0 ],
[ 169.0 , 33.0 ],
[ 47.0 , 33.0 ]
]]
}
}
},
'fall': 'Fell'
},
{
year: {$year: { "$toDate": "$year"}},
"pointed_geolocation.coordinates": 1,
name: 1,
_id: 0
}).sort( { year: -1 } )
Muestra
{ «name»: «Chelyabinsk», «pointed_geolocation»: { «coordinates»: [ 61.11667, 54.81667 ] }, «year»: 2013 }
{ «name»: «Dashoguz», «pointed_geolocation»: { «coordinates»: [ 59.685, 41.98444 ] }, «year»: 1998 }
{ «name»: «Kunya-Urgench», «pointed_geolocation»: { «coordinates»: [ 59.2, 42.25 ] }, «year»: 1998 }
{ «name»: «Sterlitamak», «pointed_geolocation»: { «coordinates»: [ 55.98333, 53.66667 ] }, «year»: 1990 }
{ «name»: «Undulung», «pointed_geolocation»: { «coordinates»: [ 124.76667, 66.13889 ] }, «year»: 1986 }
{ «name»: «Omolon», «pointed_geolocation»: { «coordinates»: [ 161.80833, 64.02 ] }, «year»: 1981 }
{ «name»: «Yardymly», «pointed_geolocation»: { «coordinates»: [ 48.25, 38.93333 ] }, «year»: 1959 }
{ «name»: «Vengerovo», «pointed_geolocation»: { «coordinates»: [ 77.26667, 56.13333 ] }, «year»: 1950 }
{ «name»: «Kunashak», «pointed_geolocation»: { «coordinates»: [ 61.36667, 55.78333 ] }, «year»: 1949 }
{ «name»: «Krasnyi Klyuch», «pointed_geolocation»: { «coordinates»: [ 56.08333, 54.33333 ] }, «year»: 1946 }
{ «name»: «Lavrentievka», «pointed_geolocation»: { «coordinates»: [ 51.56667, 52.45 ] }, «year»: 1938 }
{ «name»: «Pavlodar (stone)», «pointed_geolocation»: { «coordinates»: [ 77.03333, 52.3 ] }, «year»: 1938 }
{ «name»: «Kainsaz», «pointed_geolocation»: { «coordinates»: [ 53.25, 55.43333 ] }, «year»: 1937 }
{ «name»: «Ichkala», «pointed_geolocation»: { «coordinates»: [ 82.93333, 58.2 ] }, «year»: 1936 }
{ «name»: «Nikolaevka», «pointed_geolocation»: { «coordinates»: [ 78.63333, 52.45 ] }, «year»: 1935 }
{ «name»: «Brient», «pointed_geolocation»: { «coordinates»: [ 59.31667, 52.13333 ] }, «year»: 1933 }
{ «name»: «Pesyanoe», «pointed_geolocation»: { «coordinates»: [ 66.08333, 55.5 ] }, «year»: 1933 }
{ «name»: «Kuznetzovo», «pointed_geolocation»: { «coordinates»: [ 75.33333, 55.2 ] }, «year»: 1932 }
{ «name»: «Boriskino», «pointed_geolocation»: { «coordinates»: [ 52.48333, 54.23333 ] }, «year»: 1930 }
{ «name»: «Khmelevka», «pointed_geolocation»: { «coordinates»: [ 75.33333, 56.75 ] }, «year»: 1929 }
Type «it» for more
> it
{ «name»: «Mamra Springs», «pointed_geolocation»: { «coordinates»: [ 62.08333, 45.21667 ] }, «year»: 1927 }
{ «name»: «Demina», «pointed_geolocation»: { «coordinates»: [ 84.76667, 51.46667 ] }, «year»: 1911 }
{ «name»: «Krutikha», «pointed_geolocation»: { «coordinates»: [ 77, 56.8 ] }, «year»: 1906 }
{ «name»: «Barnaul», «pointed_geolocation»: { «coordinates»: [ 84.08333, 52.73333 ] }, «year»: 1904 }
{ «name»: «Tyumen», «pointed_geolocation»: { «coordinates»: [ 65.53333, 57.16667 ] }, «year»: 1903 }
{ «name»: «Ochansk», «pointed_geolocation»: { «coordinates»: [ 55.26667, 57.78333 ] }, «year»: 1887 }
{ «name»: «Dashoguz», «pointed_geolocation»: { «coordinates»: [ 59.685, 41.98444 ] }, «year»: 1998 }
{ «name»: «Kunya-Urgench», «pointed_geolocation»: { «coordinates»: [ 59.2, 42.25 ] }, «year»: 1998 }
{ «name»: «Sterlitamak», «pointed_geolocation»: { «coordinates»: [ 55.98333, 53.66667 ] }, «year»: 1990 }
{ «name»: «Undulung», «pointed_geolocation»: { «coordinates»: [ 124.76667, 66.13889 ] }, «year»: 1986 }
{ «name»: «Omolon», «pointed_geolocation»: { «coordinates»: [ 161.80833, 64.02 ] }, «year»: 1981 }
{ «name»: «Yardymly», «pointed_geolocation»: { «coordinates»: [ 48.25, 38.93333 ] }, «year»: 1959 }
{ «name»: «Vengerovo», «pointed_geolocation»: { «coordinates»: [ 77.26667, 56.13333 ] }, «year»: 1950 }
{ «name»: «Kunashak», «pointed_geolocation»: { «coordinates»: [ 61.36667, 55.78333 ] }, «year»: 1949 }
{ «name»: «Krasnyi Klyuch», «pointed_geolocation»: { «coordinates»: [ 56.08333, 54.33333 ] }, «year»: 1946 }
{ «name»: «Lavrentievka», «pointed_geolocation»: { «coordinates»: [ 51.56667, 52.45 ] }, «year»: 1938 }
{ «name»: «Pavlodar (stone)», «pointed_geolocation»: { «coordinates»: [ 77.03333, 52.3 ] }, «year»: 1938 }
{ «name»: «Kainsaz», «pointed_geolocation»: { «coordinates»: [ 53.25, 55.43333 ] }, «year»: 1937 }
{ «name»: «Ichkala», «pointed_geolocation»: { «coordinates»: [ 82.93333, 58.2 ] }, «year»: 1936 }
{ «name»: «Nikolaevka», «pointed_geolocation»: { «coordinates»: [ 78.63333, 52.45 ] }, «year»: 1935 }
{ «name»: «Brient», «pointed_geolocation»: { «coordinates»: [ 59.31667, 52.13333 ] }, «year»: 1933 }
{ «name»: «Pesyanoe», «pointed_geolocation»: { «coordinates»: [ 66.08333, 55.5 ] }, «year»: 1933 }
{ «name»: «Kuznetzovo», «pointed_geolocation»: { «coordinates»: [ 75.33333, 55.2 ] }, «year»: 1932 }
{ «name»: «Boriskino», «pointed_geolocation»: { «coordinates»: [ 52.48333, 54.23333 ] }, «year»: 1930 }
{ «name»: «Khmelevka», «pointed_geolocation»: { «coordinates»: [ 75.33333, 56.75 ] }, «year»: 1929 }
Type «it» for more
> it
{ «name»: «Mamra Springs», «pointed_geolocation»: { «coordinates»: [ 62.08333, 45.21667 ] }, «year»: 1927 }
{ «name»: «Demina», «pointed_geolocation»: { «coordinates»: [ 84.76667, 51.46667 ] }, «year»: 1911 }
{ «name»: «Krutikha», «pointed_geolocation»: { «coordinates»: [ 77, 56.8 ] }, «year»: 1906 }
{ «name»: «Barnaul», «pointed_geolocation»: { «coordinates»: [ 84.08333, 52.73333 ] }, «year»: 1904 }
{ «name»: «Tyumen», «pointed_geolocation»: { «coordinates»: [ 65.53333, 57.16667 ] }, «year»: 1903 }
{ «name»: «Ochansk», «pointed_geolocation»: { «coordinates»: [ 55.26667, 57.78333 ] }, «year»: 1887 }
Extraño, no sabía que Chelyabinskiy no figuraba en la categoría de encontrados.
Agreguemos y encontremos cuántos se encontraron y cuántos no:
db.eml45k.aggregate([
{ $match: {
"pointed_geolocation.coordinates" : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
[ 47.0 , 33.0 ],
[ 47.0 , 65.0 ],
[ 169.0 , 65.0 ],
[ 169.0 , 33.0 ],
[ 47.0 , 33.0 ]
]]
}
}
}
} },
{"$group" : {_id: "$fall", count: { $sum: 1 }}}
])
{ "_id" : "Fell", "count" : 26 }
{ "_id" : "Found", "count" : 63 }
En total, se encontraron 63 de 89 y se encontraron __26__ - __no__, por lo que existe la posibilidad :)
Uso de índices Eliminemos
todos los índices de la colección de experimentos anteriores:
db.eml45k.dropIndexes()
{
"nIndexesWas" : 1,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
Intentemos ver el tiempo de ejecución estimado de la solicitud:
db.eml45k.find({
"pointed_geolocation.coordinates" : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
[ 47.0 , 33.0 ],
[ 47.0 , 65.0 ],
[ 169.0 , 65.0 ],
[ 169.0 , 33.0 ],
[ 47.0 , 33.0 ]
]]
}
}
}
}).explain("executionStats").executionStats.executionTimeMillis
...
110
...
110
...
109
El resultado es de aproximadamente 110 segundos en promedio.
Vamos a indexar:
db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )
{
"ok" : 0,
"errmsg" : "Index build failed: 98b9ead2-c156-4312-81af-1adf5896e3c9: Collection otus_003.eml45k ( 6db2d178-61b5-4627-8512-fcf919fe596f ) :: caused by :: Can't extract geo keys: { _id: ObjectId('5f838e30fb89bd9d553ae27f'), name: \"Bulls Run\", id: \"5163\", nametype: \"Valid\", recclass: \"Iron?\", mass: \"2250\", fall: \"Fell\", year: \"1964-01-01T00:00:00.000\", pointed_geolocation: { type: \"Point\", coordinates: [ null, null ] } } Point must only contain numeric elements",
"code" : 16755,
"codeName" : "Location16755"
}
El error se debe a valores NULL, no encontré algo de inmediato cómo (¿quién sabe?) Para excluirlo del índice durante la indexación, así que eliminaré estas claves:
db.eml45k.updateMany(
{ "pointed_geolocation.coordinates" : [ null , null ] },
[{
$set: { "pointed_geolocation": null }
}]
);
Probar el índice de nuevo
db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )
{
"ok" : 0,
"errmsg" : "Index build failed: d33b31d4-4778-4537-a087-58b7bd1968f3: Collection otus_003.eml45k ( 6db2d178-61b5-4627-8512-fcf919fe596f ) :: caused by :: Can't extract geo keys: { _id: ObjectId('5f838e35fb89bd9d553b3b8f'), name: \"Meridiani Planum\", id: \"32789\", nametype: \"Valid\", recclass: \"Iron, IAB complex\", fall: \"Found\", year: \"2005-01-01T00:00:00.000\", reclat: \"-1.946170\", reclong: \"354.473330\", geolocation: { latitude: \"-1.94617\", longitude: \"354.47333\" }, pointed_geolocation: { type: \"Point\", coordinates: [ 354.47333, -1.94617 ] } } longitude/latitude is out of bounds, lng: 354.473 lat: -1.94617",
"code" : 16755,
"codeName" : "Location16755"
}
El error __longitude / latitude está fuera de los límites, lng: 354.473 lat: -1.94617__ y en la documentación https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy
Valid longitude values are between -180 and 180, both inclusive.
Valid latitude values are between -90 and 90, both inclusive.
y 354.47333 no está incluido en el rango de -180 a 180.
Muy extraño, al principio pensé que debía haber una enmienda en todas partes por menos 180 para hacer
(`$subtract: [{ $toDouble : "$geolocation.longitude" }, 180.0]`)
, pero al final no todo es tan sencillo.
Qué longitudes no están dentro del rango:
db.eml45k.find({"pointed_geolocation.coordinates.0": {$lt: -180}} ) # ,
db.eml45k.find({"pointed_geolocation.coordinates.0": {$lt: 0}} ) # ,
db.eml45k.find({"pointed_geolocation.coordinates.0": {$gt: 180}} ) # ,
{ "_id" : ObjectId("5f838e35fb89bd9d553b3b8f"), "name" : "Meridiani Planum", "id" : "32789", "nametype" : "Valid", "recclass" : "Iron, IAB complex", "fall" : "Found", "year" : "2005-01-01T00:00:00.000", "reclat" : "-1.946170", "reclong" : "354.473330", "geolocation" : { "latitude" : "-1.94617", "longitude" : "354.47333" }, "pointed_geolocation" : { "type" : "Point", "coordinates" : [ 354.47333, -1.94617 ] } }
Como resultado, solo un meteorito tiene coordenadas extrañas. Después de buscar, descubrí que este meteorito Meridiani Planum fue encontrado accidentalmente por el rover Opportunity en 2005
( http://old.mirf.ru/Articles/art2427_2.htm ). Este es un ( ADVERTENCIA ) meteorito marciano encontrado ( ADVERTENCIA ) en Marte. Aquí están los bromistas de la NASA.
Eliminémoslo de la colección.
db.eml45k.remove({"id" : "32789"})
WriteResult({ "nRemoved" : 1 })
Indexación
> db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Medimos
db.eml45k.find({
"pointed_geolocation.coordinates" : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
[ 47.0 , 33.0 ],
[ 47.0 , 65.0 ],
[ 169.0 , 65.0 ],
[ 169.0 , 33.0 ],
[ 47.0 , 33.0 ]
]]
}
}
}
}).explain("executionStats").executionStats.executionTimeMillis
Como resultado, volver a verificar 104 ... 107 ... 106 ...
Es un poco extraño, no muy inteligente.
Índice eliminado, verificado.
Sin índice y con índice, es lo mismo.
Lo intento por separado para Chelyabinsk:
db.eml45k.find(
{"pointed_geolocation.coordinates" : [ 61.11667, 54.81667 ]}
).explain("executionStats").executionStats.executionTimeMillis
sin índice y con índice, lo mismo. Debe
tener más cuidado, el índice se construye para el campo point_geolocation , y el campo point_geolocation.coordinates está involucrado en la consulta .
Como resultado, la consulta
db.eml45k.find({
"pointed_geolocation" : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
[ 47.0 , 33.0 ],
[ 47.0 , 65.0 ],
[ 169.0 , 65.0 ],
[ 169.0 , 33.0 ],
[ 47.0 , 33.0 ]
]]
}
}
}
}).explain("executionStats").executionStats.executionTimeMillis
sin el índice 125, 123, 119, 123 milisegundos, y con el índice - 7, 4, 4, 5.
Todo salió bien.