module Neo4j class Session @@current_session = nil @@all_sessions = {} @@factories = {} # @abstract def close self.class.unregister(self) end # Only for embedded database # @abstract def start raise "not impl." end # Only for embedded database # @abstract def shutdown raise "not impl." end # Only for embedded database # @abstract def running raise "not impl." end # @return [:embedded_db | :server_db] def db_type raise "not impl." end def auto_commit? true # TODO end # @abstract def begin_tx raise "not impl." end class CypherError < StandardError attr_reader :error_msg, :error_status, :error_code def initialize(error_msg, error_code, error_status) super(error_msg) @error_msg = error_msg @error_status = error_status end end # Performs a cypher query. See {Neo4j::Core::Query} for more details, but basic usage looks like: # # @example Using cypher DSL # Neo4j::Session.query.match("(c:person)-[:friends]->(p:person)").where(c: {name: 'andreas'}).pluck(:p).first[:name] # # @example Show the generated Cypher # Neo4j::Session.query..match("(c:person)-[:friends]->(p:person)").where(c: {name: 'andreas'}).return(:p).to_cypher # # @example Use Cypher string instead of the cypher DSL # Neo4j::Session.query("MATCH (c:person)-[:friends]->(p:person) WHERE c.name = \"andreas\" RETURN p").first[:p][:name] # # @return [Neo4j::Core::Query, Enumerable] return a Query object for DSL or a Enumerable if using raw cypher strings # @see http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html The Cypher Query Language Documentation # def query(options = {}) raise 'not implemented, abstract' end # Same as #query but does not accept an DSL and returns the raw result from the database. # Notice, it might return different values depending on which database is used, embedded or server. # @abstract def _query(*params) raise 'not implemented' end class << self # Creates a new session to Neo4j. # This will be the default session to be used unless there is already a session created (see #current and #set_current) # # @example A Neo4j Server session # Neo4j::Session.open(:server_db, 'http://localhost:7474', {basic_auth: {username: 'foo', password: 'bar'}}) # # @example Using a user defined Faraday HTTP connection # connection = Faraday.new do |b| # # faraday config # end # Neo4j::Session.open(:server_db, 'http://localhost:7474', connection: connection) # # @example A embedded Neo4j session # Neo4j::Session.open(:embedded_db, 'path/to/db') # # @see also Neo4j::Server::CypherSession#open for :server_db params # @param db_type the type of database, e.g. :embedded_db, or :server_db def open(db_type=:server_db, *params) register(create_session(db_type, params)) end def open_named(db_type, name, default = nil, *params) raise "Multiple sessions is currently only supported for Neo4j Server connections." unless db_type == :server_db register(create_session(db_type, params), name, default) end # @private def create_session(db_type, params = {}) unless (@@factories[db_type]) raise "Can't connect to database '#{db_type}', available #{@@factories.keys.join(',')}" end @@factories[db_type].call(*params) end # @return [Neo4j::Session] the current session def current @@current_session end # Returns the current session or raise an exception if no session is available def current! raise "No session, please create a session first with Neo4j::Session.open(:server_db) or :embedded_db" unless current current end # @see Neo4j::Session#query def query(*args) current!.query(*args) end # Returns a session with given name or else raise an exception def named(name) @@all_sessions[name] || raise("No session named #{name}.") end # Sets the session to be used as default # @param [Neo4j::Session] session the session to use def set_current(session) @@current_session = session end # Registers a callback which will be called immediately if session is already available, # or called when it later becomes available. def on_session_available(&callback) if (Neo4j::Session.current) callback.call(Neo4j::Session.current) end add_listener do |event, data| callback.call(data) if event == :session_available end end # @private def add_listener(&listener) self._listeners << listener end # @private def _listeners @@listeners ||= [] @@listeners end # @private def _notify_listeners(event, data) _listeners.each {|li| li.call(event, data)} end # @private def register(session, name = nil, default = nil) if default == true set_current(session) elsif default.nil? set_current(session) unless @@current_session end @@all_sessions[name] = session if name session end # @private def unregister(session) @@current_session = nil if @@current_session == session end def inspect "Neo4j::Session available: #{@@factories && @@factories.keys}" end # @private def register_db(db, &session_factory) puts "replace factory for #{db}" if @@factories[db] @@factories[db] = session_factory end end end end