查询开始时的过滤会中断查询结束时的过滤

Filtering at start of query breaks filtering at end of query

这是我的 JSON:

{
   "items":[
      {
         "fieldone":"sdfsdfsdfsdf",
         "fieldtwo":{
            "subfieldTwo":{
               "aaa/bbbb":"test-app-one"
            }
         },
         "fieldthree":{
            "subfieldThree":[
               {
                  "subObjField":"mark",
                  "otherfieldA":"sdfsdfsdf",
                  "otherfieldB":"dfsdfsdfsdf"
               },
               {
                  "subObjField":"mark",
                  "otherfieldA":"sdfsdfsdf",
                  "otherfieldB":"dfsdfsdfsdf"
               },
               {
                  "subObjField":"nomark",
                  "otherfieldA":"sdfsdfsdf",
                  "otherfieldB":"dfsdfsdfsdf"
               }
            ]
         }
      }
   ]
}

我想筛选 fieldone == "sdfsdfsdfsdf" 的项目和 return subfieldThreesubObjField == "mark" 对象。我不能让这两个东西同时工作。

以下正确returns子字段所有项的三个对象,其中subObjField == "mark"

items[*].fieldthree.subfieldThree[?subObjField == `mark`]

以下returns all subfieldThree objects of all items where fieldone == "sdfsdfsdfsdf"

items[?fieldone == `sdfsdfsdfsdf`].fieldthree.subfieldThree

但是,如果我尝试应用 subObjField 过滤器,我不会得到任何输出:

items[?fieldone == `sdfsdfsdfsdf`].fieldthree.subfieldThree[?subObjField == `mark`]

为什么当我添加第一个过滤器时 fieldthree.subfieldThree[?subObjField == `mark`] 完全崩溃?

我什至尝试过 | 但它仍然无法正常工作,这是 jmespath 的错误或限制吗?

items[?fieldone == `sdfsdfsdfsdf`].fieldthree.subfieldThree | @[?subObjField == `mark`]

我不确定为什么,但我需要像这样展平第一个查询的结果:

items[?fieldone == `sdfsdfsdfsdf`].fieldthree.subfieldThree[] | [?subObjField == `mark`]

这行得通,但我仍然不知道为什么它会这样——你什么时候需要使用 | 什么时候不需要?

因此,您的自我回答表明解决方案确实是

items[?fieldone == `sdfsdfsdfsdf`]
  .fieldthree
  .subfieldThree[] | [?subObjField == `mark`]

但是你不明白pipe expression的用法的原因。


TL;DR;

这是因为您的第一个过滤器 [?fieldone == `sdfsdfsdfsdf`] 产生了投影,您需要停止该投影。
而且,由于停止投影是通过 JMESPath 中的 pipe expression 完成的,因此这是正确的方法。


我们实际上可以在非常简化的 JSON:

上理解该行为
{
  "items": [{
    "fox": "quick",
    "dog": "lazy"
  }]
}

在这个 JSON 上,你会做过滤器 items[?fox == `quick`] 你会得到完全相同的对象数组并且可能会想 “好吧,这是一个数组,我可以投入另一个过滤器,在那里.
因此,您将执行 items[?fox == `quick`][?dog == `lazy`],这就是故事的结尾,因为 JMESPath 将在 return.

中为您生成一个非常空的数组 — []

注意:当然,在这种简化的情况下,您会诉诸于使用 and 运算符 — && — 并且会逃脱更简单的查询:items[?fox == `quick` && dog == `lazy`]

为什么会这样呢?
好吧,因为在应用过滤器之后,您可以假设是一个数组的东西不再是一个数组,它就是 JMESPath 所称的 projection.

从该章或教程中我们了解到:

There’s a few things to keep in mind when working with projections. These are discussed in more detail in the wildcard expressions section of the spec, but the main points are:

  • Projections are evaluated as two steps. The left hand side (LHS) creates a JSON array of initial values. The right hand side (RHS) of a projection is the expression to project for each element in the JSON array created by the left hand side. Each projection type has slightly different semantics when evaluating either the left hand side and/or the right hand side.
  • If the result of the expression projected onto an individual array element is null, then that value is omitted from the collected set of results.
  • You can stop a projection with a Pipe Expression (discussed later). A list projection is only valid for a JSON array. If the value is not a list, then the result of the expression is null.

来源:https://jmespath.org/tutorial.html#list-and-slice-projections

因此,您可以考虑将那些应用到现有数组的视图真正类似于 Python 或 SQL 视图。

由于上面文档中解释的两个步骤,这一章更进一步:

Projections are an important concept in JMESPath. However, there are times when projection semantics are not what you want. A common scenario is when you want to operate of the result of a projection rather than projecting an expression onto each element in the array.

来源:https://jmespath.org/tutorial.html#pipe-expressions

这正是您在这里需要的,您不想将过滤器 fieldthree.subfieldThree[?subObjField == `mark`] — 右侧 (RHS) — 应用于 items[?fieldone == `sdfsdfsdfsdf`] 的每个元素,您想要而是将其应用于结果数组。

所以你需要一个管道表达式才能做到这一点,管道表达式表示投影必须停止。

不过,您可以在对象树中的任意位置停止它:

  • items[?fieldone == `sdfsdfsdfsdf`] | []
      .fieldthree
      .subfieldThree[?subObjField == `mark`][]
    
  • items[?fieldone == `sdfsdfsdfsdf`]
      .fieldthree | []
      .subfieldThree[?subObjField == `mark`][]
    
  • items[?fieldone == `sdfsdfsdfsdf`]
      .fieldthree
      .subfieldThree[] | [?subObjField == `mark`]
    

三种风格的查询会给出完全相同的结果。

一旦你停止了现有的投影,并以实际的对象数组结束,你就回到了原点,可以根据需要应用另一个投影。

因此,根据经验,如果您需要 “链” 过滤器,那么您知道您必须在某个时候重置投影,并且需要管道表达式.