【问题标题】:How to paginate a search result using same query with different skip and limit values如何使用具有不同跳过和限制值的相同查询对搜索结果进行分页
【发布时间】:2015-12-25 12:59:49
【问题描述】:

你们中的一些人可能会争辩说这是programmers.stackexchange.com 的问题,但是,在阅读了 Stack Overflow 的Help Center 之后,我相信这是一个特定的编程问题,我更有可能在这里得到答复。

我有一个使用 ExpressJS 和 Neo4j 数据库作为后端的 web 应用程序。我有一个搜索屏幕,我想在其中使用 Neo4j 关系的力量。搜索屏幕接受一个或多个值(即制造年份、燃料类型、变速箱等),然后向 ExpressJS 发出一个 post 请求,在此我使用 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

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

如果结果中的车辆超过 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 数据库运行密码并接收结果。
  • 假设有 200 辆车与搜索结果匹配。然后我只想显示其中的 20 个结果并提供下一个/上一个按钮。
  • 当用户看完前 20 个后,他/她想看到下一个 20,也就是说,当我必须重新运行用户最初提交的相同查询时,SKIP 值为 20(SKIP 值不断增加20 用户导航到下一页,并减少 20 移动到上一页)。

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

  1. 每次用户单击下一页或上一页时,我都会使用原始请求的保留值发出一个新的 POST 请求并重建密码查询(POST 的成本可能很高 - 我想避免这种情况,请说明为什么这是更好的选择)
  2. 我将原始密码查询存储在 Redis 中,每当用户单击下一个或上一个时,我都会从 Redis 检索特定于该用户的查询(需要通过 cookie、会话或某种隐藏的 uuid 处理),提供SKIP 的新值并重新运行它(当我应该从 Redis 中删除此条目时,我必须以某种方式处理 - 当用户更改他的搜索或放弃页面/站点时应该发生删除)。
  3. 我将查询存储在会话中(用户不必登录)或其他一些提供快速访问的临时存储(而不是 Redis)(不确定这是否安全有效)

我确信有人以一种有效的方式遇到了这个问题,这就是我在这里发布问题的原因。请告诉我如何最好地解决这个问题。

【问题讨论】:

    标签: javascript express neo4j pagination redis


    【解决方案1】:

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

    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}
    

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

    {
      "vgearbox_type": "MANUAL",
      "vcondition": "USED",
      "manufacture_year": 1990,
      "page": 1,
      "per_page": 20
    }
    

    我认为每次从 Node 对数据库进行新查询并没有什么问题。您应该对实际需要多长时间进行基准测试,看看它是否真的是一个问题。

    如果您想通过缓存解决问题,则取决于您的服务器设置。如果 Node 应用程序和 DB 在同一台机器上或彼此非常接近,则可能并不重要。否则,您可以使用 redis 基于一个键来缓存,该键是您查询的值的组合。如果您正在考虑基于每个用户进行缓存,您甚至可以使用浏览器的本地存储,但用户是否经常一遍又一遍地重新访问相同的页面?您的数据有多静态?数据是否因用户而异?

    【讨论】:

    • 感谢密码参数的建议,我肯定会调查注入问题。关于每次用户转到下一页/上一页时都进行新的 POST,我应该如何保留原始请求中的帖子值?隐藏字段?会话?
    • 我建议在每个 POST 中发送所有表单参数。它根本不应该增加您的请求大小。这可能是隐藏字段,也可能是您的 javascript 的编写方式,具体取决于您进行 POST 的方式。如果您使用类似会话的东西,我怀疑您最终会遇到至少一个错误,否则您会尝试跟踪事情。
    • 感谢您抽出宝贵时间回复帖子,很有道理。
    猜你喜欢
    • 2019-10-26
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 2017-09-21
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    相关资源
    最近更新 更多