* VRFS-1957 - use median instead of just most recent for current_scores

This commit is contained in:
Seth Call 2014-07-28 17:37:47 -05:00
parent ef87d635e2
commit e9a4f33420
4 changed files with 311 additions and 133 deletions

View File

@ -196,4 +196,5 @@ score_histories.sql
update_sms_index.sql
connection_allow_null_locidispid.sql
track_user_in_scores.sql
median_aggregate.sql
median_aggregate.sql
current_scores_use_median.sql

View File

@ -0,0 +1,13 @@
-- this results in a rough median; the only problem is that we don't avg if it's an even number. not a big deal truthfully, since eventually you'll have > 5
DROP VIEW current_scores;
CREATE OR REPLACE VIEW current_scores AS
SELECT * FROM (SELECT * , row_number() OVER (PARTITION BY alocidispid, blocidispid, scorer ORDER BY score DESC) AS pcnum FROM
(SELECT * FROM
(SELECT percent_rank() over (PARTITION BY alocidispid, blocidispid ORDER BY score ASC) AS pc, * FROM
(SELECT * FROM
(SELECT *, row_number() OVER (PARTITION BY alocidispid, blocidispid ORDER BY created_at DESC) AS rownum FROM scores) tmp
WHERE rownum < 6) AS score_ranked)
AS tmp2 WHERE pc <= .5 ORDER BY pc DESC) pcs )
AS final WHERE pcnum < 2;

View File

@ -390,7 +390,7 @@ module JamRuby
limit = 50
rel = User.musicians_geocoded
.where(['created_at >= ? AND users.id != ?', since_date, usr.id])
.where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
.joins('inner join current_scores on users.last_jam_locidispid = current_scores.alocidispid')
.where(['current_scores.blocidispid = ?', locidispid])
.where(['current_scores.score <= ?', score_limit])

View File

@ -10,141 +10,305 @@ describe Score do
let(:latency_tester2) { FactoryGirl.create(:latency_tester) }
let(:score_with_latency_tester) { s1, s2 = Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo', { alatencytestid: latency_tester1.id, blatencytestid: latency_tester2.id}); s1 }
before do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
end
it "count" do
Score.count.should == 6
end
it 'a to b' do
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 2345
s.bnodeid.should eql('bnodeid')
s.baddr.should == 0x02030405
s.score.should == 20
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should eq('foo')
end
it 'b to a' do
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 2345
s.anodeid.should eql('bnodeid')
s.aaddr.should == 0x02030405
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 20
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'a to c' do
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 3456
s.bnodeid.should eql('cnodeid')
s.baddr.should == 0x03040506
s.score.should == 30
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'c to a' do
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 3456
s.anodeid.should eql('cnodeid')
s.aaddr.should == 0x03040506
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 30
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'delete a to c' do
Score.deletex(1234, 3456)
Score.count.should == 2
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
end
it 'findx' do
Score.findx(1234, 1234).should == -1
Score.findx(1234, 2345).should == 20
Score.findx(1234, 3456).should == 30
Score.findx(2345, 1234).should == 20
Score.findx(2345, 2345).should == -1
Score.findx(2345, 3456).should == -1
Score.findx(3456, 1234).should == 30
Score.findx(3456, 2345).should == -1
Score.findx(3456, 3456).should == -1
end
it "test shortcut for making scores from connections" do
user1 = FactoryGirl.create(:user)
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
user2 = FactoryGirl.create(:user)
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
user3 = FactoryGirl.create(:user)
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
Score.findx(5, 6).should == -1
Score.findx(6, 5).should == -1
Score.findx(5, 7).should == -1
Score.findx(7, 5).should == -1
Score.findx(6, 7).should == -1
Score.findx(7, 6).should == -1
Score.score_conns(conn1, conn2, 12)
Score.score_conns(conn1, conn3, 13)
Score.score_conns(conn2, conn3, 23)
Score.findx(5, 6).should == 12
Score.findx(6, 5).should == 12
Score.findx(5, 7).should == 13
Score.findx(7, 5).should == 13
Score.findx(6, 7).should == 23
Score.findx(7, 6).should == 23
end
describe "createx" do
it "creates with user info" do
score_with_user.touch
score_with_user.auserid.should == user1.id
score_with_user.buserid.should == user2.id
score_with_user.alatencytestid.should be_nil
score_with_user.blatencytestid.should be_nil
describe "with default scores" do
before do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
end
it "creates with latency-tester info" do
score_with_latency_tester.touch
score_with_latency_tester.auserid.should be_nil
score_with_latency_tester.buserid.should be_nil
score_with_latency_tester.alatencytestid.should == latency_tester1.id
score_with_latency_tester.blatencytestid.should == latency_tester2.id
it "count" do
Score.count.should == 6
end
it 'a to b' do
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 2345
s.bnodeid.should eql('bnodeid')
s.baddr.should == 0x02030405
s.score.should == 20
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should eq('foo')
end
it 'b to a' do
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 2345
s.anodeid.should eql('bnodeid')
s.aaddr.should == 0x02030405
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 20
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'a to c' do
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 3456
s.bnodeid.should eql('cnodeid')
s.baddr.should == 0x03040506
s.score.should == 30
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'c to a' do
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 3456
s.anodeid.should eql('cnodeid')
s.aaddr.should == 0x03040506
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 30
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'delete a to c' do
Score.deletex(1234, 3456)
Score.count.should == 2
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
end
it 'findx' do
Score.findx(1234, 1234).should == -1
Score.findx(1234, 2345).should == 20
Score.findx(1234, 3456).should == 30
Score.findx(2345, 1234).should == 20
Score.findx(2345, 2345).should == -1
Score.findx(2345, 3456).should == -1
Score.findx(3456, 1234).should == 30
Score.findx(3456, 2345).should == -1
Score.findx(3456, 3456).should == -1
end
it "test shortcut for making scores from connections" do
user1 = FactoryGirl.create(:user)
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
user2 = FactoryGirl.create(:user)
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
user3 = FactoryGirl.create(:user)
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
Score.findx(5, 6).should == -1
Score.findx(6, 5).should == -1
Score.findx(5, 7).should == -1
Score.findx(7, 5).should == -1
Score.findx(6, 7).should == -1
Score.findx(7, 6).should == -1
Score.score_conns(conn1, conn2, 12)
Score.score_conns(conn1, conn3, 13)
Score.score_conns(conn2, conn3, 23)
Score.findx(5, 6).should == 12
Score.findx(6, 5).should == 12
Score.findx(5, 7).should == 13
Score.findx(7, 5).should == 13
Score.findx(6, 7).should == 23
Score.findx(7, 6).should == 23
end
describe "createx" do
it "creates with user info" do
score_with_user.touch
score_with_user.auserid.should == user1.id
score_with_user.buserid.should == user2.id
score_with_user.alatencytestid.should be_nil
score_with_user.blatencytestid.should be_nil
end
it "creates with latency-tester info" do
score_with_latency_tester.touch
score_with_latency_tester.auserid.should be_nil
score_with_latency_tester.buserid.should be_nil
score_with_latency_tester.alatencytestid.should == latency_tester1.id
score_with_latency_tester.blatencytestid.should == latency_tester2.id
end
end
end
# current_scores is a view that tries to take the median of up to the last 5 entries
describe "current_scores" do
it "works with empty data set" do
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 0
end
it "works with one score" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['alocidispid'].to_i.should == 1234
result[0]['scorer'].to_i.should == 0
result[1]['alocidispid'].to_i.should == 2345
result[1]['scorer'].to_i.should == 1
end
it "works with two scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
end
it "works with three scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 25
result[1]['score'].to_i.should == 25
end
it "works with six scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # we'll make sure this is old, so it won't be in the set
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 31, nil, 'foo')# median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 32, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 33, nil, 'foo')
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20").cmdtuples.should == 2
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 31
result[1]['score'].to_i.should == 31
# now push back score with 33 to the very back, which will shift the median up to 30
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 33").check
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 30
result[1]['score'].to_i.should == 30
end
it "works with one score each in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
result[2]['score'].to_i.should == 25
result[3]['score'].to_i.should == 25
end
it "works with multiple scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
result[2]['score'].to_i.should == 25
result[3]['score'].to_i.should == 25
end
it "works with multiple scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 30
result[1]['score'].to_i.should == 30
result[2]['score'].to_i.should == 35
result[3]['score'].to_i.should == 35
end
it "works with over 6 scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 45, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 50, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 55, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 60, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 65, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 70, nil, 'foo')
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20 OR score = 25").cmdtuples.should == 4
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 45
result[1]['score'].to_i.should == 45
result[2]['score'].to_i.should == 50
result[3]['score'].to_i.should == 50
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 65 OR score = 70").cmdtuples.should == 4
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 40
result[1]['score'].to_i.should == 40
result[2]['score'].to_i.should == 45
result[3]['score'].to_i.should == 45
end
end