{
  "schema_version": 2,
  "id": "develop/ai/search-and-query/advanced-concepts/aggregations-syntax",
  "title": "FT.AGGREGATE order of operations",
  "url": "https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations-syntax/",
  "summary": "Order of operations for the FT.AGGREGATE command",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-11T15:33:04+01:00",
  "page_type": "content",
  "content_hash": "01da9106f60e8c5e6908b19e3f4ec526e6b5b3d5c0c70eb41e0ec3e9d1fa0284",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "[`FT.AGGREGATE`](https://redis.io/docs/latest/commands/ft.aggregate) is a powerful Redis Search command for performing advanced data aggregation, filtering, sorting, and transformations on indexed hash or JSON documents. This reference page provides a structured breakdown of syntax, ordering rules, and best practices.\n\nThe [main aggregations page](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) has a simple diagram showing how `FT.AGGREGATE` pipelines are constructed, but it doesn't tell the whole story. For example, you can create more complex aggregation pipelines by applying multiple `REDUCE` functions under a single `GROUPBY` clause, or you can chain groupings and mix in additional mapping steps:\n\n`GROUPBY` ... `REDUCE` ... `APPLY` ... `GROUPBY` ... `REDUCE`"
    },
    {
      "id": "syntax-and-expression-ordering",
      "title": "Syntax and expression ordering",
      "role": "syntax",
      "text": "The `FT.AGGREGATE` command processes multiple expressions in a pipeline. Below is the recommended order:\n\n1. `index` – the name of your index, which must be the first argument.\n1. `query` – your query, which must be the second argument.\n1. `FILTER` – filters raw documents before transformations or aggregation.\n1. `LOAD` – loads document fields.\n1. `APPLY` – applies transformations on fields.\n1. `GROUPBY` – groups results by specific fields.\n1. `REDUCE` – performs aggregations. For example, `SUM`, `COUNT`, and `AVG`.\n1. `SORTBY` – orders the results based on specified fields.\n1. `LIMIT` – restricts the number of results returned.\n1. `DIALECT 2` - provides for more comprehensive query syntax, for example using parameters in `FILTER` expressions.\n\nOther keywords will be discussed toward the end of this page."
    },
    {
      "id": "when-to-use-",
      "title": "When to use `@`",
      "role": "content",
      "text": "You must add `@` at the start of a field name in the following circumstances:\n\n- When referencing fields loaded from documents. In the following example, `price` is a document field and must be prefixed with `@`.\n\n[code example]\n\n- When referencing fields inside a `FILTER` clause that were loaded from documents.\n\n[code example]\n\n- When referencing fields inside `GROUPBY` or `REDUCE` clauses.\n\n[code example]\n\n- When referencing fields created by `REDUCE` in `APPLY` or `FILTER` clauses.\n\n[code example]\n\n- When referencing fields created by `APPLY` in another `APPLY` or `FILTER` clause.\n\n[code example]\n\n- When referencing fields created by `APPLY` in a `SORTBY` clause.\n\n[code example]"
    },
    {
      "id": "groupby-with-multiple-reduce-operations",
      "title": "GROUPBY with multiple REDUCE operations",
      "role": "content",
      "text": "You can use multiple `REDUCE` operations after `GROUPBY` for different aggregations.\n\n[code example]"
    },
    {
      "id": "multiple-apply-operations-followed-by-groupby-and-reduce",
      "title": "Multiple APPLY operations followed by GROUPBY and REDUCE",
      "role": "content",
      "text": "You can use `APPLY` in various ways before and after `GROUPBY` and `REDUCE`.\n\n[code example]"
    },
    {
      "id": "filter-and-params",
      "title": "FILTER and PARAMS",
      "role": "content",
      "text": "Use `FILTER` to remove unwanted records, and `PARAMS` to pass values to parameterized queries.\n\n[code example]"
    },
    {
      "id": "placement-of-filter-before-and-after-groupbyapply",
      "title": "Placement of FILTER before and after GROUPBY/APPLY",
      "role": "content",
      "text": "- **Before GROUPBY:** Removes records before aggregation.\n- **After GROUPBY:** Filters based on aggregated results."
    },
    {
      "id": "load-after-groupbyreduce",
      "title": "LOAD after GROUPBY/REDUCE",
      "role": "content",
      "text": "This is not allowed and you'll get a syntax error."
    },
    {
      "id": "placement-rules-for-specific-parameters",
      "title": "Placement rules for specific parameters",
      "role": "content",
      "text": "| Parameter    | Placement                                                 |\n|-----         |-----                                                      |\n| `TIMEOUT`    | Can be placed anywhere.                                   |\n| `LIMIT`      | Must be at the end, before `DIALECT`.                     |\n| `WITHCURSOR` | Must be at the end, before `DIALECT`.                     |\n| `SCORER`     | Must be placed between the query and pipeline operations. |\n| `ADDSCORES`  | Must be placed between the query and pipeline operations. |\n| `DIALECT`    | Must be at the end.                                       |"
    },
    {
      "id": "limit-and-withcursor-used-together",
      "title": "LIMIT and WITHCURSOR used together",
      "role": "limits",
      "text": "While you wouldn't ordinarily use `LIMIT` and `WITHCURSOR` together in the same query, you can do so if necessary.\n`LIMIT`, as the name suggests, will limit the total number of results returned for the given query. `WITHCURSOR` will paginate the results in chunks of size `COUNT`. You can use the [cursor API](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations#cursor-api) to retrieve more results, as shown below.\n\n[code example]\n\nSee the following resources for more information:\n\n- [Aggregations](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/aggregations) discussion page.\n- [`FT.AGGREGATE` command page](https://redis.io/docs/latest/commands/ft.aggregate/)\n- [Redis Search source code](https://github.com/RediSearch/RediSearch/tree/master/src/aggregate)"
    }
  ],
  "examples": [
    {
      "id": "when-to-use--ex0",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 1 @price\n  APPLY \"@price * 1.1\" AS adjusted_price\n  SORTBY 2 @adjusted_price DESC\n  LIMIT 0 10\n\n 1) (integer) 200\n 2) 1) \"price\"\n    2) \"623\"\n    3) \"adjusted_price\"\n    4) \"685.3\"\n 3) 1) \"price\"\n    2) \"619.75\"\n    3) \"adjusted_price\"\n    4) \"681.725\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "when-to-use--ex1",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 1 @rating\n  FILTER \"@rating >= 4.5\"\n  LIMIT 0 10\n\n 1) (integer) 5\n 2) 1) \"rating\"\n    2) \"4.5\"\n 3) 1) \"rating\"\n    2) \"4.8\"\n 4) 1) \"rating\"\n    2) \"4.5\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "when-to-use--ex2",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE SUM 1 @price AS total_price\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Toys\"\n   3) \"total_price\"\n   4) \"9799.25\"\n3) 1) \"category\"\n   2) \"Electronics\"\n   3) \"total_price\"\n   4) \"10683.5\"\n4) 1) \"category\"\n   2) \"Apparel\"\n   3) \"total_price\"\n   4) \"10273.5\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "when-to-use--ex3",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE SUM 1 @price AS total_price\n  APPLY \"@total_price * 1.2\" AS boosted_price\n  FILTER \"@total_price > 1000\"\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Toys\"\n   3) \"total_price\"\n   4) \"9799.25\"\n   5) \"boosted_price\"\n   6) \"11759.1\"\n3) 1) \"category\"\n   2) \"Electronics\"\n   3) \"total_price\"\n   4) \"10683.5\"\n   5) \"boosted_price\"\n   6) \"12820.2\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "when-to-use--ex4",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 2 @price @discount\n  APPLY \"@price - @discount\" AS net_price\n  APPLY \"@net_price * 1.1\" AS marked_up\n  FILTER \"@net_price > 200\"\n  LIMIT 0 10\n\n1) (integer) 60\n2) 1) \"price\"\n   2) \"220\"\n   3) \"discount\"\n   4) \"0\"\n   5) \"net_price\"\n   6) \"220\"\n   7) \"marked_up\"\n   8) \"242\"\n3) 1) \"price\"\n   2) \"223.25\"\n   3) \"discount\"\n   4) \"1.5\"\n   5) \"net_price\"\n   6) \"221.75\"\n   7) \"marked_up\"\n   8) \"243.925\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "when-to-use--ex5",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 2 @price @discount\n  APPLY \"@price - @discount\" AS net_price\n  SORTBY 2 @net_price DESC\n  LIMIT 0 10\n\n 1) (integer) 200\n 2) 1) \"price\"\n    2) \"623\"\n    3) \"discount\"\n    4) \"6\"\n    5) \"net_price\"\n    6) \"617\"\n 3) 1) \"price\"\n    2) \"619.75\"\n    3) \"discount\"\n    4) \"4.5\"\n    5) \"net_price\"\n    6) \"615.25\"\n  .\n  .\n  .",
      "section_id": "when-to-use-"
    },
    {
      "id": "groupby-with-multiple-reduce-operations-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE COUNT 0 AS product_count\n  REDUCE SUM 1 @price AS total_price\n  REDUCE AVG 1 @rating AS avg_rating\n  SORTBY 2 @total_price DESC\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Groceries\"\n   3) \"product_count\"\n   4) \"44\"\n   5) \"total_price\"\n   6) \"13495.25\"\n   7) \"avg_rating\"\n   8) \"3.94090909091\"\n3) 1) \"category\"\n   2) \"Home\"\n   3) \"product_count\"\n   4) \"40\"\n   5) \"total_price\"\n   6) \"11026.75\"\n   7) \"avg_rating\"\n   8) \"3.78\"\n  .\n  .\n  .",
      "section_id": "groupby-with-multiple-reduce-operations"
    },
    {
      "id": "multiple-apply-operations-followed-by-groupby-and-reduce-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 3 @price @discount @quantity\n  APPLY \"@price - @discount\" AS final_price\n  APPLY \"@final_price * @quantity\" AS total_revenue\n  GROUPBY 1 @category\n  REDUCE SUM 1 @total_revenue AS total_category_revenue\n  SORTBY 2 @total_category_revenue DESC\n  LIMIT 0 10\n\n1) (integer) 6\n2) 1) \"category\"\n   2) \"Groceries\"\n   3) \"total_category_revenue\"\n   4) \"81373\"\n3) 1) \"category\"\n   2) \"Home\"\n   3) \"total_category_revenue\"\n   4) \"55797.5\"\n  .\n  .\n  .",
      "section_id": "multiple-apply-operations-followed-by-groupby-and-reduce"
    },
    {
      "id": "filter-and-params-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  LOAD 3 @price @rating @quantity\n  FILTER \"@price >= 500\"\n  FILTER \"@rating >= 4.0\"\n  APPLY \"@price * @quantity\" AS total_value\n  SORTBY 2 @total_value DESC\n  LIMIT 0 10\n  DIALECT 2\n\n1) (integer) 200\n 2) 1) \"price\"\n    2) \"606.75\"\n    3) \"rating\"\n    4) \"4.2\"\n    5) \"quantity\"\n    6) \"10\"\n    7) \"total_value\"\n    8) \"6067.5\"\n 3) 1) \"price\"\n    2) \"541.75\"\n    3) \"rating\"\n    4) \"4.5\"\n    5) \"quantity\"\n    6) \"10\"\n    7) \"total_value\"\n    8) \"5417.5\"\n  .\n  .\n  .",
      "section_id": "filter-and-params"
    },
    {
      "id": "limit-and-withcursor-used-together-ex0",
      "language": "sh",
      "code": "FT.AGGREGATE products \"*\"\n  GROUPBY 1 @category\n  REDUCE COUNT 0 AS product_count\n  LIMIT 0 100\n  WITHCURSOR COUNT 3\n\n1) 1) (integer) 6\n   2) 1) \"category\"\n      2) \"Toys\"\n      3) \"product_count\"\n      4) \"28\"\n   3) 1) \"category\"\n      2) \"Electronics\"\n      3) \"product_count\"\n      4) \"31\"\n   4) 1) \"category\"\n      2) \"Apparel\"\n      3) \"product_count\"\n      4) \"36\"\n2) (integer) 89400486\n127.0.0.1:6379> FT.CURSOR READ products 89400486 COUNT 3\n1) 1) (integer) 0\n   2) 1) \"category\"\n      2) \"Home\"\n      3) \"product_count\"\n      4) \"40\"\n   3) 1) \"category\"\n      2) \"Groceries\"\n      3) \"product_count\"\n      4) \"44\"\n   4) 1) \"category\"\n      2) \"Books\"\n      3) \"product_count\"\n      4) \"21\"\n2) (integer) 89400486\n  .\n  .\n  .",
      "section_id": "limit-and-withcursor-used-together"
    }
  ]
}
