在我的应用程序中,我需要一个类似SQL的文档查询。大局是有一个页面带有分页表,显示某个“类型”的couchdb文档。我有大约15个可搜索的列,如时间戳,客户名称,美国状态,不同的数字字段等。所有这些列都是可订购的,还有一个过滤器表单,允许用户按每个字段进行过滤。
下面是一个更具体的问题,这是一个典型的查询,它是客户设置一些过滤器选项并跟随第二页的结果。它用pseodo-sql代码编写,只是为了解释问题:
timestamp > last_weeks_monday_epoch AND timestamp < this_weeks_monday_epoch AND marked_as_test = False AND dataspace="production" AND fico > 650
SORT BY timestamp DESC
LIMIT 15
SKIP 15
如果我使用任何类似sql的数据库,这将是一个微不足道的问题,但是couchdb更有趣;)为了解决这个问题,我创建了一个具有以下发射行结构的视图:
key: [field, value], id: doc._id, value: null
现在,要解决上面的示例查询,我需要执行一堆查询:
{startkey: ["timestamp", last_weeks_monday_epoch], endkey: ["timestamp", this_weeks_monday_epoch]}
,这里的*_epoch
是整数纪元时间戳,
{key: ["marked_as_test", False]}
,
{key: ["dataspace", "production"]}
,
{startkey: ["fico", 650], endkey: ["fico", {}]}
获得上述查询的结果后,我计算文档ID集的交集,并使用timestamp查询的结果应用排序。最后我可以应用解析行15-30的文档ID的片段并使用批量获取操作下载它们的内容。
毋庸置疑,它不是最快的操作。目前我正在使用的数据集大约是10K文档。我已经可以看到,当我计算集合的交集时,该部分可能需要4秒,显然我需要进一步优化它。我害怕思考,当我的数据集加倍,三倍等时,它会在几个月内变得多慢。
好的,所以在解释了我所处的情况后,让我问实际的问题。
在不失去工具灵活性的情况下,是否有更好,更自然的方法来实现我的目标?
我使用的视图结构是否最佳?在某些时候,我正在考虑使用一个单独的map()函数来生成每个字段的值。这将导致更小的b树,但视图服务器的更多工作将生成索引。我可以这样受益吗?
算法的一部分,我必须计算大集合的交集,以便稍后得到结果的切片困扰我。它不是一种可扩展的方法。有没有人知道更好的算法?
答案 0 :(得分:1)
有地图功能:
function(doc){
if(doc.marked_as_test) return;
emit([doc.dataspace, doc.timestamp, doc.fico], null):
}
你可以提出类似的要求:
http://localhost:5984/db/_design/ddoc/_view/view?startkey=["production", :this_weeks_monday_epoch]&endkey=["production", :last_weeks_monday_epoch, 650]&descending=true&limit=15&skip=15
但是,您应该从客户端传递:this_weeks_monday_epoch
和:last_weeks_monday_epoch
值(我相信它们是数据库端的一些可计算变量,对吗?)
如果您不关心dataspace
字段(例如,它始终是常量),您可以将其移动到地图功能代码中,而不是将其放在查询参数中。
答案 1 :(得分:1)
我不认为CouchDB非常适合您的问题的一般解决方案。但是,有两种基本方法可以缓解CouchDB解决问题的方式。
编写/生成一堆map()
函数,这些函数使用每个单独的列作为键(为了更好的读/查询性能,您甚至可以进行组合方法)。这样你就可以进行智能过滤和排序,利用数据上的一堆不同索引。另一方面,这将花费额外的磁盘空间和索引缓存性能。
尝试找出用户实际使用的过滤器/排序顺序,并针对这些过滤/排序顺序进行优化。似乎不太可能使用过滤器/排序顺序的每个组合,因此您应该能够找到一些最常用的模式并编写最适合这些模式的视图函数。
我更喜欢第二种选择,但这取决于你的用例。这是SQL引擎传统上非常擅长的事情之一。