fa post factor calculator

This commit is contained in:
Dylan Knutson
2024-12-19 20:46:08 +00:00
parent b672b7ed68
commit a83b790386
15 changed files with 376 additions and 105 deletions

View File

@@ -5,8 +5,6 @@ services:
build:
context: ..
dockerfile: .devcontainer/Dockerfile.devcontainer
environment:
- RUBY_DEBUG_OPEN=true
volumes:
- ../..:/workspaces:cached
- ./fish-shell-conf-d:/home/vscode/.config/fish/conf.d

View File

@@ -95,7 +95,8 @@ module Domain::Fa::UsersHelper
def fa_user_account_status(user)
log_entry_id = user.log_entry_detail["last_user_page_id"]
return nil if log_entry_id.nil?
log_entry = HttpLogEntry.find(log_entry_id)
log_entry = HttpLogEntry.find_by(id: log_entry_id)
return nil if log_entry.nil?
parser =
Domain::Fa::Parser::Page.new(
log_entry.response.contents,

View File

@@ -0,0 +1,61 @@
class Domain::Fa::PostFactorCalculator
include HasMeasureDuration
def initialize(epochs = 20)
factors = Domain::Fa::PostFactor::FACTORS_WIDTHS
@recommender = Disco::Recommender.new(factors: factors, epochs: epochs)
logger.info "epochs=#{epochs.to_s.bold} factors=#{factors.to_s.bold}"
end
def fit
logger.info "loading fav rows..."
dataset =
measure(proc { |r| "loaded #{r.length.to_s.bold} favs" }) do
Domain::Fa::Fav
.all
.pluck(:user_id, :post_id)
.map { |user_id, post_id| { user_id: user_id, item_id: post_id } }
end
measure("fit #{dataset.length.to_s.bold} favs") do
@recommender.fit(dataset)
end
end
def write_factors
measure("#{"for_favorite".bold} - done") do
write_factors_col(:item_ids, :item_factors, :for_favorite)
end
end
def write_factors_col(id_list_name, getter_name, factors_col_name)
total = 0
id_list = @recommender.send(id_list_name)
native_col_width =
Domain::Fa::PostFactor.columns_hash[
factors_col_name.to_s
].sql_type_metadata.limit
logger.info "#{factors_col_name.to_s.bold} - writing #{id_list.length.to_s.bold} factors"
id_list
.map do |post_id|
factors = @recommender.send(getter_name, post_id)
padding = [0.0] * (native_col_width - factors.length)
{ :post_id => post_id, factors_col_name => padding + factors.to_a }
end
.each_slice(20_000) do |chunk|
total += chunk.size
measure(
" -> wrote chunk of #{chunk.size.to_s.bold} - (#{total.to_s.bold} total)"
) do
Domain::Fa::PostFactor.upsert_all(
chunk,
unique_by: :post_id,
update_only: factors_col_name,
returning: :id
)
end
end
end
end

View File

@@ -1,4 +1,4 @@
class Domain::Fa::FactorCalculator
class Domain::Fa::UserFactorCalculator
include HasMeasureDuration
def initialize(epochs = 20)
@@ -23,12 +23,6 @@ class Domain::Fa::FactorCalculator
end
def write_factors
total = 0
for_followed_width =
Domain::Fa::UserFactor.native_factor_width("for_followed")
for_follower_width =
Domain::Fa::UserFactor.native_factor_width("for_follower")
measure("#{"for_followed".bold} - done") do
write_factors_col(:item_ids, :item_factors, :for_followed)
end

View File

@@ -46,6 +46,12 @@ class Domain::Fa::Post < ReduxApplicationRecord
through: :fav_post_joins,
source: :user
has_one :disco,
class_name: "::Domain::Fa::PostFactor",
inverse_of: :post,
foreign_key: :post_id,
dependent: :destroy
def file_uri
Addressable::URI.parse(self.file_url_str) if self.file_url_str
end

View File

@@ -0,0 +1,8 @@
class Domain::Fa::PostFactor < ReduxApplicationRecord
self.table_name = "domain_fa_post_factors"
belongs_to :post, class_name: "::Domain::Fa::Post"
FACTORS_WIDTHS = 16
has_neighbors :for_favorite
end

View File

@@ -6,8 +6,4 @@ class Domain::Fa::UserFactor < ReduxApplicationRecord
FACTORS_WIDTHS = 16
has_neighbors :for_follower
has_neighbors :for_followed
def self.native_factor_width(column)
self.columns_hash[column.to_s].sql_type_metadata.limit
end
end

View File

@@ -27,4 +27,31 @@
<div>(No post description)</div>
<% end %>
</section>
<section class='border-2 border-slate-300 rounded-md'>
<div class='text-lg border-b-2 border-b-slate-300 text-slate-600 p-1 italic'>Similar Posts</div>
<div class='bg-slate-100'>
<% cache(@post.disco, expires_in: 12.hours) do %>
<% similar = @post.
disco&.
nearest_neighbors(:for_favorite, distance: "euclidean")&.
limit(10)&.
includes(:post)
%>
<% if similar %>
<% similar.each do |factor| %>
<% post = factor.post %>
<div class='flex flex-row py-1 px-2 border-b-2 last:border-b-0'>
<span class='text-md italic'><%= link_to post.title, domain_fa_post_path(post.fa_id), class: 'underline' %></span> -
<%= render "domain/fa/users/inline_link", user: post.creator %>
<span class='text-sm text-slate-500 ml-2'>
(distance: <%= number_with_precision(factor.neighbor_distance, precision: 3) %>)
</span>
</div>
<% end %>
<% else %>
<div class='p-2'>No similar posts</div>
<% end %>
<% end %>
</div>
</section>
</div>

View File

@@ -14,6 +14,7 @@
<% end %>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= javascript_pack_tag "application-bundle" %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= yield :head %>
</head>

View File

@@ -1,5 +1,5 @@
<% content_for :head do %>
<%= javascript_pack_tag "application-bundle" %>
<%# <%= javascript_pack_tag "application-bundle" %>
<% end %>
<div class='w-full mt-2 sm:mt-6'>
<%= react_component("UserSearchBar", props: {}, prerender: true) %>

View File

@@ -0,0 +1,19 @@
class CreateDomainFaPostFactors < ActiveRecord::Migration[7.0]
def change
create_table :domain_fa_post_factors do |t|
t.references :post, index: { unique: true }, null: false
t.vector :for_favorite, limit: 32
t.timestamps
end
add_index :domain_fa_post_factors,
:for_favorite,
using: :ivfflat,
opclass: :vector_l2_ops
add_foreign_key :domain_fa_post_factors,
:domain_fa_posts,
column: :post_id,
primary_key: :id,
validate: true
end
end

239
db/schema.rb generated
View File

@@ -10,15 +10,25 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
ActiveRecord::Schema[7.0].define(version: 2024_12_19_201430) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_prewarm"
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
enable_extension "pgcrypto"
enable_extension "plpgsql"
enable_extension "vector"
create_table "blob_entries", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
t.binary "contents", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["sha256"], name: "index_blob_entries_on_sha256", unique: true
end
create_table "blob_entries_p", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
@@ -29,7 +39,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_on_sha256", unique: true
end
create_table "blob_entries_p_00", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_00", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -38,7 +49,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_00_on_sha256", unique: true
end
create_table "blob_entries_p_01", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_01", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -47,7 +59,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_01_on_sha256", unique: true
end
create_table "blob_entries_p_02", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_02", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -56,7 +69,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_02_on_sha256", unique: true
end
create_table "blob_entries_p_03", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_03", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -65,7 +79,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_03_on_sha256", unique: true
end
create_table "blob_entries_p_04", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_04", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -74,7 +89,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_04_on_sha256", unique: true
end
create_table "blob_entries_p_05", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_05", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -83,7 +99,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_05_on_sha256", unique: true
end
create_table "blob_entries_p_06", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_06", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -92,7 +109,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_06_on_sha256", unique: true
end
create_table "blob_entries_p_07", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_07", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -101,7 +119,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_07_on_sha256", unique: true
end
create_table "blob_entries_p_08", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_08", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -110,7 +129,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_08_on_sha256", unique: true
end
create_table "blob_entries_p_09", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_09", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -119,7 +139,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_09_on_sha256", unique: true
end
create_table "blob_entries_p_10", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_10", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -128,7 +149,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_10_on_sha256", unique: true
end
create_table "blob_entries_p_11", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_11", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -137,7 +159,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_11_on_sha256", unique: true
end
create_table "blob_entries_p_12", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_12", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -146,7 +169,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_12_on_sha256", unique: true
end
create_table "blob_entries_p_13", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_13", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -155,7 +179,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_13_on_sha256", unique: true
end
create_table "blob_entries_p_14", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_14", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -164,7 +189,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_14_on_sha256", unique: true
end
create_table "blob_entries_p_15", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_15", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -173,7 +199,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_15_on_sha256", unique: true
end
create_table "blob_entries_p_16", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_16", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -182,7 +209,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_16_on_sha256", unique: true
end
create_table "blob_entries_p_17", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_17", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -191,7 +219,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_17_on_sha256", unique: true
end
create_table "blob_entries_p_18", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_18", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -200,7 +229,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_18_on_sha256", unique: true
end
create_table "blob_entries_p_19", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_19", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -209,7 +239,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_19_on_sha256", unique: true
end
create_table "blob_entries_p_20", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_20", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -218,7 +249,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_20_on_sha256", unique: true
end
create_table "blob_entries_p_21", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_21", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -227,7 +259,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_21_on_sha256", unique: true
end
create_table "blob_entries_p_22", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_22", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -236,7 +269,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_22_on_sha256", unique: true
end
create_table "blob_entries_p_23", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_23", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -245,7 +279,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_23_on_sha256", unique: true
end
create_table "blob_entries_p_24", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_24", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -254,7 +289,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_24_on_sha256", unique: true
end
create_table "blob_entries_p_25", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_25", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -263,7 +299,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_25_on_sha256", unique: true
end
create_table "blob_entries_p_26", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_26", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -272,7 +309,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_26_on_sha256", unique: true
end
create_table "blob_entries_p_27", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_27", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -281,7 +319,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_27_on_sha256", unique: true
end
create_table "blob_entries_p_28", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_28", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -290,7 +329,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_28_on_sha256", unique: true
end
create_table "blob_entries_p_29", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_29", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -299,7 +339,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_29_on_sha256", unique: true
end
create_table "blob_entries_p_30", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_30", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -308,7 +349,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_30_on_sha256", unique: true
end
create_table "blob_entries_p_31", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_31", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -317,7 +359,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_31_on_sha256", unique: true
end
create_table "blob_entries_p_32", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_32", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -326,7 +369,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_32_on_sha256", unique: true
end
create_table "blob_entries_p_33", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_33", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -335,7 +379,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_33_on_sha256", unique: true
end
create_table "blob_entries_p_34", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_34", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -344,7 +389,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_34_on_sha256", unique: true
end
create_table "blob_entries_p_35", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_35", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -353,7 +399,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_35_on_sha256", unique: true
end
create_table "blob_entries_p_36", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_36", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -362,7 +409,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_36_on_sha256", unique: true
end
create_table "blob_entries_p_37", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_37", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -371,7 +419,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_37_on_sha256", unique: true
end
create_table "blob_entries_p_38", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_38", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -380,7 +429,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_38_on_sha256", unique: true
end
create_table "blob_entries_p_39", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_39", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -389,7 +439,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_39_on_sha256", unique: true
end
create_table "blob_entries_p_40", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_40", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -398,7 +449,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_40_on_sha256", unique: true
end
create_table "blob_entries_p_41", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_41", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -407,7 +459,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_41_on_sha256", unique: true
end
create_table "blob_entries_p_42", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_42", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -416,7 +469,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_42_on_sha256", unique: true
end
create_table "blob_entries_p_43", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_43", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -425,7 +479,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_43_on_sha256", unique: true
end
create_table "blob_entries_p_44", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_44", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -434,7 +489,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_44_on_sha256", unique: true
end
create_table "blob_entries_p_45", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_45", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -443,7 +499,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_45_on_sha256", unique: true
end
create_table "blob_entries_p_46", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_46", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -452,7 +509,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_46_on_sha256", unique: true
end
create_table "blob_entries_p_47", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_47", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -461,7 +519,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_47_on_sha256", unique: true
end
create_table "blob_entries_p_48", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_48", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -470,7 +529,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_48_on_sha256", unique: true
end
create_table "blob_entries_p_49", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_49", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -479,7 +539,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_49_on_sha256", unique: true
end
create_table "blob_entries_p_50", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_50", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -488,7 +549,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_50_on_sha256", unique: true
end
create_table "blob_entries_p_51", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_51", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -497,7 +559,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_51_on_sha256", unique: true
end
create_table "blob_entries_p_52", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_52", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -506,7 +569,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_52_on_sha256", unique: true
end
create_table "blob_entries_p_53", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_53", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -515,7 +579,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_53_on_sha256", unique: true
end
create_table "blob_entries_p_54", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_54", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -524,7 +589,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_54_on_sha256", unique: true
end
create_table "blob_entries_p_55", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_55", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -533,7 +599,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_55_on_sha256", unique: true
end
create_table "blob_entries_p_56", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_56", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -542,7 +609,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_56_on_sha256", unique: true
end
create_table "blob_entries_p_57", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_57", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -551,7 +619,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_57_on_sha256", unique: true
end
create_table "blob_entries_p_58", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_58", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -560,7 +629,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_58_on_sha256", unique: true
end
create_table "blob_entries_p_59", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_59", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -569,7 +639,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_59_on_sha256", unique: true
end
create_table "blob_entries_p_60", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_60", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -578,7 +649,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_60_on_sha256", unique: true
end
create_table "blob_entries_p_61", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_61", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -587,7 +659,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_61_on_sha256", unique: true
end
create_table "blob_entries_p_62", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_62", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -596,7 +669,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["sha256"], name: "index_blob_entries_p_62_on_sha256", unique: true
end
create_table "blob_entries_p_63", primary_key: "sha256", id: :binary, force: :cascade do |t|
create_table "blob_entries_p_63", id: false, force: :cascade do |t|
t.binary "sha256", null: false
t.binary "base_sha256"
t.string "content_type", null: false
t.integer "size", null: false
@@ -1269,9 +1343,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.datetime "updated_at"
t.string "signature"
t.jsonb "args"
t.index ["priority", "run_at"], name: "delayed_jobs_priority_run_at_idx"
t.index ["queue"], name: "delayed_jobs_queue_idx"
t.index ["signature"], name: "delayed_jobs_signature_idx", unique: true
t.index ["priority", "run_at"], name: "delayed_jobs_priority"
end
create_table "domain_e621_post_versions", force: :cascade do |t|
@@ -1333,6 +1405,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
create_table "domain_fa_favs", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "post_id", null: false
t.index ["post_id"], name: "index_domain_fa_favs_on_post_id"
t.index ["user_id"], name: "index_domain_fa_favs_on_user_id"
end
@@ -1343,6 +1416,15 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["follower_id", "followed_id"], name: "index_domain_fa_follows_on_follower_id_and_followed_id", unique: true
end
create_table "domain_fa_post_factors", force: :cascade do |t|
t.bigint "post_id", null: false
t.vector "for_favorite", limit: 32
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["for_favorite"], name: "index_domain_fa_post_factors_on_for_favorite", using: :ivfflat
t.index ["post_id"], name: "index_domain_fa_post_factors_on_post_id", unique: true
end
create_table "domain_fa_posts", force: :cascade do |t|
t.integer "fa_id"
t.bigint "creator_id"
@@ -1365,7 +1447,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.jsonb "state_detail"
t.jsonb "log_entry_detail"
t.index ["creator_id"], name: "index_domain_fa_posts_on_creator_id"
t.index ["fa_id", "id"], name: "index_domain_fa_posts_on_fa_id", unique: true
t.index ["fa_id"], name: "index_domain_fa_posts_on_fa_id", unique: true
t.index ["file_id"], name: "index_domain_fa_posts_on_file_id"
end
@@ -1425,9 +1507,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.jsonb "state_detail"
t.datetime "scanned_follows_at"
t.datetime "scanned_favs_at"
t.index ["name"], name: "domain_fa_users_name_idx", opclass: :gist_trgm_ops, using: :gist
t.index ["name"], name: "index_domain_fa_users_on_name", unique: true
t.index ["url_name"], name: "domain_fa_users_url_name_idx", opclass: :gist_trgm_ops, using: :gist
t.index ["url_name"], name: "index_domain_fa_users_on_url_name", unique: true
end
@@ -1523,13 +1603,6 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.datetime "updated_at", null: false
end
create_table "domain_inkbunny_user_avatars", force: :cascade do |t|
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_domain_inkbunny_user_avatars_on_user_id"
end
create_table "domain_inkbunny_users", force: :cascade do |t|
t.integer "state", null: false
t.json "state_detail"
@@ -1585,6 +1658,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.integer "state"
t.json "state_detail"
t.json "raw_data"
t.integer "tw_id"
t.string "name", null: false
t.string "nick"
t.string "description"
@@ -1595,7 +1669,6 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.datetime "scanned_timeline_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "tw_id"
t.index ["name"], name: "index_domain_twitter_users_on_name", unique: true
t.index ["tw_id"], name: "index_domain_twitter_users_on_tw_id", unique: true
end
@@ -1765,10 +1838,13 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
t.index ["item_type", "item_id"], name: "index_versions_on_item_type_and_item_id"
end
add_foreign_key "blob_entries", "blob_entries", column: "base_sha256", primary_key: "sha256"
add_foreign_key "domain_e621_post_versions", "domain_e621_posts", column: "item_id"
add_foreign_key "domain_fa_favs", "domain_fa_posts", column: "post_id"
add_foreign_key "domain_fa_favs", "domain_fa_users", column: "user_id"
add_foreign_key "domain_fa_follows", "domain_fa_users", column: "followed_id"
add_foreign_key "domain_fa_follows", "domain_fa_users", column: "follower_id"
add_foreign_key "domain_fa_post_factors", "domain_fa_posts", column: "post_id"
add_foreign_key "domain_fa_posts", "domain_fa_users", column: "creator_id"
add_foreign_key "domain_fa_posts", "http_log_entries", column: "file_id"
add_foreign_key "domain_fa_user_avatar_versions", "domain_fa_user_avatars", column: "item_id"
@@ -1784,7 +1860,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_12_18_190906) do
add_foreign_key "domain_inkbunny_pool_joins", "domain_inkbunny_posts", column: "post_id"
add_foreign_key "domain_twitter_medias", "domain_twitter_tweets", column: "tweet_id"
add_foreign_key "domain_twitter_medias", "http_log_entries", column: "file_id"
add_foreign_key "domain_twitter_tweets", "domain_twitter_users", column: "author_id", primary_key: "tw_id", name: "on_author_id"
add_foreign_key "domain_twitter_tweets", "domain_twitter_users", column: "author_id"
add_foreign_key "domain_twitter_user_versions", "domain_twitter_users", column: "item_id"
add_foreign_key "http_log_entries", "http_log_entries", column: "caused_by_id"
add_foreign_key "http_log_entries", "http_log_entry_headers", column: "request_headers_id"
add_foreign_key "http_log_entries", "http_log_entry_headers", column: "response_headers_id"

View File

@@ -131,10 +131,18 @@ namespace :fa do
loop { sleep poll_duration if enqueuer.run_once == :sleep }
end
desc "calculate user follow factors"
task calculate_follow_factors: %i[set_logger_stdout environment] do
desc "calculate user follow factors for similar users"
task calc_similar_users: %i[set_logger_stdout environment] do
epochs = (ENV["epochs"] || 20).to_i
worker = Domain::Fa::FactorCalculator.new(epochs)
worker = Domain::Fa::UserFactorCalculator.new(epochs)
worker.fit
worker.write_factors
end
desc "calculate post favorite factors for similar posts"
task calc_similar_posts: %i[set_logger_stdout environment] do
epochs = (ENV["epochs"] || 20).to_i
worker = Domain::Fa::PostFactorCalculator.new(epochs)
worker.fit
worker.write_factors
end

View File

@@ -1,8 +1,8 @@
require "rails_helper"
describe Domain::Fa::FactorCalculator do
describe Domain::Fa::UserFactorCalculator do
before do
setup_follows =
create_follows =
proc do |follower, users|
users.each do |user|
Domain::Fa::Follow.create!(follower: follower, followed: user)
@@ -22,13 +22,13 @@ describe Domain::Fa::FactorCalculator do
SpecUtil.create_domain_fa_user(name: "follower-#{i + 1}")
end
setup_follows.call(@follower1, @cluster1)
setup_follows.call(@follower2, @cluster2)
setup_follows.call(@follower3, @cluster1[0...5] + @cluster2[0...5])
create_follows.call(@follower1, @cluster1)
create_follows.call(@follower2, @cluster2)
create_follows.call(@follower3, @cluster1[0...5] + @cluster2[0...5])
end
it "works" do
worker = Domain::Fa::FactorCalculator.new
worker = Domain::Fa::UserFactorCalculator.new
worker.fit
worker.write_factors

View File

@@ -0,0 +1,75 @@
require "rails_helper"
RSpec.describe Domain::Fa::PostFactor do
it "provides an example of how to use disco / neighbor" do
creator1 = SpecUtil.create_domain_fa_user(name: "creator-1")
cluster1 =
10.times.map do |i|
SpecUtil.create_domain_fa_post(
creator: creator1,
fa_id: "1000000#{i}".to_i
)
end
cluster2 =
10.times.map do |i|
SpecUtil.create_domain_fa_post(
creator: creator1,
fa_id: "2000000#{i}".to_i
)
end
cluster1_ids = Set.new cluster1.map(&:fa_id)
cluster2_ids = Set.new cluster2.map(&:fa_id)
faver1, faver2, faver3, faver4 =
4.times.map { |i| SpecUtil.create_domain_fa_user(name: "faver-#{i + 1}") }
# faver1 favs cluster1
cluster1.each { |post| Domain::Fa::Fav.create!(user: faver1, post: post) }
# faver2 favs cluster2
cluster2.each { |post| Domain::Fa::Fav.create!(user: faver2, post: post) }
# faver3 favs everything
(cluster1 + cluster2).each do |post|
Domain::Fa::Fav.create!(user: faver3, post: post)
end
# faver4 favs a few in cluster1
cluster1[0...2].each do |post|
Domain::Fa::Fav.create!(user: faver4, post: post)
end
# calculate the recommender
recommender =
Disco::Recommender.new(factors: Domain::Fa::PostFactor::FACTORS_WIDTHS)
query = Enumerator.new { |e| Domain::Fa::Fav.find_each { |fav| e << fav } }
recommender.fit(
query.map { |f| { user_id: f.user.name, item_id: f.post.fa_id } }
)
recommender.optimize_item_recs
# cluster1 posts should all be similar to each other
cluster1.each do |post|
similar_items = Set.new(recommender.similar_items(post.fa_id, count: 9))
similar_item_ids = Set.new(similar_items.map { |item| item[:item_id] })
assert cluster1_ids.superset?(similar_item_ids), similar_items
end
# cluster2 posts should all be similar to each other
cluster2.each do |post|
similar_items = Set.new(recommender.similar_items(post.fa_id, count: 9))
similar_item_ids = Set.new(similar_items.map { |item| item[:item_id] })
assert cluster2_ids.superset?(similar_item_ids), similar_items
end
# faver4's fav recommendations should be other posts in cluster1
faver4_recs = Set.new(recommender.user_recs(faver4.name))
faver4_rec_ids = Set.new(faver4_recs.map { |item| item[:item_id] })
assert cluster1_ids.superset?(faver4_rec_ids), faver4_recs
end
end