)]}'
{
  "commit": "d927755461c99e240c70cdba9c3d1930badcd96e",
  "tree": "5d8d36bd80a28e2b419e85ac6d09dd34013027aa",
  "parents": [
    "06584bf5dfced8b340b94a4a8f49ce5063190d2e"
  ],
  "author": {
    "name": "Baha Aiman",
    "email": "bahaaiman@google.com",
    "time": "Mon Apr 13 16:41:15 2026 -0700"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Mon Apr 13 16:41:15 2026 -0700"
  },
  "message": "fix(firestore): fixes for Query to Pipeline Conversion (#14422)\n\nThis PR updates the Firestore `toPipeline` logic to ensure that queries\nconverted into pipelines follow standard Firestore query semantics.\nPreviously, the pipeline translation was missing several critical\nbehaviors regarding filtering, sorting, and missing-field handling.\n\nAll the below gaps were identified by running the Java tests in\n[ITQueryToPipelineTest.java](https://github.com/googleapis/java-firestore/blob/9065134fd70da22250f038d7e964f26aebfeb3cf/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITQueryToPipelineTest.java)\nand comparing the execute request payload sent by Java with the ones\nsent by Go equivalent pipelines.\n\nHere are the key improvements:\n\n### 1. Correct Stage Ordering (Filters Before Sorts)\nWe now apply the `Where` filters before the `Sort` stage. Applying\nsorting before filtering is inefficient and diverges from the standard\nFirestore execution plan, which filters the dataset as early as\npossible.\n\n**Example:**\n```go\n// Before: collection -\u003e sort(\"name\") -\u003e where(\"age\" \u003d\u003d 25)\n// After:  collection -\u003e where(\"age\" \u003d\u003d 25) -\u003e sort(\"name\")\nquery :\u003d client.Collection(\"users\").Where(\"age\", \"\u003d\u003d\", 25).OrderBy(\"name\", firestore.Asc)\npipeline :\u003d query.toPipeline()\n```\n\n### 2. Enforcing \"Missing Field\" Semantics\nIn Firestore, a query for `foo \u003d\u003d 1` or an ordering on `foo` should\nimplicitly exclude any document where the field `foo` is missing. We now\nautomatically inject `FieldExists` checks into the pipeline to mirror\nthis behavior.\n\n**Example:**\n* **Filter:** `Where(\"foo\", \"\u003d\u003d\", 1)` $\\rightarrow$\n`And(FieldExists(\"foo\"), Equal(\"foo\", 1))`\n* **Order:** `OrderBy(\"bar\", Asc)` $\\rightarrow$\n`Where(FieldExists(\"bar\")) -\u003e Sort(\"bar\", Asc)`\n\n### 3. Support for Composite Filters (OR / Nested AND)\nThe previous implementation could only handle simple top-level field\nfilters. We\u0027ve added a recursive filter translator that allows the\n`toPipeline` method to handle complex nested filters, including `OR`\nqueries.\n\n**Example:**\n```go\n// This now correctly translates to a nested \u0027or\u0027 function in the pipeline \u0027where\u0027 stage\nfilter :\u003d firestore.OrFilter{\n    firestore.PropertyFilter{Path: \"status\", Op: \"\u003d\u003d\", Value: \"urgent\"},\n    firestore.PropertyFilter{Path: \"priority\", Op: \"\u003e\", Value: 10},\n}\nquery :\u003d client.Collection(\"tasks\").WhereEntity(filter)\n```\n\n### 4. Native `LimitToLast` Implementation\nSince the pipeline backend doesn\u0027t have a specific \"limit to last\"\nstage, we now implement this by:\n1. Reversing the sorting order.\n2. Applying a standard `Limit`.\n3. Re-sorting back to the original requested order.\n\n**Example:**\n```go\n// Fetch the 5 most recent items\n// Pipeline: Sort(timestamp DESC) -\u003e Limit(5) -\u003e Sort(timestamp ASC)\nquery :\u003d client.Collection(\"logs\").OrderBy(\"timestamp\", firestore.Asc).LimitToLast(5)\n```\n\n### 5. Correct Path Resolution for Subcollections\nWe fixed an issue where subcollection queries were passing the wrong\npath to the `collection` stage. It now correctly resolves the relative\npath from the database root.\n\n**Example:**\n```go\n// Previously, this might only pass \"comments\" to the pipeline.\n// Now it correctly passes \"posts/post_123/comments\".\nquery :\u003d client.Collection(\"posts\").Doc(\"post_123\").Collection(\"comments\")\n```\n\n### 6. Accurate Cursor Boundaries (StartAt / EndAt)\nWe\u0027ve refined how multi-field cursors are translated into pipeline\ninequalities.\n- **Reference Types:** When using a document as a cursor, we now\ncorrectly pass it as a `Reference` type instead of a string ID, ensuring\nthe backend can perform accurate comparisons.\n- **Strict Inequalities:** For multi-field sorts (e.g.,\n`OrderBy(\"A\").OrderBy(\"B\")`), we now use strict inequalities for\nintermediate fields to ensure the cursor correctly \"steps\" through the\nresults without including duplicates or skipping valid entries.",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "60c9eb54cb2b90a49b4865d24591c7f94ecf1e7e",
      "old_mode": 33188,
      "old_path": "firestore/query.go",
      "new_id": "dc41d2d41050995986d186969701981d66e4045a",
      "new_mode": 33188,
      "new_path": "firestore/query.go"
    },
    {
      "type": "modify",
      "old_id": "0fc38a0ccb5045d7fc530da0743cce60d6005104",
      "old_mode": 33188,
      "old_path": "firestore/query_test.go",
      "new_id": "40ac885119668d7de44d317c685baea201e5a544",
      "new_mode": 33188,
      "new_path": "firestore/query_test.go"
    }
  ]
}
