Статьи

Neo4j Spatial, часть 1: создание механизма рекомендаций

 

http://www.iconarchive.com/show/gis-gps-map-icons-by-icons-land/Layers-icon.html

Одно из моих новогодних решений — создать проект с Neo4j Spatial , поэтому мы начнем свой первый блог-пост года с подробным введением в этот удивительный плагин. Я советую вам посмотреть это очень короткое 15-минутное видео от создателя Neo4j Spatial Крейга Тавернера . Человек — разработчик уровня гения, вы получите очки IQ, просто слушая, клянусь.

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

Поэтому первое, что я всегда делаю при запуске нового проекта, это поиск данных. Спросив Google, всезнающий и всемогущий, я столкнулся с…фактический

У Factual есть огромное количество данных, доступных через API, и в частности некоторые данные о ресторанах, которые меня заинтересовали. Давайте посмотрим на это:

{
  "accessible_wheelchair": true,
  "address": "5951 N Broadway St",
  "alcohol": true,
  "alcohol_bar": true,
  "alcohol_beer_wine": true,
  "alcohol_byob": true,
  "attire": "casual",
  "category_ids": [
    366
  ],
  "category_labels": [
    [
      "Social",
      "Food and Dining",
      "Restaurants",
      "Sushi"
    ]
  ],
  "country": "us",
  "cuisine": [
    "Cafe",
    "Thai",
    "Sushi",
    "Asian",
    "Japanese"
  ],
  "email": "[email protected]",
  "factual_id": "8f4b9dff-d1e2-47d5-b761-1e63928f79ac",
  "groups_goodfor": true,
  "hours": "{\"monday\":[[\"15:00\",\"22:00\",\"Dinner\"]],\"tuesday\":[[\"17:00\",\"22:00\",\"Dinner\"]],\"wednesday\":[[\"15:00\",\"22:00\",\"Dinner\"],[\"12:00\",\"15:00\"]],\"thursday\":[[\"15:00\",\"22:00\",\"Dinner\"],[\"12:00\",\"15:00\"]],\"friday\":[[\"15:00\",\"22:30\",\"Dinner\"],[\"12:00\",\"15:00\"]],\"saturday\":[[\"15:00\",\"22:30\",\"Dinner\"],[\"12:00\",\"15:00\"]],\"sunday\":[[\"15:00\",\"22:00\",\"Dinner\"],[\"12:00\",\"15:00\"]]}",
  "hours_display": "Mon 3:00 PM-10:00 PM; Tue 5:00 PM-10:00 PM; Wed-Thu 12:00 PM-10:00 PM; Fri-Sat 12:00 PM-10:30 PM; Sun 12:00 PM-10:00 PM",
  "kids_goodfor": true,
  "latitude": 41.990326,
  "locality": "Chicago",
  "longitude": -87.672907,
  "meal_cater": true,
  "meal_deliver": true,
  "meal_dinner": true,
  "meal_lunch": true,
  "meal_takeout": true,
  "name": "Indie Cafe",
  "neighborhood": [
    "Edgewater",
    "Far North Side",
    "northside",
    "North Edgewater"
  ],
  "open_24hrs": false,
  "options_healthy": true,
  "options_vegan": true,
  "options_vegetarian": true,
  "parking": true,
  "parking_street": true,
  "payment_cashonly": false,
  "postcode": "60660",
  "price": 2,
  "rating": 5,
  "region": "IL",
  "reservations": true,
  "seating_outdoor": true,
  "smoking": false,
  "status": "1",
  "tel": "(773) 561-5577",
  "website": "http://www.indiecafe.us"
}

Посмотрите на все эти прекрасные данные. Мы знаем их часы, есть ли у них здоровые, веганские и вегетарианские блюда. Если это хороший ресторан для детей или больших групп, кухня, рейтинг, цена … а также широта и долгота. Мы также знаем, принимают ли они резервирование, но у нас нет простого способа сделать резервирование (кроме как позвонить им и поговорить с настоящим человеком, но кто хочет это сделать). Я знаю, как это исправить …

opentable_logo_reg

Я использовал OpenTable раньше, и, потратив некоторое время на просмотр их сайта в поисках API, я не смог его найти. Не бояться, Дэн Соседофф был достаточно любезен, чтобы создать неофициальный API OpenTable для нас.

Какие данные мы можем получить здесь:

{
  "id": 68710,
  "name": "Indie Cafe",
  "address": "5951 North Broadway",
  "city": "Chicago",
  "state": "IL",
  "area": "Chicago / Illinois",
  "postal_code": "60660",
  "country": "US",
  "phone": "7735615111",
  "reserve_url": "http://www.opentable.com/single.aspx?rid=68710",
  "mobile_reserve_url": "http://mobile.opentable.com/opentable/?restId=68710"
}

Круто, есть та волшебная ссылка, которую я искал. Теперь, если бы только у меня были меню и пункты меню …

food_genius

Ах, ха! Найдены некоторые, но они, кажется, прекратили свой API данных в недавней корпоративной опоре. Неважно, я напишу им по электронной почте и попрошу образец их данных. Если повезет, у нас будет больше данных, чем мы знаем, что делать.

Теперь вернемся к Neo4j Spatial. Самое простое, что мы можем здесь сделать, это создать пространственный индекс и добавить к нему рестораны, тогда мы сможем запросить его. Теперь мы, разработчики Ruby, ленивы, поэтому вместо того, чтобы возиться с maven и создавать плагин из исходного кода, я настроил Neography так, чтобы установка Neo4j с плагином Spatial выглядела так:

echo "require 'neography/tasks'" > Rakefile
rake neo4j:install
rake neo4j:get_spatial
rake neo4j:start

Легко ли? Теперь давайте попробуем создать пространственный индекс Restaurants и добавить к нему ресторан:

@neo = Neography::Rest.new
@neo.create_spatial_index("restaurants")

Ответ, который мы получаем от сервера:

{ "template" => "http://localhost:7474/db/data/index/node/restaurants/{key}/{value}",
  "provider" => "spatial",
  "geometry_type" => "point",
  "lat" => "lat",
  "lon" => "lon" }

Но что на самом деле произошло?

пространственно-1

Он создал для нас 4 узла. Специальный 0-й узел под названием «Пространственный корень», узел 1, который мы рассмотрим через секунду, и корень R-дерева и другой узел, который содержит метаданные R-дерева. Давайте внимательнее посмотрим на узел 1:

пространственно-узел1

Взгляните на это название, «рестораны» … верно, мы не используем Lucene или какой-либо другой механизм внешнего индекса, мы строим индекс внутри нашего собственного графика! Хорошо, давайте добавим этот ресторан.

node = @neo.create_node({:name => "Indie Cafe", :lat => 41.990326, :lon => -87.672907 })

Он создал узел 4, не связанный ни с чем другим, просто как бы зависающий …

Снимок экрана 2014-01-31 в 7.28.27 вечера

Итак, давайте теперь добавим этот узел в наш пространственный индекс:

@neo.add_node_to_spatial_index("restaurants", node)

Был создан дополнительный узел 5 со свойством «id», которое указывает на наш ресторан «узел 4».

Снимок экрана 2014-01-31 в 7.31.28 вечера

Чтобы убедиться, что это работает правильно, давайте попробуем запросить наш индекс. Мы выполним запрос шифра, который просматривает пространственный индекс узла «рестораны» для всего, что появляется в пределах 10,0 километров от широты 41,99, долготы -87,67.

@neo.execute_query("start n = node:restaurants({location}) return n",
                   {:location => "withinDistance:[41.99,-87.67,10.0]"})

… и мы находим это:

...{"data"=>{"lon"=>-87.672907, "lat"=>41.990326, "name"=>"Indie Cafe"}...

Отлично, теперь давайте попробуем добавить второй ресторан.

node2 = @neo.create_node({:name => "La Ciudad", :lat => 41.964239, :lon => -87.654758 })
@neo.add_node_to_spatial_index("restaurants", node2)

Наше R-дерево получает другую ветвь, и наши корневые свойства дерева меняются, чтобы охватить оба узла ветки:

Снимок экрана 2014-01-31 в 7.47.09 вечера

Мы запросим наш график еще раз:

@neo.execute_query("start n = node:restaurants({location}) return n",
                   {:location => "withinDistance:[41.99,-87.67,10.0]"})

… и мы видим, что мы возвращаем оба ресторана.

...{"data"=>{"lon"=>-87.672907, "lat"=>41.990326, "name"=>"Indie Cafe"}}...
...{"data"=>{"lon"=>-87.654758, "lat"=>41.964239, "name"=>"La Ciudad"}}...

Итак, теперь у вас есть хорошее представление о том, как работает простейшая часть плагина Neo4j. В следующей записи блога мы будем использовать то, что мы узнали здесь, для создания полноценного приложения, а также рассмотрим более сложные части плагина Neo4j Spatial в будущих публикациях блога.