我正在使用php和mysql构建一个颜色搜索功能。搜索的要求是它需要快速,而不是使用连接,并允许1-5个十六进制颜色输入查询数据库并返回“最准确”的结果。 “最准确”是指结果将反映搜索输入。我有一些数据可以帮助你,例如映射的颜色值(映射到预定义颜色数组)与原始搜索输入十六进制值(例如ff0000
)之间的距离。
颜色搜索引擎的工作方式是输入1-5个十六进制值(例如#ff0000,#000000,#9ef855等),点击搜索,然后搜索数据库以查找包含最高百分比的图像那些颜色。有关颜色搜索引擎的工作原理,请参阅this color search。 注意:我构建了这个,但是它有一个完全不同的模式,它有缩放问题而且无法添加索引,因为颜色的数量与表列的数量直接相关,即120.建议我使用我所拥有的现在已经无法建立。
数据库中的数据来自对图像的测量。从图像中提取最多5种颜色,然后将每个十六进制颜色值(hex
)映射到最接近的预定义十六进制值(map_hex
)。这两个数据以及以下数据都存储在数据库中:
在将颜色搜索查询发送到数据库之前,它会映射到一组颜色,以便我们可以使用映射在map_hex
上进行直接查找。这对我来说似乎比尝试进行范围类型的查询更快。
截至目前,我正在尝试两种数据库设计模式,但两者似乎都有自己的问题。
架构1
CREATE TABLE `media_has_colors` (
`media_id` int(9) unsigned NOT NULL,
`hex` varchar(6) NOT NULL DEFAULT '',
`map_hex` varchar(6) NOT NULL,
`percentage` double unsigned NOT NULL,
`distance` double unsigned NOT NULL,
`sequence` int(11) unsigned NOT NULL,
PRIMARY KEY (`media_id`,`hex`),
KEY `index_on_hex` (`hex`),
KEY `index_on_percentage` (`percentage`),
KEY `index_on_timestamp` (`sequence`),
KEY `index_on_media_id` (`media_id`),
KEY `index_on_mapping_distance` (`distance`),
KEY `index_on_mapping_hex` (`map_hex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
示例查询:
SELECT sql_no_cache media_id, hex, map_hex, distance,
avg(percentage) as percentage,
SUM((IF(map_hex = '61615a',1,0)) + (IF(map_hex = '34362d',1,0)) + (IF(map_hex = 'dbd5dd',1,0))) as matchCount
FROM media_has_colors
WHERE map_hex = '61615a' or map_hex = '34362d' or map_hex = 'dbd5dd'
GROUP BY media_id
ORDER BY matchCount DESC, distance, percentage DESC
LIMIT 100;
我在架构1中看到的第一个问题是我被迫使用group by
和sum
。我承认我还没有测试过大量的记录,但似乎它可能会变慢。最重要的是,我无法确定哪些map_hex
值匹配(这就是为什么我要尝试使用matchCount
。
架构2
CREATE TABLE `media_has_colors` (
`media_id` int(9) unsigned NOT NULL,
`color_1_hex` varchar(6) NOT NULL DEFAULT '',
`color_2_hex` varchar(6) NOT NULL DEFAULT '',
`color_3_hex` varchar(6) NOT NULL DEFAULT '',
`color_4_hex` varchar(6) NOT NULL DEFAULT '',
`color_5_hex` varchar(6) NOT NULL DEFAULT '',
`color_1_map_hex` varchar(6) NOT NULL DEFAULT '',
`color_2_map_hex` varchar(6) NOT NULL DEFAULT '',
`color_3_map_hex` varchar(6) NOT NULL DEFAULT '',
`color_4_map_hex` varchar(6) NOT NULL DEFAULT '',
`color_5_map_hex` varchar(6) NOT NULL DEFAULT '',
`color_1_percent` double unsigned NOT NULL DEFAULT '0',
`color_2_percent` double unsigned NOT NULL DEFAULT '0',
`color_3_percent` double unsigned NOT NULL DEFAULT '0',
`color_4_percent` double unsigned NOT NULL DEFAULT '0',
`color_5_percent` double unsigned NOT NULL DEFAULT '0',
`color_1_distance` double unsigned NOT NULL DEFAULT '0',
`color_2_distance` double unsigned NOT NULL DEFAULT '0',
`color_3_distance` double unsigned NOT NULL DEFAULT '0',
`color_4_distance` double unsigned NOT NULL DEFAULT '0',
`color_5_distance` double unsigned NOT NULL DEFAULT '0',
`sequence` int(11) unsigned NOT NULL,
PRIMARY KEY (`media_id`),
KEY `index_on_timestamp` (`sequence`),
KEY `index_on_map_hex` (`color_1_map_hex`,`color_2_map_hex`,`color_3_map_hex`,`color_4_map_hex`,`color_5_map_hex`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第二个架构并不那么简单,但它确实避免了使用group,每个媒体只允许1行。但是,它似乎也有同样的问题,即找出map_hex
值匹配的内容。以下是一个示例查询:
SELECT sql_no_cache media_id,
(IF(color_1_percent = '61615a',color_1_percent,1)) *
(IF(color_2_percent = '34362d',color_2_percent,1)) *
(IF(color_3_percent = 'dbd5dd',color_3_percent,1)) as percentage,
(IF(color_1_distance = '61615a',color_1_distance,1)) +
(IF(color_2_distance = '34362d',color_2_distance,1)) +
(IF(color_3_distance = 'dbd5dd',color_3_distance,1)) as distance,
color_1_map_hex, color_2_map_hex, color_3_map_hex, color_4_map_hex, color_5_map_hex,
(IF(color_1_map_hex = '61615a',1,1)) +
(IF(color_2_map_hex = '34362d',1,1)) +
(IF(color_3_map_hex = 'dbd5dd',1,1)) as matchCount
FROM media_has_colors
WHERE color_1_map_hex IN ('61615a','34362d','dbd5dd') OR
color_2_map_hex IN ('61615a','34362d','dbd5dd') OR
color_3_map_hex IN ('61615a','34362d','dbd5dd')
ORDER BY matchCount DESC, distance, percentage DESC
LIMIT 100;
您可以看到计算百分比和距离时出现问题,因为实际的map_hex值可能不会出现在这些特定列中。
更新: 我不需要具体知道查询中匹配的颜色,但我需要按哪个匹配最高匹配。
所以我的问题是,如何修复架构或查询?如果没有,是否有更好的解决方案?