В своем блоге
Марко А. Родригес показал нам, как создать
движок для графического рекомендателя фильмов на основе Gremlin и Neo4j.
В этой серии из двух частей мы собираемся взять его работу из оболочки Gremlin и разместить ее в Интернете с помощью дополнения Heroku Neo4j и изменить проект Neovigator для нашего варианта использования. У Heroku есть отличная статья о том, как настроить пример приложения Neo4j и запустить его в своем Центре разработки, а Майкл Хангер покажет вам, как добавить расширения JRuby, и предоставит пример кода с использованием Geo Neo4j.rb от Andreas Ronge .
Мы собираемся следовать их рецепту, но мы собираемся добавить немного специй. Вместо создания небольшого графа отношений 2 узла и 1 я собираюсь показать вам, как использовать возможности Gremlin и Groovy для построения значительно большего графа из набора файлов.
Начнем с клонирования приложения Neoflix Sinatra, и вместо локальной установки и запуска Neo4j мы собираемся создать приложение Heroku и добавить Neo4j.
git clone [email protected]:maxdemarzi/neoflix.git cd neoflix bundle install heroku apps:create neoflix --stack cedar heroku addons:add neo4j git push heroku master
Давайте убедимся, что Neo4j был успешно добавлен в наш проект:
$ heroku addons logging:basic neo4j:test releases:basic
Отлично, это так (если вы читаете это в будущем, это может сказать neo4j: basic или neo4j: silver или что-то в этом роде). Так где же находится наша база данных Neo4j?
$ heroku config GEM_PATH => vendor/bundle/ruby/1.9.1 LANG => en_US.UTF-8 NEO4J_HOST => 70825a524.hosted.neo4j.org NEO4J_INSTANCE => 70825a524 NEO4J_LOGIN => xxxxxxxx NEO4J_PASSWORD => yyyyyyyy NEO4J_PORT => 7014 NEO4J_REST_URL => http://xxxxxxxx:[email protected]:7014/db/data NEO4J_URL => http://xxxxxxxx:[email protected]:7014 PATH => bin:vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/bin RACK_ENV => production
Xs и ys — это наше имя пользователя и пароль. Мы можем использовать адрес, указанный в NEO4J_URL, чтобы взглянуть на сервер. Для второй части было бы разумно следить за «приборной панелью», так как мы создаем новые узлы и отношения. Макет проекта Neoflix:
neoflix.rb public/movies.dat public/users.dat public/ratings.dat
Давайте посмотрим на исходный код в neoflix.rb: нам нужны наши гемы и мы используем переменную NEO4J_URL, чтобы сообщить Neography, как добраться до сервера Neo4j.
require 'rubygems' require 'neography' require 'sinatra' neo = Neography::Rest.new(ENV['NEO4J_URL'] || "http://localhost:7474")
Затем мы создаем маршрут в Синатре, который очистит и заполнит график, когда мы посетим его.
get '/create_graph' do neo.execute_script("g.clear();") create_graph(neo) end
Мы используем ярлык Gremlin, чтобы удалить график перед его созданием.
g.clear();
Функция резервного копирования и восстановления дополнения Heroku также позволяет перезагрузить график, но экземпляр Neo4j будет временно недоступен во время обмена.
Если вы хотите окончательно удалить экземпляр Neo4j (как только вы закончите с этим примером приложения), вы можете просто удалить дополнение heroku.
heroku addons:remove neo4j:test Removing neo4j:test from neoflix...done.
Давайте посмотрим на часть метода create_graph.
Мы не хотим создавать граф, если он уже существует. Поэтому мы проверяем, есть ли какие-либо узлы Movie перед началом.
def create_graph(neo) return if neo.execute_script("g.idx('vertices')[[type:'Movie']].count();").to_i > 0
Поскольку мы все очистили, мы настроили автоматическое индексирование по всем вершинам и всем свойствам.
if neo.execute_script("g.indices;").empty? neo.execute_script("g.createAutomaticIndex('vertices', Vertex.class, null);") end
Мы собираемся создать большое количество данных, поэтому мы настроим наш график на фиксацию каждые 1000 изменений в автоматической транзакции .
g.setMaxBufferSize(1000);
Здесь приходит немного магии. У нас нет доступа к файловой системе сервера, на котором работает наш экземпляр Neo4j, но, поскольку у нас есть все возможности Groovy, мы просто берем файл из Sinatra. Все, что вы поместите в публичный каталог, будет автоматически предоставлено вам. Поля movies.dat разделены «::», а роды — «|».
1::Toy Story (1995)::Animation|Children's|Comedy 2::Jumanji (1995)::Adventure|Children's|Fantasy 3::Grumpier Old Men (1995)::Comedy|Romance
Поэтому для каждой строки в нашем файле мы собираемся создать вершину фильма и связать ее с одним или несколькими родами. Мы отправляем этот скрипт Gremlin внутри строки Ruby, поэтому мы должны избегать escape-слэшей, которые выходят из | в финальном сценарии. По мере продвижения мы также создаем вершины для родов, если они еще не существуют.
'http://neoflix.heroku.com/movies.dat'.toURL().eachLine { def line -> def components = line.split('::'); def movieVertex = g.addVertex(['type':'Movie', 'movieId':components[0].toInteger(), 'title':components[1]]); components[2].split('\\\\|').each { def genera -> def hits = g.idx(Tokens.T.v)[[genera:genera]].iterator(); def generaVertex = hits.hasNext() ? hits.next() : g.addVertex(['type':'Genera', 'genera':genera]); g.addEdge(movieVertex, generaVertex, 'hasGenera'); } };
Если вы Rubyist, вы сможете прочитать этот Groovy-код, но позвольте мне указать на несколько вещей. В определениях переменных Groovy необходимо либо явно указывать имя типа, либо использовать «def» вместо.
И этот прикольный фрагмент кода является неудачным выходом символа канала из-за обратной косой черты, которая также должна быть экранирована, которые находятся в нашей строке Ruby и также должны быть экранированы.
components[2].split('\\\\|').each { def genera ->
Следующий фрагмент кода ищет роды в нашем индексе, и, если он не существует, он создает его.
def hits = g.idx(Tokens.T.v)[[genera:genera]].iterator(); def generaVertex = hits.hasNext() ? hits.next() : g.addVertex(['type':'Genera', 'genera':genera]);
Этот Hash внутри Array внутри конструкции, выглядящей как Array, является способом Gremlins для запроса индекса. Мы говорим ему возвращать узел, если у него есть свойства родов, которые соответствуют переменной родов, которую мы проанализировали после разделения поля компоненты [2].
g.idx(Tokens.T.v)[[genera:genera]].iterator();
Мы делаем это еще несколько раз, чтобы загрузить пользователей и рейтинги в наш график, и заканчиваем этим:
g.stopTransaction(TransactionalGraph.Conclusion.SUCCESS);")
Который фиксирует любые оставшиеся элементы в нашем буфере транзакций.
Во второй части мы покажем наше приложение Heroku, загрузим данные, возможно добавим постеры фильмов из стороннего API и представим некоторые неявные отношения на графике, как описано в оригинальном сообщении в блоге… и я, вероятно, выполните третью часть, которая будет использовать только что импортированный CSV File Importer и перезагрузите график с большим набором данных фильма, используя Heroku. Между тем, однако, я думаю, что пришло время посмотреть на Neo4j Spatial . Вы будете знать, когда новые публикации будут опубликованы, следуя за мной в Twitter.
Источник: http://maxdemarzi.com/2012/01/13/neo4j-on-heroku-part-one/