如何使用具有不同跳过和限制值的相同查询对搜索结果进行分页

时间:2015-12-25 12:59:49

标签: javascript express neo4j pagination redis

有些人可能会认为这是programmers.stackexchange.com的问题但是,在阅读了Stack Overflow的Help Center后,我认为这是一个特定的编程问题,我更有可能在这里得到回复。

我有一个使用ExpressJS和Neo4j数据库作为后端的webapp。我有一个搜索屏幕,我想利用Neo4j关系的力量。搜索屏幕接受一个或多个值(即制造年份,燃料类型,变速箱等等),然后向ExpressJS发出请求,我使用POST请求的参数构建一个密码查询,如下所示:

MATCH
  (v:VEHICLE),(v)-[:VGEARBOX_IS]->(:VGBOX{type:'MANUAL'}),
  (v)-[:VCONDITION_IS]->(:VCONDITION{condition:'USED'})  
WITH DISTINCT v
WHERE  v.manufacture_year = 1990
MATCH (v)-[r]->(info)
RETURN v AS vehicle, COLLECT({type:type(r), data:info}) AS details

假设运行以上查询,返回以下三种车辆及其属性

enter image description here

如果结果超过20辆,那么我想对结果进行分页并且我知道它是如何工作的,我们使用SKIP和LIMIT,如下所示:

MATCH
  (v:VEHICLE)
OPTIONAL MATCH (v)-[r:VFUEL_TYPE|:VGEARBOX_IS|:VHAVING_COLOR|...]->(info)
RETURN
  v.vehicle_id AS vehicle_id,
  v.engine_size AS engine_size,
  v.published_date AS published_date,
  COUNT(v) AS count,
  COLLECT({type:type(r), data:info}) as data
ORDER BY v.published_date DESC
SKIP 20
LIMIT 16

这是工作流程,

  • 用户导航到搜索屏幕,这是一个带有POST方法和各种输入字段的表单。
  • 用户根据他/她希望搜索的内容选择一些选项。
  • 用户然后提交表单,该表单向服务器发出发布请求。
  • 此请求由ROUTE处理,ROUTE使用请求的参数构建上面显示的密码查询。它针对Neo4j数据库运行cypher并接收结果。
  • 假设有200辆车匹配搜索结果。然后,我想只显示其中的20个结果并提供下一个/上一个按钮。
  • 当用户看到前20个时,他/她想要看到接下来的20个,那就是我必须重新运行用户最初提交的相同查询,但SKIP值为20(SKIP值保持递增) 20用户导航到下一页,当你移动到上一页时减少20)。

我的问题是,保存搜索请求(或原始请求生成的密码)的最佳方法是什么,这样当用户点击下一页/上一页时,我会用不同的SKIP重新运行原始搜索密码查询值?每次用户进入下一页/上一页时,我都不想发出新的POST请求。这个问题可以通过以下方式解决,但不确定哪个更适合性能?

  1. 每次用户点击下一页或上一页时,我都会创建一个新的POST请求,并保留原始请求的值并重建密码查询(POST可能代价高昂 - 我想避免这种情况,请说明为什么这是更好的选择)
  2. 我将原始的密码查询存储在Redis中,每当用户点击下一个或上一个时,我都会从Redis中检索特定于该用户的查询(需要通过cookie,会话或某种隐藏的uuid来处理),提供SKIP的新值并重新运行它(当我从Redis删除此条目时,我必须处理 - 当用户更改其搜索或放弃页面/站点时,应该删除)。
  3. 我将查询存储在会话中(用户无需登录)或其他一些提供快速访问的临时存储(而不是Redis)(不确定这是否安全有效)
  4. 我确信有人会以有效的方式遇到这个问题,这就是我在这里发布问题的原因。请告知我如何才能最好地解决这个问题。

1 个答案:

答案 0 :(得分:1)

就性能而言,首先应该使用Cypher parameters。这是一种将查询字符串与动态数据分开的方法。这样做的好处是可以防止注入攻击,但它的性能也更高,因为如果查询字符串没有更改,Neo4j可以缓存查询的查询计划并反复使用它。使用参数,您的第一个查询将如下所示:

public function fetchAllRows($table, $rowOrder, $direction,$con)

你的Neo4j javascript库应该允许你传递一个单独的对象。这是json中表示的对象:

MATCH
  (v:VEHICLE),(v)-[:VGEARBOX_IS]->(:VGBOX{type: {vgearbox_type}}),
  (v)-[:VCONDITION_IS]->(:VCONDITION{condition: {vcondition}})  
WITH DISTINCT v
WHERE  v.manufacture_year = {manufacture_year}
MATCH (v)-[r]->(info)
RETURN v AS vehicle, COLLECT({type:type(r), data:info}) AS details
SKIP ({page} - 1) * {per_page}
LIMIT {per_page}

每次从Node向数据库进行新查询时,我都没有发现太多问题。您应该确定实际需要多长时间才能确定它是否确实存在问题。

如果您希望通过缓存解决这个问题,则取决于您的服务器设置。如果节点应用程序和数据库位于同一台机器上或彼此非常接近,可能它并不重要。否则,您可以使用redis基于一个键进行缓存,该键是您要查询的值的组合。如果您考虑基于每个用户进行缓存,您甚至可以使用浏览器的本地存储,但用户是否经常反复访问相同的页面?您的数据的静态程度如何,数据因用户而异吗?