commit: 447033038549495318f7c218941829e845aabb61
parent: f9c9fef1575af3b97cbb300acae60272affc7730
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 16 Oct 2016 17:04:13 +0200
Backfill follow suggestions with fallback when not enough results. Cycling
through suggestions in UI
Diffstat:
2 files changed, 65 insertions(+), 23 deletions(-)
diff --git a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx
@@ -6,7 +6,8 @@ import { Link } from 'react-router';
const outerStyle = {
marginBottom: '10px',
- borderTop: '1px solid #616b86'
+ borderTop: '1px solid #616b86',
+ position: 'relative'
};
const headerStyle = {
@@ -41,26 +42,64 @@ const acctStyle = {
textOverflow: 'ellipsis'
};
+const nextStyle = {
+ fontWeight: '400',
+ color: '#2b90d9'
+};
+
const SuggestionsBox = React.createClass({
propTypes: {
- accounts: ImmutablePropTypes.list.isRequired
+ accounts: ImmutablePropTypes.list.isRequired,
+ perWindow: React.PropTypes.number
+ },
+
+ getInitialState () {
+ return {
+ index: 0
+ };
+ },
+
+ getDefaultProps () {
+ return {
+ perWindow: 2
+ };
},
mixins: [PureRenderMixin],
+ handleNextClick (e) {
+ e.preventDefault();
+
+ let newIndex = this.state.index + 1;
+
+ if (this.props.accounts.skip(this.props.perWindow * newIndex).size === 0) {
+ newIndex = 0;
+ }
+
+ this.setState({ index: newIndex });
+ },
+
render () {
- const accounts = this.props.accounts.take(3);
+ const { accounts, perWindow } = this.props;
if (accounts.size === 0) {
return <div />;
}
+ let nextLink = '';
+
+ if (accounts.size > perWindow) {
+ nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}>Next</a>;
+ }
+
return (
<div style={outerStyle}>
- <strong style={headerStyle}>Who to follow</strong>
+ <strong style={headerStyle}>
+ Who to follow {nextLink}
+ </strong>
- {accounts.map(account => {
+ {accounts.skip(perWindow * this.state.index).take(perWindow).map(account => {
let displayName = account.get('display_name');
if (displayName.length === 0) {
diff --git a/app/models/follow_suggestion.rb b/app/models/follow_suggestion.rb
@@ -14,9 +14,17 @@ END
results = neo.execute_query(query, id: for_account_id, limit: limit)
- return fallback(for_account_id, limit) if results.empty? || results['data'].empty?
+ if results.empty?
+ results = fallback(for_account_id, limit)
+ elsif results['data'].size < limit
+ results['data'] = (results['data'] + fallback(for_account_id, limit - results['data'].size)['data']).uniq
+ end
- map_to_accounts(for_account_id, results)
+ account_ids = results['data'].map(&:first)
+ blocked_ids = Block.where(account_id: for_account_id).pluck(:target_account_id)
+ accounts_map = Account.where(id: account_ids - blocked_ids).map { |a| [a.id, a] }.to_h
+
+ account_ids.map { |id| accounts_map[id] }.compact
rescue Neography::NeographyError, Excon::Error::Socket => e
Rails.logger.error e
return []
@@ -25,23 +33,18 @@ END
private
def self.fallback(for_account_id, limit)
- neo = Neography::Rest.new
- query = 'MATCH (a) WHERE a.account_id <> {id} RETURN a.account_id ORDER BY a.nodeRank DESC LIMIT {limit}'
- results = neo.execute_query(query, id: for_account_id, limit: limit)
-
- map_to_accounts(for_account_id, results)
- rescue Neography::NeographyError, Excon::Error::Socket => e
- Rails.logger.error e
- return []
- end
-
- def self.map_to_accounts(for_account_id, results)
- return [] if results.empty? || results['data'].empty?
+ neo = Neography::Rest.new
- account_ids = results['data'].map(&:first)
- blocked_ids = Block.where(account_id: for_account_id).pluck(:target_account_id)
- accounts_map = Account.where(id: account_ids - blocked_ids).map { |a| [a.id, a] }.to_h
+ query = <<END
+START a=node:account_index(Account={id})
+MATCH (b)
+WHERE a <> b
+AND NOT (a)-[:follows]->(b)
+RETURN b.account_id
+ORDER BY b.nodeRank DESC
+LIMIT {limit}
+END
- account_ids.map { |id| accounts_map[id] }.compact
+ neo.execute_query(query, id: for_account_id, limit: limit)
end
end