В части 1 этой серии мы рассмотрели, как начать работу с Neo4j Spatial и мы смотрели на некоторых из частей мы используем сегодня , чтобы построить доказательство концепции приложения. Я называю приложение «Nom Nom Nom» в связи с его звукоподражательным мемом .
Итак, мы получим данные из Factual , получим данные из OpenTable , объединим их и импортируем в Neo4j:
rake neo4j:install rake neo4j:get_spatial rake neo4j:start rake neo4j:get_factual rake neo4j:get_opentable rake neo4j:combine rake neo4j:import rackup
Это займет некоторое время, но как только это будет сделано, у нас будет образец данных о ресторанах для США, проиндексированных в пространственном индексе «ресторанов». Я использую только данные из городов в списке ниже, так что имейте это в виду, когда пробуете демо.
locations = ["Arlington", "Atlanta", "Austin", "Baltimore", "Boston", "Bronx", "Brooklyn", "Charlotte", "Chicago", "Cincinnati", "Cleveland", "Columbia", "Columbus", "Dallas", "Denver", "Fort Worth", "Honolulu", "Houston", "Indianapolis", "Jacksonville", "Las Vegas", "Los Angeles", "Louisville", "Memphis", "Miami", "Milwaukee", "Minneapolis", "Nashville", "New Orleans", "New York", "Newark", "Oklahoma City", "Orlando", "Philadelphia", "Phoenix", "Pittsburgh", "Portland", "Richmond", "Rochester", "Sacramento", "Saint Louis", "San Antonio", "San Diego", "San Francisco", "San Jose", "Seattle", "Springfield", "Tampa", "Tucson", "Washington"]
Все приложение сводится к одному запросу на шифр, который выглядит следующим образом:
START n = node:restaurants({location}) WHERE n.price <= {price} AND n.rating >= {rating} AND n.meal_lunch = true # when lunch is selected AND n.meal_dinner = true # when dinner is selected AND n.alcohol_bar = true # when drinks is selected AND n.kids_goodfor = true # when... I think you get the idea AND n.groups_goodfor = true AND n.options_healthy = true AND n.options_vegan = true AND n.options_vegetarian = true AND n.smoking = true AND n.accessible_wheelchair = true AND n.alcohol = true AND n.alcohol_beer_wine = true AND n.alcohol_byob = true RETURN n LIMIT 25
Параметры, передаваемые в запрос на шифрование, потребуют, чтобы мы получили широту и долготу голодного человека, использующего приложение.
{:location => "withinDistance:[#{latitude},#{longitude},#{distance}]", :price => price.to_i, :rating => rating.to_i}
К счастью для нас, современные браузеры имеют поддержку геолокации, и мы можем использовать некоторые библиотеки (например, geoPosition.js ) для поддержки старых браузеров.
geoPosition.getCurrentPosition(showPosition, function(){ Console.log("Couldn't get location");}, {enableHighAccuracy:true});
Нам понадобится карта для отображения мест расположения наших ресторанов, и для этого проекта мы будем использовать Google Maps, но мы могли бы использовать Leaflet.js или любую другую альтернативу .
jQuery('#map').goMap({ maptype: 'ROADMAP', latitude: latitude, longitude: longitude, zoom: 15, scaleControl: true, scrollwheel: false, markers: [] });
Однако голодный пользователь может искать ресторан за пределами своего текущего местоположения, поэтому нам также понадобится геокодер, который будет принимать адрес в качестве входного и возвращать координаты широты и долготы. Поскольку мы уже используем Карты Google, мы также будем использовать их геокодер , но мы могли бы использовать геокодер Data Science Tool Kit или любую другую альтернативу.
address = document.getElementById('where').value; if (address != '') { geocoder.geocode( { 'address': address}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { latitude = parseFloat(results[0].geometry.location.d); longitude = parseFloat(results[0].geometry.location.e); $.goMap.setMap({latitude: latitude, longitude: longitude}); ...
Мы создадим форму для пользовательского ввода, сделаем AJAX-вызов, который передает наши параметры.
$.ajax({ type: "POST", url: "/search", data: "what="+$("#search option:selected").val() + "&latitude=" + latitude + "&longitude=" + longitude + "&distance=" + document.getElementById("distance").innerHTML.replace(" km", ".0") + "&price=" + document.getElementById("price").innerHTML.replace("< ", "").length + "&rating=" + document.getElementById("rating").innerHTML.replace(">= ", "") + "&alcohol=" + $("#alcohol-selector-advanced").val() + "&good=" + $("#good-selector-advanced").val(), dataType: "html", success: function(data) { $('#restaurants').html(data); } });
Мы сделаем этот шифр вызов Neo4j.
restaurants = $neo.execute_query(cypher, location)["data"] @results = [] restaurants.each do |r| restaurant = r[0]["data"] node_id = r[0]["self"].split("/").last @results << { :pic => "/images/food/bigger/#{pick(restaurant["cuisine"])}_128.png", :cuisine => Array(restaurant["cuisine"]).join(", "), :name => restaurant["name"], :address => restaurant["address"], :rating => (restaurant["rating"] || -1), :price => (restaurant["price"] || -1), :latitude => restaurant["latitude"], :longitude => restaurant["longitude"], :group => pick(restaurant["cuisine"]), :node_id => node_id } end slim :index, :layout => false
Затем мы отобразим наши результаты :
h1 Search Results [email protected] do |result| div class="company-listing clearfix" a href="#" class="listing-image" img src="#{result[:pic]}" alt="" div class="listing-body" div class="listing-title" a href='restaurant?id=#{result[:node_id]}' class='text-colorful' = result[:name] ...
… и посыпать небольшим JavaScript, чтобы маркеры появились на карте .
$.goMap.createMarker({ latitude: "#{result[:latitude]}", longitude: "#{result[:longitude]}", group: "#{result[:group]}", icon: "/images/marker-#{result[:group]}.png", html: { content: "<a href='restaurant?id=#{result[:node_id]}'>#{result[:name]}</a>" } })
Наконец, нам нужно найти некоторые значки еды, чтобы все это выглядело красиво:
… и это все, что есть в приложении. Теперь нам нужно развернуть его где-нибудь. Мы будем использовать Heroku и GrapheneDB .
GrapheneDB — это размещенный сервис Neo4j, и одна из его особенностей заключается в том, что он позволяет нам использовать пространственный плагин Neo4j. Функция плагинов включена по умолчанию на всех платных планах. Это включено по запросу на бесплатных планах. Давайте настроим это:
Обновление : GrapheneDB добавил поддержку Spatial в 1.9.6 и 2.0.1 из коробки. Нет необходимости загружать плагин!
Мы можем получить URL плагина из инструкции Neo4j Spatial :
http://dist.neo4j.org/spatial/neo4j-spatial-0.12-neo4j-2.0.0-server-plugin.zip
Далее нам нужно перезапустить базу данных, чтобы включить плагин:
Затем мы загрузим каталог graph.db нашей уже созданной базы данных, используя функцию «Восстановить»:
Наконец, мы укажем Neography на наш подготовленный сервер:
$neo = Neography::Rest.new("http://myusername:[email protected]:24789")
… И разверните наше приложение в Heroku. Для получения дополнительной информации об использовании GrapheneDB на Heroku см. Это руководство . Вы можете увидеть приложение запущенным, перейдя на http://nomnomnomus.herokuapp.com .
Здесь мы можем поумнеть и добавить интеграцию с Facebook, чтобы вы могли получать рекомендации, которые нравятся вашим друзьям, но это довольно тривиально, и мы уже видели, как это сделать с Neo4j. Добавьте изображения еды из Foursquare и данные меню с единой платформы, и вы уже на пути к созданию приложения для рекомендации ресторанов с Neo4j Spatial.