Статьи

Neo4j на Heroku — Часть 1


В своем блоге
Марко А. Родригес показал нам, как создать
движок для графического рекомендателя фильмов на основе 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/