假设我有一个存储在关系数据库中的大型(300-500k)文本文档集合。每个文档可以属于一个或多个(最多六个)类别。我需要用户能够随机选择特定类别的文档,以便永远不会重复单个实体,就像StumbleUpon的工作方式一样。
我真的没有看到使用带有大量用户和文档的慢速NOT IN查询来实现这一点的方法,所以我想我可能需要为此目的实现一些自定义数据结构。也许已经有一篇论文描述了一些可能适合我需求的算法?
目前我正在考虑以下方法:
答案 0 :(得分:2)
这取决于用户如何获得随机条目。
选项1:
用户正在分页某些实体并在其中几个实体后停止。例如,用户看到当前的随机实体,然后移动到下一个实体,读取它并继续几次,就是这样。 在下一次该用户(或另一个)从此类别中获取实体时,已查看的实体是明确的,您可以返回已查看的实体。
在该选项中,我建议保存已经查看过的实体id的(散列)集合,每次用户请求随机实体时,从数据库中选择它并检查是否已经存在于集合中。
因为这个集很小而且你的数据太大了,你获得已经查看过的id的可能性很小,大部分时间都需要O(1)。
选项2:
用户正在实体中进行分页,并且每次用户访问您的页面时,所查看的实体都在所有用户之间保存。 在这种情况下,您可能会使用每个类别中的所有实体并保存所有已查看的实体+检查实体是否被查看需要一些时间。
在该选项中,我将获得此主题的所有ID - 将它们随机播放并将其存储在链接列表中。当你想获得一个随机未被查看的实体时 - 只需获取列表的头部并将其删除(O(1))。
答案 1 :(得分:2)
如果您通过表格跟踪用户看到的条目...请尝试此操作。我将使用mysql,因为这是我能想到的最快的例子,但要点应该是明确的。
在'使用'的链接上......
insert into viewed (userid, url_id) values ("jj", 123)
寻找链接......
select p.url_id
from pages p left join viewed v on v.url_id = p.url_id
where v.url_id is null
order by rand()
limit 1
这会导致数据库继续执行1对1连接,并限制查询只返回用户尚未看到的一个条目。
只是一个建议。
编辑:可以进行一次操作,但不能保证网址会成功传递给用户。
答案 2 :(得分:1)
我认为对于任何给定的< user,category>对,相对于该类别中可用文档的总数,查看的文档数量非常少。
所以你可以存储索引三元组< user,category,document>指出哪些文档已被查看,然后对随机选择的文档采取乐观的方法?在绝大多数情况下,用户将读取随机选择的文档。并且您可以快速检查,因为三元组已编入索引。
答案 3 :(得分:1)
我会选择伪随机方法:
1。)确定要查看的类别中的元素数量(SELECT COUNT(*)WHERE ...)
2.)选择范围1 ...计数的随机数
3.)选择单个文档(SELECT * FROM ... WHERE [与计数时相同] ORDER BY [生成稳定顺序]。根据使用的SQL方言,有不同的子句可用于仅检索部分您想要的结果集(MySQL LIMIT子句,SQLServer TOP子句等)
如果文档的数量很大,那么为同一用户提供两次相同文档的机会几乎是微不足道的。使用上述方案,您根本不必存储任何状态信息。
答案 4 :(得分:1)
您可能需要考虑像Apache Cassandra这样的nosql解决方案。这些似乎非常适合您的需求。有许多方法可以在一个环境中设计所需的算法,在这种环境中,您可以轻松地将新列添加到表(列族)中,并且可以非常支持非常稀疏的表。
编辑:以下众多可能的解决方案之一:
如果你能接受Cassandra的“最终一致”模型(即,用户永远不会获得重复文档并不是关键任务),你应该获得恒定的写入和读取时间,惊人的可扩展性等等。
答案 5 :(得分:1)
我过去通过使用Apache Lucene将关系数据库索引到面向文档的表单来解决类似的问题。这是在NoSQL服务器最近兴起之前,基本上是相同的,但它仍然是一种有效的替代方法。
您可以使用textId(关系数据库ID)字段和多值categoryId和userId字段为每个文本创建Lucene Document。适当填充categoryId字段。当用户阅读文本时,将其ID添加到userId字段。一个简单的查询将返回具有给定categoryId且没有给定userId的文档集 - 随机选择一个并显示它。
答案 6 :(得分:0)
我会尝试找到X的最佳值,但我想到的是像16的X那样的东西吗?