[MongoDB] MongoDB — базовые возможности
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Цель:
освоить базовые возможности mongodb
Необходимо:
- установить MongoDB одним из способов: ВМ, докер;
- заполнить данными;
- написать несколько запросов на выборку и обновление данных
- создать индексы и сравнить производительность.
Решение
Установка хранилища
MongoDB было развернуто на машине в локальной сети с использованием Docker-контейнеризации и оболочки www.portainer.io.
Заполнение хранилища данными
Я в поисках данных, достаточных для изучения базовых возможностей MongoDB, остановился на наборе данных NASA «Earth Meteorite Landings» (1000 строк со сведениями об упавших на Землю метеоритах, из репозитория https://github.com/jdorfman/awesome-json-datasets.
Замечание: нашел более полные (45.7K) сведения https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh, но экспорт в JSON через их API дает только 1000 записей (непонятно), надо перелопатить полные данные экспортируемого CSV файла https://data.nasa.gov/api/views/gh4g-9sfh/rows.csv?accessType=DOWNLOAD?
Замечание: упс, можно получить полные данные в JSON, но это хак. Искренне надеюсь, что в этом нет 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
Для взаимодействия с сервером в сети поставил на локальной машине только клиента и инструменты:
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
Проверка соединения:
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
Импорт данных с локальной машины на удаленную:
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.
Замечание: 10 секунд на все про все, забавненько
Выборка данных
> 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
Ищем метеорит по заведомо известному имени:
> 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" } }
Ищем метеорит по заведомо известным координатам:
> 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" } }
Выборка списка упавших метеоритов с сортировкой по году падения (интересно, почему у NASA нет конкретного времени падения по Гринвичу) и c ограничением списка выбираемых полей:
> 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
>
Что-то я не нашел? как объединить два поля непосредственно при выборке:
"geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" }
в точку (необходимо заметить изменение порядка следования произведено в соответствии с документацией (https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy, то есть
< field >: [< longitude >, < latitude >]):
"geolocation" : { "type" : "Point", "coordinates" : [ 30.16667 , 49.78333 ] } }
непосредственно при запросе, чтобы сделать (кто знает?) что-то по типу этого:
> db.eml45k.find({
[
{$toDouble: "$geolocation.longitude"} ,
{$toDouble: "$geolocation.latitude"}
] : {
$geoWithin: {
$geometry: {
type : "Polygon" ,
coordinates: [[
... ,
]]
}
}
}
})
Поэтому создал искусственное поле в коллекции:
db.eml45k.updateMany(
{},
[{
$set: {
"pointed_geolocation.type" : "Point",
"pointed_geolocation.coordinates" : [
{ $toDouble : "$geolocation.longitude" } ,
{ $toDouble: "$geolocation.latitude" }
]
}
}]
);
{ "acknowledged" : true, "matchedCount" : 45716, "modifiedCount" : 45716 }
и мы наконец-то можем отправиться на поиски необнаруженных метеоритов, упавших в определенном районе:
> 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 } )
Выборка
SPL
{ «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 }
Странно, я не знал, что Челябинский не значится в категории «найден».
Агрегируем и найдем сколько найдено и сколько нет:
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 }
Итого найдено 63 из 89, а __26__ — __не__ нашли, так что есть шанс :)
Использование индексов
Удалим все индексы в коллекции от прошлых экспериментов:
db.eml45k.dropIndexes()
{
"nIndexesWas" : 1,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
Попробуем посмотреть оценочное время исполнения запроса:
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
Итог — примерно в среднем 110 секунд.
Проиндексируем:
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"
}
Ошибка из-за NULL-значений, я что-то не нашел сходу как (кто знает?) ее при индексировании исключить из индекса, поэтому удалю ключи эти:
db.eml45k.updateMany(
{ "pointed_geolocation.coordinates" : [ null , null ] },
[{
$set: { "pointed_geolocation": null }
}]
);
Пробуем опять индекс
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"
}
Ошибка __longitude/latitude is out of bounds, lng: 354.473 lat: -1.94617__ и в документации 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.
и 354.47333 не входит в диапазон от -180 до 180.
Очень странно, я сначала думал там нужно поправку везде на минус 180 сделать
(`$subtract: [{ $toDouble : "$geolocation.longitude" }, 180.0]`)
, но в итоге не все так просто.
Какие долготы не в диапазоне:
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 ] } }
В итоге только один метеорит имеет странные координаты. Поискав, выяснил, что этот метеорит Meridiani Planum был случайно найден марсоходом Opportunity в 2005 году
(http://old.mirf.ru/Articles/art2427_2.htm). Это (ВНИМАНИЕ) марсианский метеорит, найден (ВНИМАНИЕ) на Марсе. Вот NASA шутники.
Удалим его из коллекции.
db.eml45k.remove({"id" : "32789"})
WriteResult({ "nRemoved" : 1 })
Индексируем
> db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
Замеряем
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
В итоге при перепроверках 104… 107… 106…
Как-то странно, не очень-то и шустрее.
Удалил индекс, проверил.
Без индекса и с индексом — одинаково.
Пробую отдельно для Челябинского:
db.eml45k.find(
{"pointed_geolocation.coordinates" : [ 61.11667, 54.81667 ]}
).explain("executionStats").executionStats.executionTimeMillis
без индекса и с индексом — одинаково.
Нужно быть внимательнее, индекс же построен для поля pointed_geolocation, а в запросе участвует pointed_geolocation.coordinates
В итоге, запрос
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
без индекса 125, 123, 119, 123 миллисекунды, а с индексом — 7, 4, 4, 5.
Всё получилось.
===========
Источник:
habr.com
===========
Похожие новости:
- [Тестирование IT-систем, Java, Тестирование веб-сервисов] Flame-графики: «огонь» из всех движков (перевод)
- [Анализ и проектирование систем, Высокая производительность, Промышленное программирование, Распределённые системы] Выбор архитектурного стиля. Часть 4
- [Python] Почему интернационализация и локализация имеют значение (перевод)
- [Big Data, Data Engineering] Курс «Промышленный ML на больших данных» — что это, для кого и каких навыков требует?
- [Java, Oracle, Программирование, Промышленное программирование] Бинарные операторы в Java
- [] WSL эксперименты. Часть 2
- [Laravel] Laravel Jetstream. Зачем?
- [JavaScript, Тестирование мобильных приложений, ReactJS] Моки не кусаются! Осваиваем мокинг с React Testing Library (перевод)
- [Настройка Linux, Программирование, Разработка под Linux] Знакомимся с семафорами в Linux (перевод)
- [Управление продуктом] Как найти и описать платящий сегмент пользователей?
Теги для поиска: #_mongodb, #_otus, #_nosql, #_mongodb
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:49
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Цель: освоить базовые возможности mongodb Необходимо:
Решение Установка хранилища MongoDB было развернуто на машине в локальной сети с использованием Docker-контейнеризации и оболочки www.portainer.io. Заполнение хранилища данными Я в поисках данных, достаточных для изучения базовых возможностей MongoDB, остановился на наборе данных NASA «Earth Meteorite Landings» (1000 строк со сведениями об упавших на Землю метеоритах, из репозитория https://github.com/jdorfman/awesome-json-datasets. Замечание: нашел более полные (45.7K) сведения https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh, но экспорт в JSON через их API дает только 1000 записей (непонятно), надо перелопатить полные данные экспортируемого CSV файла https://data.nasa.gov/api/views/gh4g-9sfh/rows.csv?accessType=DOWNLOAD? Замечание: упс, можно получить полные данные в JSON, но это хак. Искренне надеюсь, что в этом нет 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 Для взаимодействия с сервером в сети поставил на локальной машине только клиента и инструменты: 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 Проверка соединения: 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 Импорт данных с локальной машины на удаленную: 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. Замечание: 10 секунд на все про все, забавненько Выборка данных > 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 Ищем метеорит по заведомо известному имени: > 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" } } Ищем метеорит по заведомо известным координатам: > 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" } } Выборка списка упавших метеоритов с сортировкой по году падения (интересно, почему у NASA нет конкретного времени падения по Гринвичу) и c ограничением списка выбираемых полей: > 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 > Что-то я не нашел? как объединить два поля непосредственно при выборке: "geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" }
в точку (необходимо заметить изменение порядка следования произведено в соответствии с документацией (https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy, то есть < field >: [< longitude >, < latitude >]):
"geolocation" : { "type" : "Point", "coordinates" : [ 30.16667 , 49.78333 ] } }
непосредственно при запросе, чтобы сделать (кто знает?) что-то по типу этого: > db.eml45k.find({
[ {$toDouble: "$geolocation.longitude"} , {$toDouble: "$geolocation.latitude"} ] : { $geoWithin: { $geometry: { type : "Polygon" , coordinates: [[ ... , ]] } } } }) Поэтому создал искусственное поле в коллекции: db.eml45k.updateMany(
{}, [{ $set: { "pointed_geolocation.type" : "Point", "pointed_geolocation.coordinates" : [ { $toDouble : "$geolocation.longitude" } , { $toDouble: "$geolocation.latitude" } ] } }] ); { "acknowledged" : true, "matchedCount" : 45716, "modifiedCount" : 45716 } и мы наконец-то можем отправиться на поиски необнаруженных метеоритов, упавших в определенном районе: > 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 } ) ВыборкаSPL{ «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 } Странно, я не знал, что Челябинский не значится в категории «найден». Агрегируем и найдем сколько найдено и сколько нет: 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 } Итого найдено 63 из 89, а __26__ — __не__ нашли, так что есть шанс :) Использование индексов Удалим все индексы в коллекции от прошлых экспериментов: db.eml45k.dropIndexes()
{ "nIndexesWas" : 1, "msg" : "non-_id indexes dropped for collection", "ok" : 1 } Попробуем посмотреть оценочное время исполнения запроса: 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 Итог — примерно в среднем 110 секунд. Проиндексируем: 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" } Ошибка из-за NULL-значений, я что-то не нашел сходу как (кто знает?) ее при индексировании исключить из индекса, поэтому удалю ключи эти: db.eml45k.updateMany(
{ "pointed_geolocation.coordinates" : [ null , null ] }, [{ $set: { "pointed_geolocation": null } }] ); Пробуем опять индекс 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" } Ошибка __longitude/latitude is out of bounds, lng: 354.473 lat: -1.94617__ и в документации 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. и 354.47333 не входит в диапазон от -180 до 180. Очень странно, я сначала думал там нужно поправку везде на минус 180 сделать (`$subtract: [{ $toDouble : "$geolocation.longitude" }, 180.0]`)
, но в итоге не все так просто. Какие долготы не в диапазоне: 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 ] } } В итоге только один метеорит имеет странные координаты. Поискав, выяснил, что этот метеорит Meridiani Planum был случайно найден марсоходом Opportunity в 2005 году (http://old.mirf.ru/Articles/art2427_2.htm). Это (ВНИМАНИЕ) марсианский метеорит, найден (ВНИМАНИЕ) на Марсе. Вот NASA шутники. Удалим его из коллекции. db.eml45k.remove({"id" : "32789"})
WriteResult({ "nRemoved" : 1 }) Индексируем > db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )
{ "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } Замеряем 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 В итоге при перепроверках 104… 107… 106… Как-то странно, не очень-то и шустрее. Удалил индекс, проверил. Без индекса и с индексом — одинаково. Пробую отдельно для Челябинского: db.eml45k.find(
{"pointed_geolocation.coordinates" : [ 61.11667, 54.81667 ]} ).explain("executionStats").executionStats.executionTimeMillis без индекса и с индексом — одинаково. Нужно быть внимательнее, индекс же построен для поля pointed_geolocation, а в запросе участвует pointed_geolocation.coordinates В итоге, запрос 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 без индекса 125, 123, 119, 123 миллисекунды, а с индексом — 7, 4, 4, 5. Всё получилось. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:49
Часовой пояс: UTC + 5