From e21a3fe0cd91dfeae76bce4d58c7611e78b60fbf Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 14 Oct 2016 23:10:07 +0200 Subject: [PATCH] Adding sync of follow relationships to Neo4J, accounts/suggestions API --- .gitignore | 1 + Gemfile | 1 + Gemfile.lock | 15 ++++++++++-- .../components/actions/suggestions.jsx | 15 ++++++++++++ app/controllers/api/v1/accounts_controller.rb | 6 ++++- app/models/follow.rb | 24 +++++++++++++++++++ app/models/follow_suggestion.rb | 7 ++++++ app/views/api/v1/accounts/suggestions.rabl | 2 ++ config/application.rb | 4 ++-- config/initializers/neography.rb | 5 ++++ config/routes.rb | 1 + 11 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 app/assets/javascripts/components/actions/suggestions.jsx create mode 100644 app/models/follow_suggestion.rb create mode 100644 app/views/api/v1/accounts/suggestions.rabl create mode 100644 config/initializers/neography.rb diff --git a/.gitignore b/.gitignore index 340657ee9c..5dabc59d94 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ public/assets .env .env.* node_modules/ +neo4j/ diff --git a/Gemfile b/Gemfile index f145531a3e..1cab16a391 100644 --- a/Gemfile +++ b/Gemfile @@ -39,6 +39,7 @@ gem 'will_paginate' gem 'rack-attack' gem 'sidekiq' gem 'ledermann-rails-settings' +gem 'neography' gem 'react-rails' gem 'browserify-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 43e36cc72d..1e6068a445 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,6 +97,7 @@ GEM dotenv (= 2.1.1) railties (>= 4.0, < 5.1) erubis (2.7.0) + excon (0.53.0) execjs (2.7.0) fabrication (2.15.2) fast_blank (1.0.0) @@ -165,13 +166,21 @@ GEM mime-types-data (3.2016.0521) mimemagic (0.3.0) mini_portile2 (2.1.0) - minitest (5.9.0) + minitest (5.9.1) multi_json (1.12.1) + neography (1.8.0) + excon (>= 0.33.0) + json (>= 1.7.7) + multi_json (>= 1.3.2) + os (>= 0.9.6) + rake (>= 0.8.7) + rubyzip (>= 1.0.0) nio4r (1.2.1) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) oj (2.17.3) orm_adapter (0.5.0) + os (0.9.6) ostatus2 (1.0.1) addressable (~> 2.4) http (~> 2.0) @@ -236,7 +245,7 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.1.0) - rake (11.2.2) + rake (11.3.0) rdoc (4.2.2) json (~> 1.4) react-rails (1.8.2) @@ -281,6 +290,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-progressbar (1.8.1) + rubyzip (1.2.0) safe_yaml (1.0.4) sass (3.4.22) sass-rails (5.0.6) @@ -370,6 +380,7 @@ DEPENDENCIES letter_opener link_header lograge + neography nokogiri oj ostatus2 diff --git a/app/assets/javascripts/components/actions/suggestions.jsx b/app/assets/javascripts/components/actions/suggestions.jsx new file mode 100644 index 0000000000..562a901c5c --- /dev/null +++ b/app/assets/javascripts/components/actions/suggestions.jsx @@ -0,0 +1,15 @@ +import api from '../api'; + +export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST'; +export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS'; +export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL'; + +export function fetchSuggestions() { + return (dispatch, getState) => { + api(getState).get('/api/v1/accounts/suggestions').then(response => { + console.log(response.data); + }).catch(error => { + console.error(error); + }); + }; +}; diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 930f60cc30..7757fd7f8a 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -1,6 +1,6 @@ class Api::V1::AccountsController < ApiController before_action :doorkeeper_authorize! - before_action :set_account, except: :verify_credentials + before_action :set_account, except: [:verify_credentials, :suggestions] respond_to :json def show @@ -19,6 +19,10 @@ class Api::V1::AccountsController < ApiController @followers = @account.followers end + def suggestions + @accounts = FollowSuggestion.get(current_user.account_id) + end + def statuses @statuses = @account.statuses.with_includes.with_counters.paginate_by_max_id(20, params[:max_id], params[:since_id]).to_a end diff --git a/app/models/follow.rb b/app/models/follow.rb index 4f47580e17..1c52f24c02 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -22,4 +22,28 @@ class Follow < ApplicationRecord def title destroyed? ? "#{account.acct} is no longer following #{target_account.acct}" : "#{account.acct} started following #{target_account.acct}" end + + after_create :add_to_graph + after_destroy :remove_from_graph + + private + + def add_to_graph + neo = Neography::Rest.new + + a = neo.create_unique_node('account_index', 'Account', account_id.to_s, account_id: account_id) + b = neo.create_unique_node('account_index', 'Account', target_account_id.to_s, account_id: target_account_id) + + neo.create_unique_relationship('follow_index', 'Follow', id.to_s, 'follows', a, b) + rescue Neography::NeographyError => e + Rails.logger.error e + end + + def remove_from_graph + neo = Neography::Rest.new + rel = neo.get_relationship_index('follow_index', 'Follow', id.to_s) + neo.delete_relationship(rel) + rescue Neography::NeographyError => e + Rails.logger.error e + end end diff --git a/app/models/follow_suggestion.rb b/app/models/follow_suggestion.rb new file mode 100644 index 0000000000..2f291bc491 --- /dev/null +++ b/app/models/follow_suggestion.rb @@ -0,0 +1,7 @@ +class FollowSuggestion + def self.get(for_account_id) + neo = Neography::Rest.new + account_ids = neo.execute_query('START a=node:account_index(Account={id}) MATCH (a)-[:follows]->(b)-[:follows]->(c) WHERE a <> c AND NOT (a)-[:follows]->(c) RETURN DISTINCT c.account_id', id: for_account_id) + Account.where(id: account_ids['data'].first) unless account_ids.empty? + end +end diff --git a/app/views/api/v1/accounts/suggestions.rabl b/app/views/api/v1/accounts/suggestions.rabl new file mode 100644 index 0000000000..f4dc121ea5 --- /dev/null +++ b/app/views/api/v1/accounts/suggestions.rabl @@ -0,0 +1,2 @@ +collection @accounts +extends('api/v1/accounts/show') diff --git a/config/application.rb b/config/application.rb index b1c9d9f6f9..3734ce9282 100644 --- a/config/application.rb +++ b/config/application.rb @@ -22,8 +22,8 @@ module Mastodon # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de - config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') - config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] + # config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') + # config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] config.active_job.queue_adapter = :sidekiq diff --git a/config/initializers/neography.rb b/config/initializers/neography.rb new file mode 100644 index 0000000000..25d54658b2 --- /dev/null +++ b/config/initializers/neography.rb @@ -0,0 +1,5 @@ +Neography.configure do |config| + config.protocol = "http" + config.server = ENV['NEO4J_HOST'] || 'localhost' + config.port = ENV['NEO4J_PORT'] || 7474 +end diff --git a/config/routes.rb b/config/routes.rb index ec0309dc7b..f3708938ac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -73,6 +73,7 @@ Rails.application.routes.draw do collection do get :relationships get :verify_credentials + get :suggestions end member do