• Type: Bug
    • Resolution: Unresolved
    • Priority: Critical - P2
    • None
    • Affects Version/s: None
    • Component/s: None
    • Query Integration
    • ALL
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Filed by the AI query bug hunter (qhunt), an agent that reviews query code for corner-case bugs. A differential oracle and the repro below confirmed the bug.

      Problem

      A view whose definition contains $unionWith on a second view returns the correct documents when it is read directly or through a top-level $unionWith. When the same view is the from of a $lookup, the second view's pipeline is not applied, so documents that the second view filters out are returned. Both $lookup forms (localField/foreignField and pipeline) are affected. The query returns no error, only extra rows. Default settings; no extensions.

      Repro

      Fails at master ba06f31d06c (2026-10-05). Self-contained jstests/noPassthrough-style test:

      /**
       * F003: a $lookup with a pipeline over a view whose definition contains $unionWith on
       * another view does not apply the inner view's pipeline, so documents the inner view
       * filters out are returned.
       *
       * @tags: [
       *   requires_fcv_90,
       * ]
       */
      
      const conn = MongoRunner.runMongod({setParameter: {internalQueryDisablePlanCache: true}});
      const db = conn.getDB(jsTestName());
      
      assert.commandWorked(db.local.insert({_id: 1, k: 1}));
      assert.commandWorked(db.foreign.insertMany([{_id: 1, x: 1, k: 1}, {_id: 2, x: 2, k: 1}]));
      assert.commandWorked(db.other.insertMany([{_id: 10, x: 1, k: 1}, {_id: 11, x: 2, k: 1}]));
      // innerV keeps only x: 1, so _id 11 must never appear through it.
      assert.commandWorked(db.createView("innerV", "other", [{$match: {x: 1}}]));
      assert.commandWorked(db.createView("outerV", "foreign", [{$unionWith: {coll: "innerV", pipeline: [{$set: {tag: "in"}}]}}]));
      
      const expectedIds = [1, 2, 10];
      function ids(docs) {
          return docs.map((d) => d._id).sort((a, b) => a - b);
      }
      function run(label, fn) {
          const got = fn();
          jsTest.log.info("F003 run", {label, ids: got});
          return got;
      }
      
      const direct = run("aggregate on outerV", () => ids(db.outerV.aggregate([]).toArray()));
      run("top-level $unionWith outerV", () =>
          ids(db.local.aggregate([{$match: {_id: -1}}, {$unionWith: {coll: "outerV", pipeline: []}}]).toArray()));
      run("$lookup localField/foreignField on outerV", () =>
          ids(db.local.aggregate([{$lookup: {from: "outerV", localField: "k", foreignField: "k", as: "j"}}]).toArray()[0].j));
      const lookupPipeline = run("$lookup pipeline on outerV", () =>
          ids(db.local.aggregate([{$lookup: {from: "outerV", pipeline: [{$sort: {_id: 1}}], as: "j"}}]).toArray()[0].j));
      
      // Control: reading the view directly applies both view pipelines.
      assert.eq(direct, expectedIds, "aggregate on outerV");
      // Fails at ba06f31d06c: returns [1, 2, 10, 11]; innerV's $match is not applied.
      assert.eq(lookupPipeline, expectedIds, "$lookup pipeline on outerV");
      
      MongoRunner.stopMongod(conn);
      

      Results of the repro (innerV keeps only x: 1, so _id: 11 must never appear):

      Access to outerV _id values returned
      aggregate on outerV 1, 2, 10
      top-level $unionWith of outerV 1, 2, 10
      $lookup localField/foreignField 1, 2, 10, 11
      $lookup with a pipeline 1, 2, 10, 11

      Possible cause (from the hunter's code reading, not verified)

      The execution-time view binder for the $lookup subpipeline resolves nested levels with bindOnly=true, so the inner $unionWith is marked as targeting innerV without prepending innerV's pipeline. SERVER-131409 fixed nested view binding for the extension $search path; this looks like the same gap on the plain $unionWith path.

      Impact

      Silent wrong results for any $lookup whose from is a view built with $unionWith over another view.

      Related

      • SERVER-131409: nested $unionWith/$lookup on views dropped results for extension $search.
      • SERVER-129249: view filter dropped for $rankFusion (desugared $unionWith) in a localField/foreignField $lookup on a view. Different shape; its fix is in ba06f31d06c.

            Assignee:
            Unassigned
            Reporter:
            Zixuan Zhuang
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: