Статьи

Использование Groovy для импорта XML в MongoDB

В этом году я продемонстрировал, как легко  создавать современные веб-приложения, используя AngularJS, Java и MongoDB . Я также использую Groovy во время этой демонстрации, чтобы делать то, что Groovy действительно хорошо умеет писать описательные тесты и создавать сценарии.

Из-за нехватки времени в демоверсии у меня никогда не было возможности углубиться в детали сценария, который я использую, поэтому цель этого давно ожидаемого поста в блоге состоит в том, чтобы немного подробнее рассмотреть этот сценарий Groovy.

Во-первых, я хочу уточнить, что это не моя оригинальная работа — я 

украл
большинство идей для демонстрации у моего коллеги Росса Лоули. В  этом посте  он подробно рассказывает о том, как он создал приложение, которое находит самые популярные названия пабов в Великобритании. Там есть раздел,  где он рассказывает о загрузке открытых карт улиц и использовании python для преобразования XML во что-то более удобное для MongoDB — именно этот процесс я в основном украл, переработал для кафе и переписал для JVM.

Я предполагаю, что если вы работали с Java в течение какого-то периода времени, наступил момент, когда вам нужно было использовать его для анализа XML. Поскольку предполагается, что моя демонстрация посвящена тому, как легко работать с Java, я не  хотел этого делать. Когда я писал демо, я не очень хорошо знал Groovy, но я знал, что он встроил поддержку синтаксического анализа и манипулирования XML, что я и хотел делать. Кроме того, создавать  Карты  (структуры данных, а не географические) с помощью Groovy очень просто, и это действительно то, что нам нужно вставить в  MongoDB .

Цель сценария

  • Разобрать файл XML, содержащий открытые данные карт улиц всех кофеен.
  • Извлеките атрибуты XML широты и долготы и преобразуйте их в MongoDB GeoJSON .
  • Выполните некоторую базовую проверку данных кафе из XML.
  • Вставьте в MongoDB.
  • Убедитесь, что MongoDB знает, что он содержит данные геолокации с возможностью запроса.

Сценарий  PopulateDatabase.groovy , по этой ссылке вы перейдете к версии, которую я представил на JavaOne:

PopulateDatabase.groovy

Во-первых, нам нужны данные

Я использовал тот же сервис, который Росс использовал в своем блоге, чтобы получить XML-файл, содержащий «все» кафе по всему миру. Теперь, открытые данные карт улиц несколько… необработанные и неструктурированные (вот почему MongoDB является таким прекрасным инструментом для их хранения), поэтому я не уверен, что у меня действительно есть  все  кафе, но я получил достаточно данных для интересного демонстрация с использованием

http://www.overpass-api.de/api/xapi?*[amenity=cafe][cuisine=coffee_shop]

Полученный файл XML  в проекте GitHub, но если вы попробуете это сами вы могли бы (на самом деле, вероятно , будет) получить разные результаты.

Каждая запись XML выглядит примерно так:

<node id="178821166" lat="40.4167226" lon="-3.7069112">
    <tag k="amenity" v="cafe"/>
    <tag k="cuisine" v="coffee_shop"/>
    <tag k="name" v="Chocolatería San Ginés"/>
    <tag k="wheelchair" v="limited"/>
    <tag k="wikipedia" v="es:Chocolatería San Ginés"/>
</node>

Каждое кафе имеет уникальный идентификатор, а также широту и долготу в качестве атрибутов  node элемента. Внутри этого узла есть ряд  tag элементов, все с  k и  v атрибутами. Каждый кафе имеет различное количество этих атрибутов, и они не совместимы от магазина к магазину (кроме  amenity и  cuisine которые мы использовали , чтобы выбрать эти данные).

Инициализация

Инициализация скрипта

Прежде чем делать что-то еще, мы хотим подготовить базу данных. Предположение этого сценария состоит в том, что либо коллекция, в которой мы хотим хранить кофейни, пуста, либо полна устаревших данных. Поэтому мы собираемся использовать  Java-драйвер MongoDB,  чтобы получить интересующую нас коллекцию, а затем отбросить ее.

Здесь есть две интересные вещи:

  • Этот скрипт Groovy просто использует базовый драйвер Java. Groovy может очень счастливо общаться с ванильной Java, ей не нужно использовать библиотеку Groovy. Существуют специфичные для Groovy библиотеки для общения с MongoDB (например,  плагин MongoDB GORM ), но драйвер Java работает на отлично.
  • Вам не нужно создавать  базы данных  или  коллекции  (коллекции немного похожи на таблицы, но менее структурированы) явно в MongoDB. Вы просто используете интересующую вас базу данных и коллекцию, и, если она еще не существует, сервер создаст их для вас.

В этом примере мы просто используем конструктор по умолчанию для MongoClientкласса, представляющего соединение с серверами баз данных. По умолчанию это localhost: 27017, где я и работаю с базой данных. Однако вы можете указать свой собственный адрес и порт — для получения более подробной информации см.  Начало работы с MongoDB и Java .

Превратите XML в нечто в форме MongoDB

Разобрать и преобразовать XML

Итак, далее мы собираемся использовать XmlSlurper Groovy   для чтения XML-данных открытой карты улиц, о которых мы говорили ранее. Для того, чтобы перебирать каждый узел мы используем:  xmlSlurper.node.each. Для тех из вас, кто является новичком в Groovy или новичком в Java 8, вы можете заметить, что он использует замыкание для определения поведения, применяемого к каждому элементу «узла» в XML.

Создать GeoJSON

Создать GeoJSONПоскольку документы MongoDB — это просто карты пар ключ-значение, мы собираемся создать карту,  coffeeShop которая содержит структуру документа, представляющую кафе, которое мы хотим сохранить в базе данных. Во-первых, мы инициализируем эту карту с атрибутами  node. Помните, что эти атрибуты похожи на:

<node id="18464077" lat="-33.8911183" lon="151.1958773">

Мы собираемся сохранить идентификатор как значение для нового поля с именем openStreetMapId. Нам нужно сделать что-то более сложное с широтой и долготой, поскольку нам нужно сохранить их как GeoJSON, который выглядит примерно так:

{ 'location' : { 'coordinates': [<longitude>, <latitude>],
                 'type'       : 'Point' } }

В строках 12-14 вы можете видеть , что мы создаем  , Map что выглядит как GeoJSON, натягивая  lat и  lon атрибуты в соответствующих местах.

Вставить оставшиеся поля

Вставить оставшиеся поляПроверить имя поляТеперь для каждого  tag элемента в XML мы получаем  k атрибут и проверяем, является ли оно допустимым именем поля для MongoDB (оно не позволяет вставлять поля с точкой в, и мы не хотим переопределять наше тщательно сконструированное  locationполе). Если это так, мы просто добавляем этот ключ в качестве поля и его соответствующий  vатрибут в качестве значения на карту. Это эффективно копирует  данные ключ / значение OpenStreetMap в пары ключ / значение в документе MongoDB, поэтому мы не теряем никаких данных, но также не делаем ничего особенно интересного для их преобразования.

Сохранить в MongoDB

Сохранить в MongoDBНаконец, как только мы создали простую  coffeeShop карту, представляющую документ, который мы хотим сохранить в MongoDB, мы вставляем его в MongoDB, если на карте есть поле с именем  name. Мы могли бы проверили это , когда мы читали XML и положить его в карту, но это на самом деле гораздо проще просто использовать довольно синтаксис Groovy для проверки ключа называется  nameв  coffeeShop.

Когда мы хотим вставить карту, нам нужно превратить это в BasicDBObjectтип документа драйвера Java, но это легко сделать, вызвав конструктор, который принимает карту. В качестве альтернативы, есть синтаксис Groovy, который фактически делает то же самое, что вы можете предпочесть:

collection.insert(coffeeShop as BasicDBObject)

Скажите MongoDB, что мы хотим выполнить гео-запросы на этих данных

Добавить Geo IndexПотому что мы собираемся сделать  nearSphere  запрос по этим данным, мы должны добавить индекс «2dsphere» на нашем поле местоположения. Мы создали  locationполе как GeoJSON, поэтому все, что нам нужно сделать, это вызвать  createIndex это поле.

Вывод

Итак, это все! Groovy — хороший инструмент для такого рода сценариев — он не только является языком сценариев, но и встроенной поддержкой XML, действительно красивым синтаксисом Map и поддержкой замыканий делает его идеальным инструментом для итерации по данным XML и превращая его во что-то, что можно вставить в коллекцию MongoDB.