[]

Ekşi Sözlük sol frame başlık listeleme algoritması
Ey geek'ler ya da yönetici abiler.
Sol framedeki konular hangi algoritmaya göre sıralanıyor. Neye göre başlığın tazeliği ve popülerliği ölçülüp listeleniyor. Bilen gören var mıdır? Reddit'in bile algoritması belli sözlüğünki kolanın formülü gibi mübarek. Kimse tartışmamış, yazıp çizmemiş.
Sol framedeki konular hangi algoritmaya göre sıralanıyor. Neye göre başlığın tazeliği ve popülerliği ölçülüp listeleniyor. Bilen gören var mıdır? Reddit'in bile algoritması belli sözlüğünki kolanın formülü gibi mübarek. Kimse tartışmamış, yazıp çizmemiş.

bugün: en son yazılan entry'ye göre.
gündem: belli bir sayıya ulaşmış başlıklarda son yazılan entry'ye göre. pek karışık bir algoritması yok yani. en son entry girilen başlık en tepeye çıkıyor. tartışılmamasının sebebi de bu olsa gerek.
gündem: belli bir sayıya ulaşmış başlıklarda son yazılan entry'ye göre. pek karışık bir algoritması yok yani. en son entry girilen başlık en tepeye çıkıyor. tartışılmamasının sebebi de bu olsa gerek.
- himmet dayi
(31.01.16 00:07:07)

tamam bunun matematiksel formüle dökülmüş halini soruyorum ben de.
MYSQL kullananlar bilir bi algoritma olması lazım.
MYSQL kullananlar bilir bi algoritma olması lazım.
- vicious
(31.01.16 00:15:17)

gerçekte ne olduğunu bilmiyorum ama şu şekilde bir mantık yürütebilirim:
- herhangi bir entry girildiğinde entry'nin girildiği tarih/saat ilgili başlığın 'son entry girilme zamanı' değişkenine atanır. bu değişkeni referans alarak başlıkları sıralarsın.
- herhangi bir entry girildiğinde entry'nin girildiği tarih/saat ilgili başlığın 'son entry girilme zamanı' değişkenine atanır. bu değişkeni referans alarak başlıkları sıralarsın.
- himmet dayi
(31.01.16 01:42:24)

Hey adamım, bunu sen istedin.
Yarım saatte küçük bir demo yaptım localhost'ta, aşağı yukarı aynı sonuçları aldım.
Önce işi parçalara bölelim, adım adım gidelim. Yenile tuşuna abandım, hemen yeni entryler gelmedi. Demek ki her birkaç dakikada bir bu kısım yenilenerek yeni bir önbellek (cache) oluşturuluyor, böylece X dakikada TEK bir sorgu yapılarak veritabanına yük binmesi engelleniyor. Birisi ekşiye her girdiğinde o kişi için özel sorgu yapılmıyor yani sol kısım için.
Mantıklı.
Sayfa başı entry benim hesapta 25, demek ki ben giriş yaptığımda bana 25 entry gösterilecek. Ekşisözlük bunu nereden biliyor? Çünkü çerez var bilgisayarımda. Vay arkadaş, bi kamyon çerez varmış.
Direk SQL sorgusu ile yazarsak uzun sürer, verimsiz olur ve güvenli olmaz, ORM kullanarak yapıyorum ben bu işleri, sana da öyle yapmanı tavsiye ederim. Algoritma demişsin, yol yordam demişsin, ben de onu anlatıyorum aşağıda.
Tablo yapısını basitçe kuralım önce. Elimizde başlık, kullanıcı ve giriş isminde üç tablo olması lazım. Bunları jargona uydurmak için İngilizce yazacağım.
users -> kullanıcı tablosu
entries -> entry tablosu
topics -> başlık tablosu
Önce veritabanı ilişkilerini kuralım.
Bir kullanıcı birden fazla başlığa sahip olabilir.
Bir kullanıcı birden fazla entry'e sahip olabilir.
Bir başlığa birden fazla entry girilebilir.
Şimdi geldik en cafcaflı yerine. Kullandığın ORM'e göre değişmekle beraber -gerçi hepsinde aynı şeyler var-, kuracağın ilişkiler şöyle olacak.
user -> hasMany topics -> bir kullanıcının tüm başlıkları -> 1-çok ilişki
topic -> belongsTo user -> bir başlığın sahbi olan kullanıcı -> 1-1 ilişki
user -> hasMany entries -> bir kullanıcının tüm entryleri -> 1-çok ilişki
topic -> hasMany entries -> bir başlığın tüm entryleri -> 1-çok ilişki
entry -> belongsTo user -> bir entry'i yazan kullanıcı -> 1-1 ilişki
entry -> belongsTo topic -> bir entry'nin ait olduğu başlık -> 1-1 ilişki
Eğer bir kullanıcının karşı tabloda yalnızca bir kayıtla ilişkisi olsaydı o zaman hasMany yerine hasOne yapacaktık ama şuanda hasOne yapacak bir durum mevcut değil. Çok nadir kullanılan bir ilişki tipi bu.
Basitçe bir şema hazırlayacak olursak
Tüm id'ler int(11) ve auto_increment olacak
users
-id
-username
-email
-created_at
-updated_at
-password_hash
topics
-id
-name
-user_id -> users tablosundan foreign key olarak ekliyoruz başlığa ukteyi kim verdi? yani kim açtı? (sözlük nasıl işliyor bilmiyorum, sanırım ukte deniyordu adına)
-today_entry_count
-created_at
-updated_at
entries
-id
-entry -> içerik
-topic_id -> topics tablosundan foreign key olarak ekliyoruz. (bu entry hangi başlığa ait?)
-user_id -> users tablosundan foreign key olarak ekliyoruz. (bu entry'i kim yazdı?)
-created_at -> ne zaman yazdı?
-updated_at -> ne zaman düzenledi?
-is_visible -> 0 ya da 1 (entry görünür durumda mı?)
Bugünü mü göstereceksin?
Topics tablosuna git.
Tüm başlıkları seç.
En son güncellenen 25 başlığı bul.
En yeniden en eskiye göre sırala.
Başlıkları döndür.
Bitti.
Gündemi mi göstereceksin?
Topics tablosuna git.
İçinde today_entry_count var, her entry girildiğinde bir artıyor orası. Gece 12'de de cronjob veya mysql trigger ile siliniyor.
Son 24 saat içinde en az 50 entry almış tüm başlıkları seç.
25 ile limitle.
En yeniden en eskiye göre döndür.
Bitti.
Video eklemek istersen yine aynı, bir başlığın bir videosu var hep.
Bu kısım nispeten kolay ama işin içine kanalları katarsan o zaman bire çok ve bire bir ilişkiler yetmez, çoka çok ilişki lazım. belongsToMany yani.
Niye?
5 tane kanal olsun.
1 tane başlık olsun.
Bu başlığın ve üç kanalı olsun.
Eğer başlığın birden fazla kanalı olacaksa -yani şu durumdaki gibi- (bir başlık birden fazla kanala aitken bir kanal da birden fazla başlığa ait, burası önemli) hem başlık tablosuna channel_1, channel_2 diye yeni sütun eklemek saçma olacaktır hem de bir kanala ait başlıkları getirmek için kanal tablosuna topic_1, topic_2 diye onlarca, hatta binlerce sütun açmamız gerekecek.
O yüzden çoka çok ilişki kuruyoruz, belongsToMany.
channel_topic diye bir tablo oluşturuyorum.
id
channel_id -> channels tablosundan foreign key
topic_id -> topics tablosundan foreign key
channels ve topics tablolarına şu ilişkileri kuruyoruz.
channels -> belongsToMany topics
topics -> belongsToMany channels
Niye iki tane ayrı ilişki kurdum? Üstteki bir kanalın tüm başlıklarını almaya yararken alttaki bir başlığın tüm kanallarını almaya yarıyor. Tablo ilişkisinin tersini de ayarlaman lazım ki veritabanındaki diğer kayıtları da alabilesin.
Ekşisözlüğün veritabanı ne kadar büyük bilmiyorum ama arşivleme ve yük getirmeyen kısımlar için MySQL, aktif kısımlar içinse Redis kullanmak daha yüksek performans sağlar diye düşünüyorum.
Yarım saatte küçük bir demo yaptım localhost'ta, aşağı yukarı aynı sonuçları aldım.
Önce işi parçalara bölelim, adım adım gidelim. Yenile tuşuna abandım, hemen yeni entryler gelmedi. Demek ki her birkaç dakikada bir bu kısım yenilenerek yeni bir önbellek (cache) oluşturuluyor, böylece X dakikada TEK bir sorgu yapılarak veritabanına yük binmesi engelleniyor. Birisi ekşiye her girdiğinde o kişi için özel sorgu yapılmıyor yani sol kısım için.
Mantıklı.
Sayfa başı entry benim hesapta 25, demek ki ben giriş yaptığımda bana 25 entry gösterilecek. Ekşisözlük bunu nereden biliyor? Çünkü çerez var bilgisayarımda. Vay arkadaş, bi kamyon çerez varmış.
Direk SQL sorgusu ile yazarsak uzun sürer, verimsiz olur ve güvenli olmaz, ORM kullanarak yapıyorum ben bu işleri, sana da öyle yapmanı tavsiye ederim. Algoritma demişsin, yol yordam demişsin, ben de onu anlatıyorum aşağıda.
Tablo yapısını basitçe kuralım önce. Elimizde başlık, kullanıcı ve giriş isminde üç tablo olması lazım. Bunları jargona uydurmak için İngilizce yazacağım.
users -> kullanıcı tablosu
entries -> entry tablosu
topics -> başlık tablosu
Önce veritabanı ilişkilerini kuralım.
Bir kullanıcı birden fazla başlığa sahip olabilir.
Bir kullanıcı birden fazla entry'e sahip olabilir.
Bir başlığa birden fazla entry girilebilir.
Şimdi geldik en cafcaflı yerine. Kullandığın ORM'e göre değişmekle beraber -gerçi hepsinde aynı şeyler var-, kuracağın ilişkiler şöyle olacak.
user -> hasMany topics -> bir kullanıcının tüm başlıkları -> 1-çok ilişki
topic -> belongsTo user -> bir başlığın sahbi olan kullanıcı -> 1-1 ilişki
user -> hasMany entries -> bir kullanıcının tüm entryleri -> 1-çok ilişki
topic -> hasMany entries -> bir başlığın tüm entryleri -> 1-çok ilişki
entry -> belongsTo user -> bir entry'i yazan kullanıcı -> 1-1 ilişki
entry -> belongsTo topic -> bir entry'nin ait olduğu başlık -> 1-1 ilişki
Eğer bir kullanıcının karşı tabloda yalnızca bir kayıtla ilişkisi olsaydı o zaman hasMany yerine hasOne yapacaktık ama şuanda hasOne yapacak bir durum mevcut değil. Çok nadir kullanılan bir ilişki tipi bu.
Basitçe bir şema hazırlayacak olursak
Tüm id'ler int(11) ve auto_increment olacak
users
-id
-username
-created_at
-updated_at
-password_hash
topics
-id
-name
-user_id -> users tablosundan foreign key olarak ekliyoruz başlığa ukteyi kim verdi? yani kim açtı? (sözlük nasıl işliyor bilmiyorum, sanırım ukte deniyordu adına)
-today_entry_count
-created_at
-updated_at
entries
-id
-entry -> içerik
-topic_id -> topics tablosundan foreign key olarak ekliyoruz. (bu entry hangi başlığa ait?)
-user_id -> users tablosundan foreign key olarak ekliyoruz. (bu entry'i kim yazdı?)
-created_at -> ne zaman yazdı?
-updated_at -> ne zaman düzenledi?
-is_visible -> 0 ya da 1 (entry görünür durumda mı?)
Bugünü mü göstereceksin?
Topics tablosuna git.
Tüm başlıkları seç.
En son güncellenen 25 başlığı bul.
En yeniden en eskiye göre sırala.
Başlıkları döndür.
Bitti.
Gündemi mi göstereceksin?
Topics tablosuna git.
İçinde today_entry_count var, her entry girildiğinde bir artıyor orası. Gece 12'de de cronjob veya mysql trigger ile siliniyor.
Son 24 saat içinde en az 50 entry almış tüm başlıkları seç.
25 ile limitle.
En yeniden en eskiye göre döndür.
Bitti.
Video eklemek istersen yine aynı, bir başlığın bir videosu var hep.
Bu kısım nispeten kolay ama işin içine kanalları katarsan o zaman bire çok ve bire bir ilişkiler yetmez, çoka çok ilişki lazım. belongsToMany yani.
Niye?
5 tane kanal olsun.
1 tane başlık olsun.
Bu başlığın ve üç kanalı olsun.
Eğer başlığın birden fazla kanalı olacaksa -yani şu durumdaki gibi- (bir başlık birden fazla kanala aitken bir kanal da birden fazla başlığa ait, burası önemli) hem başlık tablosuna channel_1, channel_2 diye yeni sütun eklemek saçma olacaktır hem de bir kanala ait başlıkları getirmek için kanal tablosuna topic_1, topic_2 diye onlarca, hatta binlerce sütun açmamız gerekecek.
O yüzden çoka çok ilişki kuruyoruz, belongsToMany.
channel_topic diye bir tablo oluşturuyorum.
id
channel_id -> channels tablosundan foreign key
topic_id -> topics tablosundan foreign key
channels ve topics tablolarına şu ilişkileri kuruyoruz.
channels -> belongsToMany topics
topics -> belongsToMany channels
Niye iki tane ayrı ilişki kurdum? Üstteki bir kanalın tüm başlıklarını almaya yararken alttaki bir başlığın tüm kanallarını almaya yarıyor. Tablo ilişkisinin tersini de ayarlaman lazım ki veritabanındaki diğer kayıtları da alabilesin.
Ekşisözlüğün veritabanı ne kadar büyük bilmiyorum ama arşivleme ve yük getirmeyen kısımlar için MySQL, aktif kısımlar içinse Redis kullanmak daha yüksek performans sağlar diye düşünüyorum.
- hayirsiz
(31.01.16 03:13:58 ~ 06:24:53)

@hayirsiz hocam maşallahın var stackoverflow'da böyle kapsamlı cevaplar alamıyoruz :)
benim merak ettiğim nokta şuydu zaten. 1 gün içinde x entry'den fazla entry olanları çek, onları kendi içinde last_post_date'e göre sırala. burası tamam. her seferinde farklı sonuç göstermemesi için de cache kullan diyorsun. bu şunu doğurur, diyelim ki bir konunun popüler olma durumu onun bir günde 25 entry'den fazla entry almasıyla oluyor, bu şartlarda listeleyeceğimiz 25 konu güncelliğini kaybedebilir. 24 enrtry almış ve 2 dakika içinde 2 entry daha almış bir başlık o popüler listesinde anında görünmeyecektir. Sadece MYSQL kullanırsak da muhtemelen server'a çok yük bindirecektir. diğer bahsettiğin tanımlara pek vakıf değilim bu sebeple araştıracağım. çok sağol.
fakat şuna takıldım:
Gündemi mi göstereceksin?
--Topics tablosuna git.
--İçinde today_entry_count var, her entry girildiğinde bir artıyor orası. Gece 12'de de ronjob veya mysql trigger ile siliniyor.
--Son 24 saat içinde en az 50 entry almış tüm başlıkları seç.
--25 ile limitle.
--En yeniden en eskiye göre döndür.
--Bitti.
ekşide böyle bir durum söz konusu değil. eğer olsaydı gece 24'te popüler listesi de sıfırlanırdı. burada daha farklı bir algoritma kullanılıyor eminim.
o ssg buraya gelecek :)
benim merak ettiğim nokta şuydu zaten. 1 gün içinde x entry'den fazla entry olanları çek, onları kendi içinde last_post_date'e göre sırala. burası tamam. her seferinde farklı sonuç göstermemesi için de cache kullan diyorsun. bu şunu doğurur, diyelim ki bir konunun popüler olma durumu onun bir günde 25 entry'den fazla entry almasıyla oluyor, bu şartlarda listeleyeceğimiz 25 konu güncelliğini kaybedebilir. 24 enrtry almış ve 2 dakika içinde 2 entry daha almış bir başlık o popüler listesinde anında görünmeyecektir. Sadece MYSQL kullanırsak da muhtemelen server'a çok yük bindirecektir. diğer bahsettiğin tanımlara pek vakıf değilim bu sebeple araştıracağım. çok sağol.
fakat şuna takıldım:
Gündemi mi göstereceksin?
--Topics tablosuna git.
--İçinde today_entry_count var, her entry girildiğinde bir artıyor orası. Gece 12'de de ronjob veya mysql trigger ile siliniyor.
--Son 24 saat içinde en az 50 entry almış tüm başlıkları seç.
--25 ile limitle.
--En yeniden en eskiye göre döndür.
--Bitti.
ekşide böyle bir durum söz konusu değil. eğer olsaydı gece 24'te popüler listesi de sıfırlanırdı. burada daha farklı bir algoritma kullanılıyor eminim.
o ssg buraya gelecek :)
- vicious
(31.01.16 13:10:36)

gece 12'yi salladım ben.
O performans sorunu endişelenmen gereken bir şey değil, milyonlarca kayıt olan tablolarda bile yere çakılmadan çalışıyor MySQL. Ekşisözlüğün hiti oldukça yüksek ama o kadar da yüksek değil :)) YAGNI (you aren't gonna need it, ihtiyacın olmayacak) diye bir şey var yazılım sektöründe, böyle çok çılgın performans sorunlarını bir projeye başlarken düşünmeye gerek yok. Zaten yaptığın işten para kazanırsan sadece veritabanı tasarımıyla uğraşan çalışanların olur, onu onlar düşünür :)
Zaten sen bu durumda her kullanıcı için yeni sorgu yapmadığın için sorun yok. Orayı 5 dakikada bir güncellersin, MySQL sunucuyu çok yormamış olur. Yeni eklenen kayıtları da (bkz: websocket) kullanarak anlık olarak ekleyebilirsin akışa.
Diyelim ki son 24 saat içinde en az 50 entry almış 25 başlığı buldun. Bu sonucu en yeni yorum almış olandan en eski yorum almış olana göre sıralıyorsun.
Şuan deneyeyim dedim, gerçekten de hepsi en yeniden en eskiye göre gelmiyor. İlgili kanalı takip etmedikçe gözükmüyordu sanırım bazı kanallardaki başlıklar, onunla ilgili olabilir. Kanallara göre farklı bir filtreleme yapıyor ya da ilgi alanına gösteriyor olabilir, onu bilemedim.
O performans sorunu endişelenmen gereken bir şey değil, milyonlarca kayıt olan tablolarda bile yere çakılmadan çalışıyor MySQL. Ekşisözlüğün hiti oldukça yüksek ama o kadar da yüksek değil :)) YAGNI (you aren't gonna need it, ihtiyacın olmayacak) diye bir şey var yazılım sektöründe, böyle çok çılgın performans sorunlarını bir projeye başlarken düşünmeye gerek yok. Zaten yaptığın işten para kazanırsan sadece veritabanı tasarımıyla uğraşan çalışanların olur, onu onlar düşünür :)
Zaten sen bu durumda her kullanıcı için yeni sorgu yapmadığın için sorun yok. Orayı 5 dakikada bir güncellersin, MySQL sunucuyu çok yormamış olur. Yeni eklenen kayıtları da (bkz: websocket) kullanarak anlık olarak ekleyebilirsin akışa.
Diyelim ki son 24 saat içinde en az 50 entry almış 25 başlığı buldun. Bu sonucu en yeni yorum almış olandan en eski yorum almış olana göre sıralıyorsun.
Şuan deneyeyim dedim, gerçekten de hepsi en yeniden en eskiye göre gelmiyor. İlgili kanalı takip etmedikçe gözükmüyordu sanırım bazı kanallardaki başlıklar, onunla ilgili olabilir. Kanallara göre farklı bir filtreleme yapıyor ya da ilgi alanına gösteriyor olabilir, onu bilemedim.
- hayirsiz
(31.01.16 14:23:29 ~ 15:22:32)

Performans konusunda değilim zaten benim merak ettiğim bu popüler algoritmasıydı. Sadece entry miktarına göre sıraladığını düşünmüyorum. Belki başlıktaki entrylerin aldığı oylara göre de bir hesaplama yapıyor olabilir ama benim sistemimde böyle bir entry oylama mevzusu olmadığından sadece post_count topic_date gibi şeylerle sağlıklı bir popüler konular listelemesi yapmaya çalışıyorum. Yeni açılan konu pat diye en üstte görünmesin, ondan daha popüler olan konular tepede kalsın istiyorum yani. Mesela hacker news'in bi aralar kullandığı şöyle bir algoritma var:
stackoverflow.com
Bunu uygularsam da eski konular tekrar popüler olamıyor tabii ki. O tarz sitelerde sürekli bir akış söz konusu ama sözlük formatında bir sitede 6 sene önce açılmış bir başlık tekrar popüler olabiliyor.
Madem ekşi'nin kullandığı algoritmayı öğrenemiyoruz bari beyin fırtınası yapalım biraz :) Ne düşünüyorsun?
stackoverflow.com
Bunu uygularsam da eski konular tekrar popüler olamıyor tabii ki. O tarz sitelerde sürekli bir akış söz konusu ama sözlük formatında bir sitede 6 sene önce açılmış bir başlık tekrar popüler olabiliyor.
Madem ekşi'nin kullandığı algoritmayı öğrenemiyoruz bari beyin fırtınası yapalım biraz :) Ne düşünüyorsun?
- vicious
(31.01.16 16:18:55)
1