Статьи

Использование базы данных Graph с Ruby, часть II: интеграция

Да, это реально, это, наконец, здесь! Долгожданное продолжение вступительной статьи уже здесь, как раз к новому году!

В первой статье мы узнали о графовых базах данных, их различиях и преимуществах по сравнению с традиционными базами данных и о Neo4j. В этой статье мы собираемся установить Neo4j, интегрировать и оценивать драгоценные камни, перечисленные в первой части этой серии.

Сценарий, с которым мы собираемся работать, является продолжением простой идеи из первой статьи , примера социальной сети, которая способна производить обходные запросы, такие как «учитывая тот факт, что Боб мой друг, дай мне всех друзей, которые друг друга друг друга Боба ».

Установка Neo4j

Установка сервера Neo4j довольно проста. Вы можете скачать автономный сервер здесь или установить его с помощью Homebrew . Для этой статьи и для простоты мы собираемся использовать Homebrew:

$ brew update && brew install neo4j

После завершения установки доступны следующие команды:

Запустите сервер:

 $ neo4j start

Остановите сервер:

 $ neo4j stop

Откройте webadmin, который позволяет вам манипулировать данными с помощью веб-интерфейса:

 $ open http://localhost:7474/webadmin/

Использование Neo4j с Ruby

Теперь, когда сервер установлен, следующим шагом будет интеграция с Neo4j. Как уже упоминалось во вступительной статье , мы будем оценивать драгоценные камни: Neo4j.rb , Neoid и Neography .

Neo4j.rb

Что такое Neo4j.rb , по мнению автора:

Вы можете думать о Neo4j как о высокопроизводительном графическом движке со всеми функциями зрелой и надежной базы данных. Программист работает с объектно-ориентированной, гибкой сетевой структурой, а не со строгими и статичными таблицами, но при этом пользуется всеми преимуществами полностью транзакционной базы данных корпоративного уровня.

Этот драгоценный камень действительно обширный, и я призываю вас изучить все его возможности. В этой статье я остановлюсь только на одном аспекте: узлах и отношениях.

Предпосылки

Этот драгоценный камень требует JRuby . Если вы используете RVM , вы можете легко установить его с помощью:

 $ rvm install jruby

Затем переключитесь на JRuby, выполнив:

 $ rvm use jruby

Установка Neo4j.rb

Вы можете добавить neo4j

 $ gem 'neo4j'

И запустите Bundler:

 $ bundle

Или установить вручную с помощью:

 $ gem install neo4j

интеграция

Прежде чем мы сможем создать наши узлы и наши отношения, нам нужно, чтобы наш код был частью транзакции. Это необходимо, потому что Neo4j имеет транзакции ACID .

Поскольку наш пример относительно прост, мы собираемся инкапсулировать наш код для запуска внутри блока Neo4j :: Transaction :

 require 'rubygems'
require 'bundler/setup'
require 'neo4j'

Neo4j::Transaction.run do
  # our code here
end

Следующее, что мы собираемся сделать, это создать несколько узлов, которые будут представлять наших пользователей:

 require 'rubygems'
require 'bundler/setup'
require 'neo4j'

Neo4j::Transaction.run do
  me   = Neo4j::Node.new(:name => 'Me',   :age => 31)
  bob  = Neo4j::Node.new(:name => 'Bob',  :age => 29)
  mark = Neo4j::Node.new(:name => 'Mark', :age => 34)
  mary = Neo4j::Node.new(:name => 'Mary', :age => 32)
  john = Neo4j::Node.new(:name => 'John', :age => 33)
  andy = Neo4j::Node.new(:name => 'Andy', :age => 31)
end

Далее мы создаем дружеские отношения между пользователями:

 require 'rubygems'
require 'bundler/setup'
require 'neo4j'

Neo4j::Transaction.run do
  me   = Neo4j::Node.new(:name => 'Me',   :age => 31)
  bob  = Neo4j::Node.new(:name => 'Bob',  :age => 29)
  mark = Neo4j::Node.new(:name => 'Mark', :age => 34)
  mary = Neo4j::Node.new(:name => 'Mary', :age => 32)
  john = Neo4j::Node.new(:name => 'John', :age => 33)
  andy = Neo4j::Node.new(:name => 'Andy', :age => 31)

  me.both(:friends)   << bob
  bob.both(:friends)  << mark
  mark.both(:friends) << mary
  mary.both(:friends) << john
  john.both(:friends) << andy
end

Вот как дружеские отношения между пользователями (узлами) в настоящее время представлены:

Последний шаг в этом примере — пройти через узлы и найти все дружеские отношения от Меня до Энди .

Полный пример выглядит так:

 require 'rubygems'
require 'bundler/setup'
require 'neo4j'

Neo4j::Transaction.run do
  me   = Neo4j::Node.new(:name => 'Me',   :age => 31)
  bob  = Neo4j::Node.new(:name => 'Bob',  :age => 29)
  mark = Neo4j::Node.new(:name => 'Mark', :age => 34)
  mary = Neo4j::Node.new(:name => 'Mary', :age => 32)
  john = Neo4j::Node.new(:name => 'John', :age => 33)
  andy = Neo4j::Node.new(:name => 'Andy', :age => 31)

  me.both(:friends)   << bob
  bob.both(:friends)  << mark
  mark.both(:friends) << mary
  mary.both(:friends) << john
  john.both(:friends) << andy   puts me.outgoing(:friends).depth(5).map{|node| node[:name]}.join(" => ")
end

Запуск примера

Давайте запустим наш пример и посмотрим, получим ли мы ожидаемый результат.

 $ ruby neo4j_example.rb

И, как и ожидалось, мы получаем следующий вывод:

 Me => Bob => Mark => Mary => John => Andy

Neography

Что такое неография , по мнению автора:

Neography — это тонкая оболочка Ruby для Neo4j Rest API

Этот драгоценный камень — отличная альтернатива, если вы не хотите использовать JRuby со своим приложением, но все же хотите использовать Neo4j. Однако у него есть свои ограничения, и автор рекомендует использовать Neo4j.rb , чтобы иметь доступ ко всем возможностям Neo4j.

Установка Неографии

Вы можете добавить neography

 gem 'neography'

И запустите Bundler:

 $ bundle

Или установить вручную с помощью:

 $ gem install neography

интеграция

Давайте начнем с создания нескольких пользовательских узлов:

 me   = Neography::Node.create(name: 'Me',   age: 31)
bob  = Neography::Node.create(name: 'Bob',  age: 29)
mark = Neography::Node.create(name: 'Mark', age: 34)
mary = Neography::Node.create(name: 'Mary', age: 32)
john = Neography::Node.create(name: 'John', age: 33)
andy = Neography::Node.create(name: 'Andy', age: 31)

Теперь давайте создадим дружеские отношения между узлами:

 me.both(:friends) << bob
bob.both(:friends) << mark
mark.both(:friends) << mary
mary.both(:friends) << john
john.both(:friends) << andy

Визуально вот как представлены отношения дружбы:

Далее мы пересекаем узлы, чтобы найти все дружеские отношения от Меня до Энди :

 me.all_simple_paths_to(andy).incoming(:friends).depth(5).nodes.each do |node|
  puts node.map{|n| n.name }.join(' => ')
end

И полный пример выглядит так:

 require 'rubygems'
require 'neography'

me   = Neography::Node.create(name: 'Me',   age: 31)
bob  = Neography::Node.create(name: 'Bob',  age: 29)
mark = Neography::Node.create(name: 'Mark', age: 34)
mary = Neography::Node.create(name: 'Mary', age: 32)
john = Neography::Node.create(name: 'John', age: 33)
andy = Neography::Node.create(name: 'Andy', age: 31)

me.both(:friends) << bob
bob.both(:friends) << mark
mark.both(:friends) << mary
mary.both(:friends) << john
john.both(:friends) << andy me.all_simple_paths_to(andy).incoming(:friends).depth(5).nodes.each do |node|   puts node.map{|n| n.name }.join(' => ')
end

Запуск сервера

Чтобы выполнить завершенный пример, нам сначала нужно инициализировать сервер Neo4j. Первый вариант — запустить сервер, который мы установили ранее с помощью Homebrew:

 $ neo4j start

Другой вариант — использовать грабли Neography для установки / запуска / остановки / перезапуска сервера. Для этого просто добавьте следующую строку в ваш Rakefile:

 require 'neography/tasks'

И вам будут доступны следующие задачи:

 rake neo4j:install[edition,version]  # Install Neo4j
rake neo4j:reset_yes_i_am_sure       # Reset the Neo4j Server
rake neo4j:restart                   # Restart the Neo4j Server
rake neo4j:start                     # Start the Neo4j Server
rake neo4j:stop                      # Stop the Neo4j Server

Запуск примера

Когда мы запустим пример:

 $ ruby neography_example.rb

Мы получаем следующий вывод:

 Me => Bob => Mark => Mary => John => Andy

Это показывает все пройденные узлы от Меня до Энди , как мы и ожидали.

Neoid

Что такое Неоид , по мнению автора:

Сделайте ваши ActiveRecords сохраненными и доступными для поиска в графовой базе данных Neo4j, чтобы делать быстрые графовые запросы, которые MySQL будет сканировать при их выполнении. Neoid to Neo4j — это как Sunspot для Solr. Вы получаете преимущества скорости Neo4j, сохраняя свою схему в своей простой старой СУБД.

Предпосылки

Поскольку Neoid работает с ActiveRecord, давайте начнем с создания простого приложения на Rails:

 $ rails new neoid_example -S -J

Установка Неоид

Вы можете добавить neoid

 gem 'neoid', git: 'git://github.com/elado/neoid.git'

И запустите Bundler:

 $ bundle

Запуск сервера

Поскольку Neoid работает на Neography , у нас есть те же опции, что и у серверов Neography. Мы можем либо запустить автономный сервер, либо включить грабли, предоставляемые Neography:

Запуск автономного сервера:

 $ neo4j start

Чтобы использовать rake-задачи Neography для установки / запуска / остановки / перезапуска сервера, просто добавьте следующую строку в ваш Rakefile:

 require 'neography/tasks'

И вам будут доступны следующие задачи:

 rake neo4j:install[edition,version]  # Install Neo4j
rake neo4j:reset_yes_i_am_sure       # Reset the Neo4j Server
rake neo4j:restart                   # Restart the Neo4j Server
rake neo4j:start                     # Start the Neo4j Server
rake neo4j:stop                      # Stop the Neo4j Server

интеграция

Теперь, когда у нас есть базовое приложение rails, следующим шагом будет создание двух моделей: User и Friendship . Первый будет содержать несколько основных атрибутов, таких как имя и возраст. Последний будет поддерживать дружеские отношения между пользователями.

Генерация пользовательской модели:

 $ rails g model user

Генерация модели дружбы:

 $ rails g model friendship

Далее нам нужно обновить миграцию для обеих моделей:

 # User migration

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :age
      t.timestamps
    end
  end
end
 # Friendship migration

class CreateFriendships < ActiveRecord::Migration
  def change
    create_table :friendships do |t|
      t.integer :user_id
      t.integer :friend_id
      t.timestamps
    end
  end
end

Теперь нам нужно добавить пользовательский инициализатор, который содержит конфигурацию, необходимую для Neoid. Автор рекомендует создать инициализатор в config/initializers/01_neo4j.rb

 require('neography') unless defined?(Neography)

ENV["NEO4J_URL"] ||= "http://localhost:7474"

uri = URI.parse(ENV["NEO4J_URL"])

neo = Neography::Rest.new(uri.to_s)

Neography.configure do |c|
  c.server = uri.host
  c.port = uri.port

  if uri.user && uri.password
    c.authentication = 'basic'
    c.username = uri.user
    c.password = uri.password
  end
end

Neoid.db = neo

Следующим шагом будет обновление модели User для включения Neoid и установки ее в качестве Node и одной ассоциации has_many :

 class User < ActiveRecord::Base
  include Neoid::Node

  attr_accessible :name, :age

  has_many :friends, class_name: Friendship

  neoidable do |c|
      c.field :name
    end
end

Теперь нам нужно обновить нашу модель Дружбы, включив в нее Neoid, и установить ее как Relationship , а также пару ассоциацийочных принадлежностей:

 class Friendship < ActiveRecord::Base
  include Neoid::Relationship

  attr_accessible :friend

  belongs_to :user
    belongs_to :friend, class_name: User

  neoidable do |c|
    c.relationship start_node: :user, end_node: :friend, type: :friends
  end

  class << self
    def create_both(user, friend)
      user.friends.create(friend: friend)
      friend.friends.create(friend: user)
    end
  end

end

Для последнего шага этой настройки нам просто нужно запустить миграцию:

 $ rake db:migrate

Теперь, когда настройка завершена, нам нужно добавить некоторые данные. Давайте откроем консоль rails и добавим несколько пользователей и несколько друзей.

Здесь мы создаем шесть пользователей.

 me   = User.create(name: 'Me',   age: 31)
bob  = User.create(name: 'Bob',  age: 29)
mark = User.create(name: 'Mark', age: 34)
mary = User.create(name: 'Mary', age: 32)
john = User.create(name: 'John', age: 33)
andy = User.create(name: 'Andy', age: 31)

Далее нам нужно создать дружеские отношения между пользователями:

 Friendship.create_both(me, bob)
Friendship.create_both(bob, mark)
Friendship.create_both(mark, mary)
Friendship.create_both(mary, john)
Friendship.create_both(john, andy)

Используя веб-интерфейс Neo4j, мы можем видеть, как взаимосвязи визуально представлены:

Запуск примера

Получив данные, нам нужно пройтись и найти всех друзей между мной и Энди . Для этого давайте обновим нашу модель User и добавим следующий метод экземпляра:

 def all_friends_to(user)
  neo_node.all_simple_paths_to(user.neo_node).incoming(:friends).depth(5).nodes.each do |node|
    puts node.map{|n| n.name }.join(' => ')
  end
end

Затем перезапустите консоль rails и выполните следующее:

 me   = User.first
andy = User.last
me.all_friends_to(andy)
=> "Me => Bob => Mark => Mary => John => Andy"

Как мы видим, это показывает все пройденные узлы от Меня до Энди , как и ожидалось.

Вывод

В этой статье демонстрируется, как установить Neo4j, и как понять, как интегрировать его с приложением Ruby / Rails, используя различные доступные решения. Несмотря на то, что приведенные здесь примеры едва касаются поверхности Neo4j, он, надеюсь, даст вам достаточно знаний и любопытства, чтобы начать интегрировать его в свои собственные проекты.

Надеюсь, вам понравилось читать эту статью так же, как мне понравилось ее писать. С новым годом!

Ресурсы