algorithm - poner - que es tags h1 y cursiva
Cómo implementar un sistema de etiquetas (6)
Me preguntaba cuál es la mejor manera de implementar un sistema de etiquetas, como el que se usa en SO. Estaba pensando en esto, pero no puedo encontrar una buena solución escalable.
Estaba pensando en tener una solución básica de 3 tablas: tener una tabla de tags
, una tabla de articles
y una tabla de tag_to_articles
.
¿Es esta la mejor solución a este problema, o hay alternativas? Usando este método, la tabla se haría extremadamente grande en el tiempo, y para buscar esto no es demasiado eficiente, supongo. Por otro lado, no es tan importante que la consulta se ejecute rápidamente.
Creo que encontrarás interesante esta publicación en el blog: Etiquetas: Esquemas de la base de datos
El problema: desea tener un esquema de base de datos donde pueda etiquetar un marcador (o una publicación de blog o lo que sea) con tantas etiquetas como desee. Más tarde, querrá ejecutar consultas para restringir los marcadores a una unión o intersección de etiquetas. También desea excluir (decir: menos) algunas etiquetas del resultado de la búsqueda.
Solución "MySQLicious"
En esta solución, el esquema tiene solo una tabla, se desnormaliza. Este tipo se llama "solución MySQLicious" porque MySQLicious importa datos del.icio.us en una tabla con esta estructura.
Intersección (Y) Consulta de "búsqueda + servicio web + semweb":
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags LIKE "%semweb%"
Unión (OR) Consulta para "search | webservice | semweb":
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
OR tags LIKE "%webservice%"
OR tags LIKE "%semweb%"
Consulta mínima para "search + webservice-semweb"
SELECT *
FROM `delicious`
WHERE tags LIKE "%search%"
AND tags LIKE "%webservice%"
AND tags NOT LIKE "%semweb%"
Solución "Scuttle"
Scuttle organiza sus datos en dos tablas. Esa tabla "scCategories" es la tabla "tag" y tiene una clave externa para la tabla "bookmark".
Intersección (AND) Consulta de "marcador + servicio web + semweb":
SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN (''bookmark'', ''webservice'', ''semweb''))
GROUP BY b.bId
HAVING COUNT( b.bId )=3
En primer lugar, se buscan todas las combinaciones de marcadores y etiquetas, donde la etiqueta es "marcador", "servicio web" o "semweb" (c.category IN (''marcador'', ''webservice'', ''semweb'')), luego solo los marcadores que se han tenido en cuenta las tres etiquetas buscadas (HAVING COUNT (b.bId) = 3).
Union (OR) Consulta para "bookmark | webservice | semweb": simplemente omita la cláusula HAVING y tiene unión:
SELECT b.*
FROM scBookmarks b, scCategories c
WHERE c.bId = b.bId
AND (c.category IN (''bookmark'', ''webservice'', ''semweb''))
GROUP BY b.bId
Minus (Exclusion) Consulta para "bookmark + webservice-semweb", es decir: marcador AND webservice AND NOT semweb.
SELECT b. *
FROM scBookmarks b, scCategories c
WHERE b.bId = c.bId
AND (c.category IN (''bookmark'', ''webservice''))
AND b.bId NOT
IN (SELECT b.bId FROM scBookmarks b, scCategories c WHERE b.bId = c.bId AND c.category = ''semweb'')
GROUP BY b.bId
HAVING COUNT( b.bId ) =2
Salir de HAVING COUNT conduce a la consulta de "bookmark | webservice-semweb".
Solución "Toxi"
Toxi le ocurrió una estructura de tres mesas. A través de la tabla "mapa de marcas", los marcadores y las etiquetas están relacionados n-to-m. Cada etiqueta se puede usar junto con diferentes marcadores y viceversa. Este esquema de DB también lo usa wordpress. Las consultas son bastante similares a las de la solución "scuttle".
Intersección (Y) Consulta de "marcador + servicio web + semweb"
SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN (''bookmark'', ''webservice'', ''semweb''))
AND b.id = bt.bookmark_id
GROUP BY b.id
HAVING COUNT( b.id )=3
Union (OR) Consulta para "bookmark | webservice | semweb"
SELECT b.*
FROM tagmap bt, bookmark b, tag t
WHERE bt.tag_id = t.tag_id
AND (t.name IN (''bookmark'', ''webservice'', ''semweb''))
AND b.id = bt.bookmark_id
GROUP BY b.id
Minus (Exclusion) Consulta para "bookmark + webservice-semweb", es decir: marcador AND webservice AND NOT semweb.
SELECT b. *
FROM bookmark b, tagmap bt, tag t
WHERE b.id = bt.bookmark_id
AND bt.tag_id = t.tag_id
AND (t.name IN (''Programming'', ''Algorithms''))
AND b.id NOT IN (SELECT b.id FROM bookmark b, tagmap bt, tag t WHERE b.id = bt.bookmark_id AND bt.tag_id = t.tag_id AND t.name = ''Python'')
GROUP BY b.id
HAVING COUNT( b.id ) =2
Salir de HAVING COUNT conduce a la consulta de "bookmark | webservice-semweb".
La implementación de tres tablas propuesta funcionará para el etiquetado.
El desbordamiento de pila utiliza, sin embargo, una implementación diferente. Almacenan etiquetas en la columna varchar en la tabla de publicaciones en texto sin formato y usan la indexación de texto completo para buscar publicaciones que coinciden con las etiquetas. Por ejemplo, posts.tags = "algorithm system tagging best-practices"
. Estoy seguro de que Jeff ha mencionado esto en alguna parte pero no recuerdo dónde.
La solución propuesta es la mejor, si no la única, práctica que se me ocurre para abordar la relación de muchos a muchos entre etiquetas y artículos. Entonces mi voto es ''sí, sigue siendo el mejor''. Sin embargo, me interesaría cualquier alternativa.
Me gustaría sugerir MySQLicious optimizado para un mejor rendimiento. Antes de eso, los inconvenientes de la solución Toxi (3 mesas) es
Si tiene millones de preguntas y tiene 5 etiquetas en cada una, habrá 5 millones de entradas en la tabla de mapas de etiquetas. Entonces, primero tenemos que filtrar 10 mil entradas de mapas de marcas basadas en la búsqueda de etiquetas y luego filtrar las preguntas coincidentes de esos 10 mil. Por lo tanto, al filtrar si el identificador artical es numérico simple, entonces está bien, pero si es un tipo de UUID (varchar de 32), el filtrado necesita una mayor comparación aunque esté indexado.
Mi solución:
Siempre que se cree una nueva etiqueta, tenga contador ++ (base 10) y conviértalo en base64. Ahora cada nombre de etiqueta tendrá ID de base64. y pasa esta identificación a UI junto con el nombre. De esta forma, tendrá un máximo de dos ID de tarjeta hasta que tengamos 4095 etiquetas creadas en nuestro sistema. Ahora concatene estas etiquetas múltiples en cada columna de etiqueta de la tabla de preguntas. Agrega delimitador también y haz que esté ordenado.
Así que la mesa se ve así
Al realizar consultas, consulta en el ID en lugar del nombre de la etiqueta real. Dado que está CLASIFICADO , and
condición en la etiqueta será más eficiente ( LIKE ''%|a|%|c|%|f|%
).
Tenga en cuenta que el delimitador de espacio único no es suficiente y necesitamos un delimitador doble para diferenciar etiquetas como sql
y mysql
porque LIKE "%sql%"
también devolverá los resultados de mysql
. Debería ser LIKE "%|sql|%"
Finalmente con esta solución, no se requiere unión interna.
No hay nada malo con su solución de tres mesas.
Otra opción es limitar el número de etiquetas que se pueden aplicar a un artículo (como 5 en SO) y agregarlas directamente a la tabla de artículos.
La normalización de la base de datos tiene sus ventajas e inconvenientes, al igual que el cableado de las cosas en una tabla tiene beneficios y desventajas.
Nada dice que no puedes hacer ambas cosas. Va en contra de los paradigmas de DB relacionales para repetir información, pero si el objetivo es el rendimiento, es posible que tenga que romper los paradigmas.
Si su base de datos es compatible con matrices indexables (como PostgreSQL, por ejemplo), recomendaría una solución completamente desnormalizada: almacenar etiquetas como una matriz de cadenas en la misma tabla. Si no, una asignación de tabla secundaria se opone a las etiquetas es la mejor solución. Si necesita almacenar información adicional en contra de las etiquetas, puede usar una tabla de etiquetas separada, pero no tiene sentido introducir una segunda combinación para cada búsqueda de etiquetas.