タグ付けを実装するいくつかの方法を聞いたことがあります。TagIDとItemIDの間のマッピングテーブルを使用します(私には理にかなっていますが、それはスケールしますか?)クレイジーですが、動作する可能性があります)。誰かが疎行列を推奨することさえ聞いたことがありますが、タグ名はどのようにして優雅に成長しますか?
タグのベストプラクティスがありませんか?
タグ付けを実装するいくつかの方法を聞いたことがあります。TagIDとItemIDの間のマッピングテーブルを使用します(私には理にかなっていますが、それはスケールしますか?)クレイジーですが、動作する可能性があります)。誰かが疎行列を推奨することさえ聞いたことがありますが、タグ名はどのようにして優雅に成長しますか?
タグのベストプラクティスがありませんか?
回答:
適切にインデックスが作成され、外部キーセットが適切なデータベースで実行されている3つのテーブル(すべてのアイテムの格納用、1つはすべてのタグ用、1つは2つのタグ間の関係用)は、適切に機能し、適切にスケーリングされます。
Table: Item
Columns: ItemID, Title, Content
Table: Tag
Columns: TagID, Title
Table: ItemTag
Columns: ItemID, TagID
通常、私はヤアコフエリスに同意しますが、この特別なケースでは別の実行可能な解決策があります。
2つのテーブルを使用します。
Table: Item
Columns: ItemID, Title, Content
Indexes: ItemID
Table: Tag
Columns: ItemID, Title
Indexes: ItemId, Title
これにはいくつかの大きな利点があります。
まず、開発がはるかに簡単になります。挿入と更新の3つのテーブルのソリューションでitem
は、Tag
テーブルを参照して、既にエントリがあるかどうかを確認する必要があります。次に、新しいものと一緒に参加する必要があります。これは簡単な作業ではありません。
次に、クエリが単純になります(おそらく高速になります)。実行する主なデータベースクエリは3つありますTags
。1つItem
にすべて出力、1つのタグクラウドを描画し、1つのタグタイトルにすべてのアイテムを選択します。
1つのアイテムのすべてのタグ:
3テーブル:
SELECT Tag.Title
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
WHERE ItemTag.ItemID = :id
2テーブル:
SELECT Tag.Title
FROM Tag
WHERE Tag.ItemID = :id
タグクラウド:
3テーブル:
SELECT Tag.Title, count(*)
FROM Tag
JOIN ItemTag ON Tag.TagID = ItemTag.TagID
GROUP BY Tag.Title
2テーブル:
SELECT Tag.Title, count(*)
FROM Tag
GROUP BY Tag.Title
1つのタグのアイテム:
3テーブル:
SELECT Item.*
FROM Item
JOIN ItemTag ON Item.ItemID = ItemTag.ItemID
JOIN Tag ON ItemTag.TagID = Tag.TagID
WHERE Tag.Title = :title
2テーブル:
SELECT Item.*
FROM Item
JOIN Tag ON Item.ItemID = Tag.ItemID
WHERE Tag.Title = :title
しかし、いくつかの欠点もあります。データベースに多くのスペースが必要になる可能性があり(これにより、ディスク操作が多くなり、速度が低下する可能性があります)、正規化されないため、不整合が発生する可能性があります。
サイズの引数はそれほど強力ではありません。タグの本質は、通常、タグがかなり小さいため、サイズの増加が大きくないためです。タグタイトルのクエリは、各タグを1回だけ含む小さなテーブルではるかに高速であると主張できますが、これは確かに当てはまります。しかし、参加する必要がないことによる節約と、それらに優れたインデックスを構築できるという事実を考慮すると、これを簡単に補うことができます。もちろん、これは使用しているデータベースのサイズに大きく依存します。
一貫性のない議論も少し議論の余地があります。タグはフリーテキストフィールドであり、「すべてのタグの名前を "foo"から "bar"に変更」などの予期される操作はありません。
だからtldr:私は2つのテーブルの解決策に行きます。(実際、私はこれに行きます。この記事を見つけて、それに対して有効な引数があるかどうかを確認しました。)
couchdbのようなmap-reduceをサポートするデータベースを使用している場合、プレーンテキストフィールドまたはリストフィールドにタグを格納するのが実際に最善の方法です。例:
tagcloud: {
map: function(doc){
for(tag in doc.tags){
emit(doc.tags[tag],1)
}
}
reduce: function(keys,values){
return values.length
}
}
これをgroup = trueで実行すると、タグ名で結果がグループ化され、タグが検出された回数のカウントが返されます。これは、テキスト内の単語の出現回数を数えるのとよく似ています。
タグを格納するために単一のフォーマットされたテキスト列[1]を使用し、これをインデックス化するために対応する全文検索エンジンを使用します。そうしないと、ブールクエリを実装しようとしたときにスケーリングの問題が発生します。
持っているタグの詳細が必要な場合は、増分的に維持されるテーブルで追跡するか、バッチジョブを実行して情報を抽出できます。
[1]一部のRDBMSは、解析手順を必要としないためストレージにさらに適しているかもしれないが、全文検索で問題を引き起こす可能性があるネイティブ配列タイプを提供することさえあります。
次のデザインをお勧めします。アイテムテーブル:Itemid、taglist1、taglist2
これは高速で、アイテムレベルでデータを簡単に保存および取得できます。
並行して別のテーブルを作成する:タグタグはタグの一意の識別子を作成せず、次の列を含む2番目の列のスペースが不足すると、100アイテムが別の行を作成するとします。
タグのアイテムを検索している間、それは超高速になります。