Мы начинаем с того места, где остановились на Neo4j на Heroku — часть первая, поэтому убедитесь, что вы прочитали это, или вы немного растерялись. Пока что мы клонировали проект Neoflix , настроили наше приложение Heroku и добавили дополнение Neo4j к нашему приложению. Теперь мы готовы заполнить наш график.
Откройте два окна браузера. На одном вы перейдете к своему экземпляру Neo4j, работающему на Heroku,
$ heroku config NEO4J_URL => http://xxxxxxxx:yyyyyyyy@70825a524.hosted.neo4j.org:7014
а с другой стороны вы перейдете к маршруту create_graph вашего приложения. Так что если вы назвали свое приложение neoflix, вы бы выбрали neoflix dot herokuapp dot com / create_graph.
Это запустит метод create_graph, и вы увидите узлы и отношения, создаваемые на Neo4j Dashboard. Это чуть более миллиона отношений, так что это займет несколько минут. Существуют более быстрые способы загрузки данных в Neo4j (дождитесь третьей части этой серии), но в нашем случае это сработает.
Прекрасные ребята на themoviedb.org предоставляют API для всех разработчиков, которые хотят интегрировать данные фильмов и актеров вместе с постерами или фанатами фильмов. Вы можете запросить ключ API, и они ответят очень быстро. Итак, давайте добавим это к нашим конфигам Heroku.
heroku config:add TMDB_KEY=XXXXXXX Adding config vars and restarting app... done, vXX TMDB => XXXXXXX
Если вы хотите проверить локально, вы можете сделать это:
export TMDB_KEY=XXXXXXX
Теперь мы можем использовать эту переменную среды в нашем приложении вместе с гемом ruby-tmdb от Aaron Gough :
require 'ruby-tmdb'
Tmdb.api_key = ENV['TMDB_KEY']
Tmdb.default_language = "en"
def get_poster(data)
movie = TmdbMovie.find(:title => CGI::escape(data["title"] || ""), :limit => 1)
if movie.empty?
"No Movie Poster found"
else
"<a href="#{movie.url}" target='_blank'>
<img src="#{movie.posters.first.url}">
<h3>#{movie.tagline}</h3>
<p>Rating: #{movie.rating} <br />
Rated: #{movie.certification}</p><p>#{movie.overview}</p>"
end
end
Мы визуализируем график, как я показал вам ранее, используя Neovigator, но вместо того, чтобы извлекать свойства нашего узла (поскольку они довольно мягкие), мы запросим постер фильма.
![]() |
![]() |
![]() |
Мы не будем визуализировать явные отношения, которые мы создали. Вместо этого мы будем визуализировать график неявных рекомендаций к фильму. Давайте посмотрим на этот метод сейчас:
def get_recommendations(neo, node_id)
rec = neo.execute_script("m = [:];
x = [] as Set;
v = g.v(node_id);
v.
out('hasGenera').
aggregate(x).
back(2).
inE('rated').
filter{it.getProperty('stars') > 3}.
outV.
outE('rated').
filter{it.getProperty('stars') > 3}.
inV.
filter{it != v}.
filter{it.out('hasGenera').toSet().equals(x)}.
groupCount(m){\"${it.id}:${it.title.replaceAll(',',' ')}\"}.iterate();
m.sort{a,b -> b.value <=> a.value}[0..24];",
{:node_id => node_id.to_i})
return [{"id" => node_id,
"name" => "No Recommendations",
"values" => [{"id" => "#{node_id}",
"name" => "No Recommendations"}]
}] if rec == "{}"
values = rec[1..rec.size-1].split(',').collect{ |v| {:id => v.split(':')[0].strip,
:name => v.split(':')[1] } }
[{"id" => node_id ,"name" => "Recommendations","values" => values }]
end
Давайте пройдемся по коду. В Groovy [:] есть карта (эквивалентная Ruby Hash) и, в конечном итоге, то, что мы хотим вернуть, поэтому мы создадим пустую и заполним ее позже. Затем мы создадим Set «x» (это неупорядоченная коллекция, см. Groovy List для упорядоченных коллекций). Мы также получаем нашу начальную вершину и присваиваем ей «v».
m = [:]; x = [] as Set; v = g.v(node_id);
Мы наполним пустой набор, который мы создали, родами нашего фильма, а потом сравним с ним роды других фильмов.
v.
out('hasGenera').
aggregate(x).
Затем мы возвращаемся на 2 шага, что ставит нас в начало нашего фильма и переходят к пользователям, которые оценили фильм более чем на 3 звезды.
back(2).
inE('rated').
filter{it.getProperty('stars') > 3}.
От этих пользователей мы выходим, чтобы найти все фильмы, которые они также оценили более чем на 3 звезды.
outV.
outE('rated').
filter{it.getProperty('stars') > 3}.
Это не наш начальный фильм (помните, что мы установили его в переменную «v»).
inV.
filter{it != v}.
… И мы проверяем, что эти фильмы имеют те же роды, что и наш стартовый фильм (помните, что мы заполнили сет «х»).
filter{it.out('hasGenera').toSet().equals(x)}.
groupCount делает то, на что это похоже, и сохраняет значение в карте «m», которую мы создали ранее. Однако мы хотим получить id, title и count, поэтому мы немного разбираемся в строках, чтобы получить id и title (без запятых… я расскажу почему через минуту) и iterate () . Оболочка Gremlin выполняет итерации автоматически для вас, но поскольку мы отправляем этот скрипт Gremlin через REST API, это не так. Однажды ты выдернешь свои волосы, пытаясь понять, что не так, и будешь проклинать «повторяйся», как только это поймешь…
groupCount(m){\"${it.id}:${it.title.replaceAll(',',' ')}\"}.iterate();
Здесь мы сортируем нашу Карту (b имеет счетчик) и получаем 25 лучших записей.
m.sort{a,b -> b.value <=> a.value}[0..24];",
Поскольку Neo4j будет выполнять этот код много раз, вы хотите его параметризировать, поэтому он анализирует его только один раз.
{:node_id => node_id.to_i})
Если мы вернем пустой хеш, мы вернем неудачное сообщение «Нет рекомендаций»,
return [{"id" => node_id,
"name" => "No Recommendations",
"values" => [{"id" => "#{node_id}",
"name" => "No Recommendations"}]
}] if rec == "{}"
Наконец, мы структурируем нашу Groovy Map в массив хэшей, которые мы используем в нашей визуализации, как я показал вам с Neovigator . Обратите внимание, что я разделяю запись запятыми (поэтому мы и подставили их раньше). Эта часть не понадобится очень скоро, так как финальная версия Neo4j 1.6 будет иметь поддержку JSON для Groovy Maps.
values = rec[1..rec.size-1].split(',').collect{ |v| {:id => v.split(':')[0].strip,
:name => v.split(':')[1] } }
[{"id" => node_id ,"name" => "Recommendations","values" => values }]
Мы сохраняем результаты получения постера фильма и его рекомендации в течение 30 дней, пользуясь кэшем лака, предоставленным нам Heroku. Затем мы получаем наш начальный узел либо по id, либо по названию.
get '/resources/show' do
response.headers['Cache-Control'] = 'public, max-age=2592000'
content_type :json
if params[:id].is_numeric?
node = neo.get_node(params[:id])
else
node = neo.execute_script("g.idx(Tokens.T.v)[[title:'#{CGI::unescape(params[:id])}']].next();")
end
id = node_id(node)
{:details_html => "<h2>#{get_name(node["data"])}</h2>" + get_poster(node["data"]),
:data => {:attributes => get_recommendations(neo, id),
:name => get_name(node["data"]),
:id => id}
}.to_json
end
По названию? Да, мы добавляем автозаполнение JQuery UI в наше приложение. Который передаст название фильма и найдет его в автоматическом индексе, который мы создали.
node = neo.execute_script("g.idx(Tokens.T.v)[[title:'#{CGI::unescape(params[:id])}']].next();")
… и вот оно у вас. Ваш собственный сайт с рекомендациями по фильмам на Heroku. Смотрите полный код на github.com/maxdemarzi/neoflix .
ОБНОВЛЕНИЕ: Похоже, что некоторые из вас, дорогие читатели, пытались запустить create_graph несколько раз, и это привело к путанице. Я постараюсь исправить это и скоро восстановить. Примечание для будущего себя: удалите маршрут create_graph на heroku перед публикацией поста.
Источник: http://maxdemarzi.com/2012/01/16/neo4j-on-heroku-part-two/





