Одно из моих новогодних решений — создать проект с 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 раньше, и, потратив некоторое время на просмотр их сайта в поисках 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" }
Круто, есть та волшебная ссылка, которую я искал. Теперь, если бы только у меня были меню и пункты меню …
Ах, ха! Найдены некоторые, но они, кажется, прекратили свой 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" }
Но что на самом деле произошло?
Он создал для нас 4 узла. Специальный 0-й узел под названием «Пространственный корень», узел 1, который мы рассмотрим через секунду, и корень R-дерева и другой узел, который содержит метаданные R-дерева. Давайте внимательнее посмотрим на узел 1:
Взгляните на это название, «рестораны» … верно, мы не используем Lucene или какой-либо другой механизм внешнего индекса, мы строим индекс внутри нашего собственного графика! Хорошо, давайте добавим этот ресторан.
node = @neo.create_node({:name => "Indie Cafe", :lat => 41.990326, :lon => -87.672907 })
Он создал узел 4, не связанный ни с чем другим, просто как бы зависающий …
Итак, давайте теперь добавим этот узел в наш пространственный индекс:
@neo.add_node_to_spatial_index("restaurants", node)
Был создан дополнительный узел 5 со свойством «id», которое указывает на наш ресторан «узел 4».
Чтобы убедиться, что это работает правильно, давайте попробуем запросить наш индекс. Мы выполним запрос шифра, который просматривает пространственный индекс узла «рестораны» для всего, что появляется в пределах 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-дерево получает другую ветвь, и наши корневые свойства дерева меняются, чтобы охватить оба узла ветки:
Мы запросим наш график еще раз:
@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 в будущих публикациях блога.