Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySQL/PHP:Encontre itens semelhantes/relacionados por tag/taxonomia


Você pergunta sobre Como eu calculo quais são as cidades mais próximas? Por exemplo. Se eu estivesse olhando para a cidade 1 (Paris), os resultados seriam:Londres (2), Nova York (3) e com base no conjunto de dados fornecido, há apenas uma coisa a relacionar que são as tags comuns entre as cidades, portanto, as cidades que compartilham as tags comuns seriam a mais próxima abaixo é a subconsulta que encontra as cidades (além da que é fornecida para encontrar suas cidades mais próximas) que compartilha as tags comuns
SELECT * FROM `cities`  WHERE id IN (
SELECT city_id FROM `cities_tags` WHERE tag_id IN (
SELECT tag_id FROM `cities_tags` WHERE city_id=1) AND city_id !=1 )

Trabalhando


Suponho que você inserirá um dos id ou nome da cidade para encontrar o mais próximo no meu caso "Paris" tem o id
 SELECT tag_id FROM `cities_tags` WHERE city_id=1

Ele encontrará todos os IDs de tags que Paris tem então
SELECT city_id FROM `cities_tags` WHERE tag_id IN (
    SELECT tag_id FROM `cities_tags` WHERE city_id=1) AND city_id !=1 )

Ele irá buscar todas as cidades exceto paris que tem as mesmas tags que paris também tem

Aqui está o seu Fiddle

Ao ler sobre a semelhança/índice de Jaccard encontrei algumas coisas para entender sobre o que realmente são os termos, vamos dar este exemplo, temos dois conjuntos A e B

Agora vá em direção ao seu cenário

Aqui está a consulta até agora que calcula o índice jaccard perfeito, você pode ver o exemplo de violino abaixo
SELECT a.*, 
( (CASE WHEN a.`intersect` =0 THEN a.`union` ELSE a.`intersect` END ) /a.`union`) AS jaccard_index 
 FROM (
SELECT q.* ,(q.sets + q.parisset) AS `union` , 
(q.sets - q.parisset) AS `intersect`
FROM (
SELECT cities.`id`, cities.`name` , GROUP_CONCAT(tag_id SEPARATOR ',') sets ,
(SELECT  GROUP_CONCAT(tag_id SEPARATOR ',')  FROM `cities_tags` WHERE city_id= 1)AS parisset

FROM `cities_tags` 
LEFT JOIN `cities` ON (cities_tags.`city_id` = cities.`id`)
GROUP BY city_id ) q
) a ORDER BY jaccard_index DESC 

Na consulta acima, tenho derivado o resultado definido para duas subseleções para obter meus aliases calculados personalizados



Você pode adicionar o filtro na consulta acima para não calcular a semelhança com ele mesmo
SELECT a.*, 
( (CASE WHEN a.`intersect` =0 THEN a.`union` ELSE a.`intersect` END ) /a.`union`) AS jaccard_index 
 FROM (
SELECT q.* ,(q.sets + q.parisset) AS `union` , 
(q.sets - q.parisset) AS `intersect`
FROM (
SELECT cities.`id`, cities.`name` , GROUP_CONCAT(tag_id SEPARATOR ',') sets ,
(SELECT  GROUP_CONCAT(tag_id SEPARATOR ',')  FROM `cities_tags` WHERE city_id= 1)AS parisset

FROM `cities_tags` 
LEFT JOIN `cities` ON (cities_tags.`city_id` = cities.`id`) WHERE  cities.`id` !=1
GROUP BY city_id ) q
) a ORDER BY jaccard_index DESC

Portanto, o resultado mostra que Paris está intimamente relacionada a Londres e depois relacionada a Nova York

Jaccard Similarity Fiddle