diff --git a/README.md b/README.md index 37c4baf..687aeec 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,16 @@ [![Build Status](https://secure.travis-ci.org/elado/neoid.png)](http://travis-ci.org/elado/neoid) +## This fork only + +This fork was made so that neoid supports a JSON type field in the database. +This 's based on PostgreSQL 9.2 and Rails 4.0. + +You need to have a PostgreSQL db availale (as PG can't be run in memory like SQLite3). +Currently, I'm manually dropping/creating the db everytime I run the test, which is time-consuming. + +# Reame + Make your ActiveRecords stored and searchable on Neo4j graph database, in order to make fast graph queries that MySQL would crawl while doing them. @@ -85,7 +95,7 @@ Then, you can customize what fields will be saved on the node in Neo4j, inside ` ```ruby class User < ActiveRecord::Base include Neoid::Node - + neoidable do |c| c.field :slug c.field :display_name @@ -182,7 +192,7 @@ If you'd like to save nodes manually rather than after_save, use `auto_index: fa ```ruby class User < ActiveRecord::Base include Neoid::Node - + neoidable auto_index: false do |c| end end diff --git a/lib/neoid/model_additions.rb b/lib/neoid/model_additions.rb index 14b3a4a..04486fe 100644 --- a/lib/neoid/model_additions.rb +++ b/lib/neoid/model_additions.rb @@ -2,11 +2,11 @@ module Neoid module ModelAdditions module ClassMethods attr_reader :neoid_config - + def neoid_config @neoid_config ||= Neoid::ModelConfig.new(self) end - + def neoidable(options = {}) # defaults neoid_config.auto_index = true @@ -25,7 +25,7 @@ def neo_model_index_name @index_name ||= "#{self.name.tableize}_index" end end - + module InstanceMethods def to_neo if self.class.neoid_config.stored_fields @@ -35,10 +35,19 @@ def to_neo else self.send(field) rescue (raise "No field #{field} for #{self.class.name}") end - + all end + # optional: if there are fields declared with json_field, merge their hash content to result + if self.class.neoid_config.stored_json_fields + json_fields_content = self.class.neoid_config.stored_json_fields.inject({}) do |all, field| + json_data = self.send(field) + all.merge(json_data) + end + hash.merge!(json_fields_content) + end + hash.reject { |k, v| v.nil? } else {} diff --git a/lib/neoid/model_config.rb b/lib/neoid/model_config.rb index a2e9820..2c98aec 100644 --- a/lib/neoid/model_config.rb +++ b/lib/neoid/model_config.rb @@ -5,34 +5,42 @@ class ModelConfig attr_reader :relationship_options attr_accessor :enable_model_index attr_accessor :auto_index - + def initialize(klass) @klass = klass end - + def stored_fields @stored_fields ||= {} end - + def field(name, &block) self.stored_fields[name] = block end - + + def stored_json_fields + @stored_json_fields ||= [] + end + + def json_field(name) + self.stored_json_fields << name + end + def relationship(options) @relationship_options = options end - + def search(&block) raise "search needs a block" unless block_given? @search_options = SearchConfig.new block.(@search_options) end - + def inspect "#" end end - + class SearchConfig def index_fields @index_fields ||= {} @@ -41,15 +49,15 @@ def index_fields def fulltext_fields @fulltext_fields ||= {} end - + def index(field, options = {}, &block) index_fields[field] = options.merge(block: block) end - + def fulltext(field, options = {}, &block) fulltext_fields[field] = options.merge(block: block) end - + def inspect "#" end diff --git a/lib/neoid/node.rb b/lib/neoid/node.rb index 851e9d1..47632a3 100644 --- a/lib/neoid/node.rb +++ b/lib/neoid/node.rb @@ -77,14 +77,14 @@ def neo_search(term, options = {}) Neoid.search(self, term, options) end end - + module InstanceMethods def neo_find_by_id # Neoid::logger.info "Node#neo_find_by_id #{self.class.neo_index_name} #{self.id}" node = Neoid.db.get_node_auto_index(Neoid::UNIQUE_ID_KEY, self.neo_unique_id) node.present? ? Neoid::Node.from_hash(node[0]) : nil end - + def _neo_save return unless Neoid.enabled? @@ -174,11 +174,11 @@ def neo_helper_get_field_value(field, options = {}) self.send(field) rescue (raise "No field #{field} for #{self.class.name}") end end - + def neo_load(hash) Neoid::Node.from_hash(hash) end - + def neo_node _neo_representation end @@ -199,7 +199,7 @@ def neo_after_relationship_through_remove(record) @__neo_temp_rels.delete(record) end end - + def self.included(receiver) receiver.send :include, Neoid::ModelAdditions receiver.extend ClassMethods diff --git a/spec/neoid/model_config_spec.rb b/spec/neoid/model_config_spec.rb index ad970d0..79298bc 100644 --- a/spec/neoid/model_config_spec.rb +++ b/spec/neoid/model_config_spec.rb @@ -15,9 +15,14 @@ Article.neoid_config.stored_fields[:title_length].should be_a(Proc) end + it "should store stored json fields" do + NodeWithJson.neoid_config.stored_json_fields.should_not be_nil + NodeWithJson.neoid_config.stored_json_fields.should include(:data) + end + it "should store stored fields based on blocks" do article = Article.create! title: "Hello", year: 2012 - + article.neo_node.title_length.should == article.title.length end end diff --git a/spec/neoid/node_spec.rb b/spec/neoid/node_spec.rb index c102397..6db2fa1 100644 --- a/spec/neoid/node_spec.rb +++ b/spec/neoid/node_spec.rb @@ -12,7 +12,7 @@ user = User.create!(name: "Elad Ossadon", slug: "elado") user.neo_node.should_not be_nil - + user.neo_node.ar_id.should == user.id user.neo_node.name.should == user.name user.neo_node.slug.should == user.slug @@ -22,11 +22,21 @@ movie = Movie.create!(name: "Memento", slug: "memento-1999", year: 1999) movie.neo_node.should_not be_nil - + movie.neo_node.ar_id.should == movie.id movie.neo_node.name.should == movie.name movie.neo_node.year.should == movie.year end + + it "should create a neo_node for node with json field" do + node = NodeWithJson.create!(data: {key1: "value1", key2: 2}, node_type: "value 3") + + node.neo_node.should_not be_nil + node.neo_node.ar_id.should == node.id + node.neo_node.node_type.should == "value 3" + node.neo_node.key1.should == "value1" + node.neo_node.key2.should == 2 + end end context "update graph nodes" do @@ -51,7 +61,7 @@ context "find by id" do it "should find a neo_node for user" do user = User.create!(name: "Elad Ossadon", slug: "elado") - + user.neo_node.should_not be_nil user.neo_find_by_id.should_not be_nil end @@ -70,7 +80,7 @@ old, Neoid.config.enable_subrefs = Neoid.config.enable_subrefs, true Neoid.send(:initialize_subrefs) - + begin Neoid.ref_node.rel(:outgoing, :users_subref).should_not be_nil ensure @@ -82,7 +92,7 @@ old, Neoid.config.enable_subrefs = Neoid.config.enable_subrefs, true Neoid.send(:initialize_subrefs) - + begin user = User.create!(name: "Elad") user.neo_node.rel(:incoming, :users).should_not be_nil diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0605e36..213cc8a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,9 @@ require 'neoid' +require 'pg' require 'active_record' require 'neography' require 'rest-client' +require 'pg' # ENV['NEOID_LOG'] = 'true' @@ -21,9 +23,19 @@ Neoid.db = $neo +# establish a connection to sqlite3 (in memory) +# logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, Logger.new('/dev/null') +# ActiveRecord::Base.configurations = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'support/database.yml'))) +# ActiveRecord::Base.establish_connection('sqlite3') + +# establish connection to a PostgreSQL db (persistent) logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, Logger.new('/dev/null') -ActiveRecord::Base.configurations = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'support/database.yml'))) -ActiveRecord::Base.establish_connection('sqlite3') +adapter = 'postgresql' +db_config = YAML.load(IO.read(File.join(File.dirname(__FILE__), 'support/database.yml')))[adapter] +ActiveRecord::Base.establish_connection(db_config) +# config = ActiveRecord::Base.connection.pool.spec.config + + require 'support/schema' require 'support/models' @@ -35,7 +47,7 @@ config.before(:all) do end - + config.before(:each) do Neoid.node_models.each(&:destroy_all) Neoid.clean_db(:yes_i_am_sure) diff --git a/spec/support/database.yml b/spec/support/database.yml index a7f4058..427bf0a 100644 --- a/spec/support/database.yml +++ b/spec/support/database.yml @@ -3,4 +3,15 @@ sqlite3: database: ":memory:" encoding: utf8 charset: utf8 - timeout: 5000 \ No newline at end of file + timeout: 5000 + +postgresql: + adapter: postgresql + database: neoid_in_development + encoding: utf8 + charset: utf8 + timeout: 5000 + pool: 5 + username: postgres + password: password + host: localhost diff --git a/spec/support/models.rb b/spec/support/models.rb index 800c84f..47c60ea 100644 --- a/spec/support/models.rb +++ b/spec/support/models.rb @@ -1,26 +1,26 @@ class User < ActiveRecord::Base include ActiveModel::Validations::Callbacks - + has_many :likes has_many :movies, through: :likes has_many :user_follows - + def likes?(movie) likes.where(movie_id: movie.id).exists? end - + def like!(movie) movies << movie unless likes?(movie) likes.where(movie_id: movie.id).first end - + def unlike!(movie) likes.where(movie_id: movie.id, user_id: self.id).destroy_all end - + include Neoid::Node - + neoidable do |c| c.field :name c.field :slug @@ -29,12 +29,12 @@ def unlike!(movie) class Movie < ActiveRecord::Base include ActiveModel::Validations::Callbacks - + has_many :likes has_many :users, through: :likes - + include Neoid::Node - + neoidable do |c| c.field :name c.field :slug @@ -55,9 +55,9 @@ class UserFollow < ActiveRecord::Base belongs_to :user belongs_to :item, polymorphic: true - + include Neoid::Relationship - + neoidable do |c| c.relationship start_node: :user, end_node: :item, type: :follows end @@ -65,12 +65,12 @@ class UserFollow < ActiveRecord::Base class Like < ActiveRecord::Base include ActiveModel::Validations::Callbacks - + belongs_to :user belongs_to :movie - + include Neoid::Relationship - + neoidable do |c| c.relationship start_node: :user, end_node: :movie, type: :likes c.field :rate @@ -104,3 +104,13 @@ class NoAutoIndexNode < ActiveRecord::Base c.field :name end end + +class NodeWithJson < ActiveRecord::Base + include ActiveModel::Validations::Callbacks + include Neoid::Node + + neoidable do |c| + c.json_field :data + c.field :node_type + end +end diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 97154a6..22bc082 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -1,4 +1,8 @@ ActiveRecord::Schema.define :version => 0 do + # enable JSON field to be created (Matching PostgreSQL 9.2 and up) + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + create_table :users do |t| t.string :name t.string :slug @@ -41,4 +45,9 @@ create_table :no_auto_index_nodes do |t| t.string :name end + + create_table :node_with_jsons do |t| + t.json :data + t.string :node_type + end end