筛选(QueryType)

QueryType 描述 Logicform 的过滤条件,语法整体接近 MongoDB,但执行能力比基础类型定义更丰富。它既可以出现在根节点 query 中,也可以出现在 PredItemType.query 中作为“指标级筛选”。同一套结构也会复用于 having,但 having 的字段名基于结果列别名,而不是原始 schema 字段。

本文覆盖了常见写法,包括:

  • 跨表字段链路
  • 对象字段的嵌套筛选
  • 数组字段的特殊语义
  • 时间字段的相对时间、局部时间和层级时间写法
  • hierarchy / parent-child 模型下的 $level$recursive

基本语法

最简单的 query 可以直接写成“字段 = 值”:

{
  "query": {
    "渠道": "自营",
    "地区": { "$in": ["华东", "华北"] },
    "GMV": { "$gte": 1000000 }
  }
}

上述结构等价于 SQL:

WHERE 渠道 = '自营'
  AND 地区 IN ('华东', '华北')
  AND GMV >= 1000000

说明:

  • 字段: 值字段: { "$eq": 值 } 的简写
  • 同一个字段可以同时写多个比较符,例如 { "$gte": 100, "$lt": 1000 }
  • 不同字段之间默认是 AND 关系

值类型

QueryType 的值不只可以是普通标量,还可以是:

  • 标量:stringnumberbooleannull
  • 数组:常用于 $in / $nin
  • 时间值:绝对时间、结构化时间、相对时间、MTD/YTD/QTD/WTD
  • 子查询:完整的 LogicformType
  • 子查询数组:常见于 $in
  • 对象字段上的嵌套 logicform

字段定位方式

直接字段

{
  "query": {
    "品类": "女装"
  }
}

跨表字段路径

query 的 key 可以写成带下划线的跨表字段路径,例如:

{
  "query": {
    "门店_地理位置_省份": "河北省",
    "产品_品牌": "Nike"
  }
}

这类路径规则见 基础结构 中的“跨表字段路径”。

对象字段的嵌套筛选

对于 实体类型 的字段,除了链式字段,还可以直接写成一个嵌套的实体查询:

{
  "query": {
    "产品": {
      "schema": "product",
      "query": {
        "品类": { "$in": ["男装", "女装"] }
      },
      "preds": [
        { "pred": "ID", "name": "_id" }
      ]
    }
  }
}

也可以继续写逻辑组合:

{
  "query": {
    "损益项目": {
      "$and": [
        { "ID": { "$ne": "ZCO001" } },
        { "名称": { "$contains": "费用" } }
      ]
    }
  }
}

按实体 ID 查询对象字段

{
  "query": {
    "产品": {
      "schema": "product",
      "entity_id": "001",
      "query": {
        "ID": "001"
      }
    }
  }
}

支持的操作符

通用比较操作符

操作符说明
$eq / $ne等于 / 不等于
$gt / $gte / $lt / $lte比较运算
$in / $nin包含 / 不包含
$exists判空。true 对应 IS NOT NULLfalse 对应 IS NULL
$and / $or逻辑组合,值为 QueryType[]

常见组合写法:

{
  "query": {
    "销售额": {
      "$gt": 100,
      "$lte": 1000
    }
  }
}

特殊行为:

  • $in: [] 会生成恒假条件
  • $nin: [] 会生成恒真条件
  • 字段: null 等价于 字段 IS NULL

字符串匹配操作符

操作符说明
$regex正则匹配
$options正则 flags 或时间辅助信息
$startsWith前缀匹配
$contains包含子串,通常不区分大小写
$includes当前执行器中与 $contains 等价
$length按字符串长度比较

示例:

{
  "query": {
    "手机号": { "$regex": "^138" },
    "姓名": { "$startsWith": "张" },
    "地址": { "$contains": "浦东" },
    "编码": { "$length": { "$gte": 8, "$lte": 12 } }
  }
}

$eq 也可以配合 $options 做带 flag 的整串正则匹配:

{
  "query": {
    "位置": {
      "$eq": "0864310101",
      "$options": "i"
    }
  }
}

逻辑组合示例

{
  "query": {
    "$and": [
      { "渠道": "自营" },
      {
        "$or": [
          { "地区": "华东" },
          { "地区": "华北" }
        ]
      }
    ],
    "门店": { "$exists": true }
  }
}

根节点还支持一种较少见但已实现的写法:用 $eq / $ne 包一层完整条件对象,例如:

{
  "query": {
    "$ne": {
      "状态": "停用",
      "渠道": "内部"
    }
  }
}

这会被翻译为 NOT (...)

不同字段类型的 query 能力

这里只按运行时能力总结。实际能否生效,还会受到 isArrayis_comparableref、hierarchy 配置等影响。

字符串字段

最常见支持:

  • 字面量 / $eq / $ne
  • $in / $nin
  • $regex / $options
  • $startsWith
  • $contains / $includes
  • $length
  • $exists

如果字符串字段同时配置了 constraints.enumis_comparable = true,那么:

  • $gt/$gte/$lt/$lte 会按枚举顺序比较
  • 如果配置了 enumMap,也会参与比较映射
  • 如果配置了 comparable_order = -1,比较方向会反转

数字字段

最常见支持:

  • 字面量 / $eq / $ne
  • $gt/$gte/$lt/$lte
  • $in/$nin
  • $exists

布尔字段

最常见支持:

  • true / false
  • $eq / $ne
  • $in/$nin
  • $exists

日期字段

最常见支持:

  • 字面量 / $eq / $ne
  • $gt/$gte/$lt/$lte
  • $in/$nin
  • $exists
  • 相对时间表达
  • 局部时间筛选
  • $calendar

对象字段

对象字段不是普通字面量列,而是一个实体引用。常见支持方式有四种:

  • 直接按实体 ID / entity_id
  • 写完整 nested logicform
  • 在对象字段内部直接写 ref schema 的字段条件
  • 用链式字段查询对象下的属性

数组字段的特殊语义

如果字段配置了 isArray = true,执行器会启用一套特殊语义:

  • $eq: "x" 表示“数组包含 x”
  • $ne: "x" 表示“数组不包含 x”
  • $in: ["a", "b"] 表示“数组与给定集合有交集”
  • $contains: "abc" / $includes: "abc" 会把数组内容拼接后做包含匹配

这类能力常见于标签数组、引用数组和部分 PostgreSQL / ClickHouse 数组字段。

子查询

某些条件需要通过子查询表达,此时应把完整的 LogicformType 嵌入值中,而不是只写 operator/pred 片段。

标量子查询

{
  "query": {
    "GMV": {
      "schema": "sales",
      "preds": [
        { "name": "max_gmv", "operator": "$max", "pred": "amount" }
      ]
    }
  }
}

可理解为:

GMV = (SELECT MAX(amount) FROM sales)

集合子查询

{
  "query": {
    "客户ID": {
      "$in": {
        "schema": "orders",
        "query": { "日期": "YTD" },
        "preds": [
          { "pred": "客户ID", "name": "客户ID" }
        ]
      }
    }
  }
}

可理解为:

客户ID IN (SELECT 客户ID FROM orders WHERE 日期 ...)

时间筛选

时间字段是 query 中最复杂的一类。

标准范围写法

{
  "日期": {
    "$gte": "2024-01-01 00:00:00",
    "$lte": "2024-01-31 23:59:59"
  }
}

结构化时间

{
  "日期": {
    "year": 2024,
    "month": 1
  }
}

你可以混用粒度,例如只指定 year/month/day 中的一部分。

相对时间

{
  "日期": {
    "$offset": { "year": -1 },
    "month": 10
  }
}

该示例表示“去年 10 月”。常见 offset 粒度包括 yearquartermonthweekdayhourminutesecond

To-date 简写

{
  "日期": "YTD"
}

支持的 to-date 简写包括 MTDQTDYTDWTD

$options 的时间条件

{
  "日期": {
    "$gte": "2025-01-01 00:00:00",
    "$lte": "2025-08-12 23:59:59",
    "$options": "YTD"
  }
}

$options 在日期场景中常用于保留 YTD/MTD/QTD/WTD 这类口径语义,便于 dashboard 或上层应用回显;在正则场景中则通常表示 flags。

局部时间筛选

执行器支持直接筛选日期字段的局部组成部分:

操作符说明
$second
$minute分钟
$hour小时
$weekday星期
$day
$week
$month
$quarter季度
$year

例如:

{
  "query": {
    "日期": {
      "$month": 10
    }
  }
}

也可以继续套比较符:

{
  "query": {
    "日期": {
      "$hour": {
        "$gte": 9,
        "$lt": 18
      }
    }
  }
}

日期(level) 简写

执行器还支持把时间粒度直接写在 query key 上:

{
  "query": {
    "日期": {
      "$gte": "2026-01-01 00:00:00",
      "$lte": "2026-06-08 23:59:59"
    },
    "日期(month)": "2026-05"
  }
}

normalizer 会把 日期(month) 展开成与主时间范围的交集,然后删除这个简写 key。

其他时间辅助写法

{
  "query": {
    "日期": {
      "period": "am"
    }
  }
}

此外,某些实现还支持 $calendar: "lunar" 这类扩展写法,用于农历等语义日期归一化。

层级与递归筛选

$level

对有 hierarchy 配置的实体,可以按层级筛选:

{
  "query": {
    "地理位置": {
      "$level": "省"
    }
  }
}

它的实际含义通常是限制层级编码长度,或匹配 full-length hierarchy code。

$recursive

对 parent-child 模型的实体,可以递归展开一整棵子树:

{
  "query": {
    "公司": {
      "$recursive": "A"
    }
  }
}

完整写法:

{
  "query": {
    "id": {
      "$recursive": {
        "schema": "company",
        "root": "A",
        "includeSelf": true,
        "parent_property": "pid",
        "id_property": "id"
      }
    }
  }
}

常见参数:

  • root / roots
  • includeSelf / include_self
  • schema
  • parent_property / parentProperty
  • id_property / idProperty

如果当前字段本身是 object 且配置了 ref,很多时候可以省略 schema,执行器会自动从 ref schema 推断。

字段对字段比较

query 的值可以直接引用另一个字段,写法是 $字段名

{
  "query": {
    "折后价": {
      "$lte": "$原价"
    }
  }
}

这表示比较两列的值,而不是把 $原价 当作普通字符串。

最佳实践

  1. 所有字段名都应使用业务建模中的“展示名称”,不要混用数据库物理列名。
  2. query 同时存在于根节点和 pred.query 中时,两者作用范围不同,执行时会分别下推。
  3. 需要子查询时,请传入完整的 LogicformType,不要把 PredItemType 误当成子查询结构。
  4. 日期筛选建议尽量保持同一 Logicform 内的粒度一致,避免自动补时间时产生歧义。
  5. 判断某个字段能否使用某类 query,不能只看字段名,还要结合 isArrayis_comparableref、hierarchy、parent-child 等建模配置一起判断。