SO PHP / MySQL ile benzer etiketleme sistemi uygulamak nasıl?

3 Cevap php

I'm coding a website in PHP/MySQL and I'd like to implement a similar to stackoverflow tagging engine. I have 3 relevant tables in DB: 1. Items 2. Tags 3. ItemTagMap (maps tags to items, n:n mapping)

Kullanıcıların bu etiket listeden etiketleri kaldırarak / ekleyerek kendi arama "rafine" böylece Şimdi, arama sayfasında ben, tüm arama sonucu (sadece geçerli sayfa) için tüm etiketleri farklı listesini göstermek istiyorum.

Soru, DB üzerinde oldukça ağır bir sorgu ve farklı sonuç kümeleri ve böylece farklı etiket setleri neden arama isteklerini ton olmasıdır.

Herkes bu etkili uygulamak için nasıl biliyor mu?

3 Cevap

Biz premature optimization moduna gitmek önce, aşağıdaki sorgu şablonu içine bakmak yararlı olabilir. Başka bir şey varsa, bu olası optimizasyonlar etkinliği ölçülebilir hangi karşı bir temel olarak kullanılabilir.

SELECT T.Tagid, TagInfo.TagName,  COUNT(*)
FROM Items I
JOIN Tags TagInfo ON TagInfo.TagId = T.TagId
JOIN ItemTagMap T  ON I.ItemId = T.ItemId 
--JOIN ItemTagMap T1 ON I.ItemId = T1.ItemId
WHERE I.ItemId IN
  (
      SELECT ItemId 
      FROM Items
      WHERE   -- Some typical initial search criteria
         Title LIKE 'Bug Report%'   -- Or some fulltext filter instead...
         AND  ItemDate > '02/22/2008'
         AND  Status = 'C'
  )
--AND T1.TagId = 'MySql'
GROUP BY T.TagId, TagInfo.TagName
ORDER BY COUNT(*) DESC

The subquery is the "driving query", i.e. the one corresponding to the end-user's initial criteria. (see below for details on how this query, required multiple times may fit in an overall optimized flow) Commented is the JOIN on T1 (and possibly T2, T3, when several tags are selected), and, with the WHERE clause, the associated criteria. These are needed when the user selects a particular tag, whether as part of the initial search or by refinement. (It may be more efficient to place these joins and where clauses within the sub-query; more on these below)

Discussion... The "driving query", or a variation thereof is needed for two distinct purposes:

  • Ilişkili tüm etiketleri numaralandırmak için gerekli itemid ve complete listesini sağlamak için 1..
  • 2. Öğe tabloda Ürün detay bilgi ararken amacıyla, ilk N ItemId değerleri (N ekran sayfa boyutu olarak) sağlamaktır.

İkinci liste alfabetik olarak artan, azalan Tarihe göre, kullanıcının seçimine (diyelim, ya da Başlığı göre sıralanır gereken sayede tam listesi, sıralanması için (ya da farklı bir düzende sıralama yararlanabilir) gerekmez unutmayın .) Ayrıca, gereken herhangi bir sıralama düzeni varsa, sorgunun maliyeti (SQL kendisi tarafından tek optimizasyon utangaç ve / veya bazı denormalizasyon tam listesi ile ilgili ima unutmayın, SQL o listede son kayıtları "görmek" gerekiyor , durumda onlar) sort-bilge, üst aittir.

Bu son gerçek, her iki amaç için aynı sorguyu sahip lehine, ilgili liste, geçici bir tabloda depolanan edilebilir. Genel akışı hızlı bir şekilde detayları ile üst N Öğe kayıtları arama ve bir kerede uygulama bu döner olacaktır. Uygulama daha sonra ajax-moda iyileştirmeler için Etiketler listesini elde edebilirsiniz. Bu liste subquery tarafından değiştirildiği yukarıda birini, akin bir sorgu ile üretmek olacaktır "temporaryTable seçin *." Oran, SQL optimizer (bazı durumlarda) bu listeyi sıralamak için karar olacağını iyi, en oldukça ikinci bunu tahmin ve açıkça sıralama daha, bunu vereyim.

Düşünün bir diğer nokta belki de "itici sorgu" iç ItemTagMap masanın üzerine (ler) katılmak getirmek yerine o kadar yukarıda gösterildiği gibi. Hem performans için, bunu yapmak için, muhtemelen en iyi ve # 2 amaç (öğelerin bir sayfa ekran) için doğru liste üretecektir çünkü.

Yukarıda açıklanan sorgu / akış olasılıkla bile nispeten mütevazı donanım üzerinde, oldukça iyi dönüşebilecek; geçici belki saniyede 10 kadar sürekli kullanıcı aramaları ile 1/2 Milyon + Öğeler, içine. En önemli faktörlerden biri ilk arama kriterlerinin seçicilik olacaktır.

Optimization ideas

  • [Tipik bir arama durumlarda ve veri istatistikleri bağlı] bu (aslında çoğaltarak) ItemTagMap masaya Öğeler 'bazı alanları getirerek denormalize mantıklı olabilir. Özellikle kısa alanlar vardır 'welcome' olabilir.
  • Veri milyon + Öğeler büyüdükçe, bazı etiketleri genellikle güçlü korelasyon yararlanılabilecek (ex: SO, PHP sık sık sebepsiz btw sık sık, MySQL ile birlikte geliyor ...), çeşitli hileler ile. Örneğin "multi-Tag" TagIds tanıtımı biraz daha karmaşık giriş mantığı render olabilir, ama aynı zamanda önemli ölçüde Harita boyutunu azaltabilir.


-- 'nough said! --
Appropriate architecture and optimizations should be selected in light of the actual requirements and of the effective data statistical profile...

PHP içine ağır çalışma koyarak, DB aramaların sayısını en aza indirmek için denemek isteyeceksiniz.

İlk olarak, DB tüm öğeleri seçmek:

select * from items where (conditions);

Ardından, sonuç kümesinden tüm id yıllardan bir dizi oluşturmak.

$ids = array();
foreach ($items as $item) {
    $ids[] = $item['id'];
}
$ids = implode(',' $ids);

Sonra önceden alınmış Öğe kimlikleri için tüm ItemTagMaps ve ilişkili etiketi verileri seçin.

select map.item_id, t.id, t.name from tags t, item_tag_maps map where t.id = map.tag_id and map.item_id in ($ids);

Şimdi ne zaman döngü senin $ ürün dizi boyunca, size sürece eşleşen item_id değeri olarak gerçekleştirilen 2. SQL sorgusu eşleşen tüm etiketleri bulabilirsiniz.

Varsayarsak:

  • Item (id);
  • Adını endeksi ile birlikte Tag (id, isim);
  • ItemTag (item_id, tag_id).

sonra:

SELECT t.name
FROM Tag t
WHERE EXISTS (SELECT 1 FROM ItemTag WHERE item_id = 1234)
ORDER BY t.name

Bu konuda yoğun bir şey. Bu benzer ama benim tahminim o yavaş olurdu:

SELECT t.name
FROM Tag t
WHERE t.id IN (SELECT tag_id FROM ItemTag WHERE item_id = 1234)
ORDER BY t.name

Bu katılmak yanı sıra yapılabilir:

SELECT DISTINCT t.name
FROM Tag t
JOIN ItemTag i WHERE i.tag_id = t.id
WHERE i.item_id = 1234
ORDER BY t.name

Ben ilk daha hızlı olacağını düşünüyorum ama SQL ile her zaman olduğu gibi, bu (bir yeterli büyüklükte veri seti üzerinde) test değer.

Yukarıdaki tek bir öğenin etiketleri listelemek için yapılmıştır. Sen arama sonuçları için etiketleri kompozit seti istiyorum. Yani yukarıdan zor değil ama sizin arama sonuçlarını almak ne bağlıdır.