Uploaded image for project: 'MongoDB Shell'
  1. MongoDB Shell
  2. MONGOSH-651

Productize new async rewriter

    • Type: Icon: Task Task
    • Resolution: Fixed
    • Priority: Icon: Major - P3 Major - P3
    • 0.12.0
    • Affects Version/s: None
    • Component/s: AsyncWriter
    • None
    • Iteration Z
    • Needed
    • Hide

      We have made some internal changes to how mongosh processes the JavaScript code that it receives as input. (This affects the built-in shell in Compass and VSCode playground as well, if it matters).

      I don’t think we have previously documented the limitations that we were imposing on users. Luckily, there’s only few left, and it’s not going to be a common occurrence for people to encounter them, but we should document them:

      The results of database calls, directly or indirectly through other functions, cannot be used in three contexts, namely, inside class constructor functions, inside non-async generator functions, and inside callbacks to array.sort(). As a workaround, wrapping the access to db results in async functions or using async generator functions works, respectively.

      Concretely, this means:

      // For constructors:
      // ------------------------
      // This will not work
      class SomeClass {
        constructor() {
          this.value = db.coll.find();
        }
      }
      
      // Neither will this
      function listEntries() { return db.coll.find(); }
      class SomeClass {
        constructor() {
          this.value = listEntries();
        }
      }
      
      // But this can be used instead, if it also fits the purpose here:
      class SomeClass {
        constructor() {
          this.value = (async() => {
            return db.coll.find();
          })();
        }
      }
      
      // For generator functions:
      // ------------------------
      // This will not work
      function* someGenerator() {
        yield db.coll.find();
      }
      
      // Neither will this
      function listEntries() { return db.coll.find(); }
      function* someGenerator() {
        yield listEntries();
      }
      
      // But this can be used instead, if it also fits the purpose here:
      function listEntries() { return db.coll.find(); }
      async function* someGenerator() {
        yield listEntries();
      }
      
      // For array.sort:
      // ------------------------
      // This will not work
      db.getCollectionNames().sort((coll1, coll2) => {
        return db[coll1].estimatedDocumentCount() - db[coll2].estimatedDocumentCount())
      });
      
      // But this approach can be used instead (and is often more performant):
      db.getCollectionNames().map(collName => {
        return { collName, size: db[collName].estimatedDocumentCount() };
      }).sort((coll1, coll2) => {
        return coll1.size - coll2.size;
      }).map(coll => coll.collName);
      
      Show
      We have made some internal changes to how mongosh processes the JavaScript code that it receives as input. (This affects the built-in shell in Compass and VSCode playground as well, if it matters). I don’t think we have previously documented the limitations that we were imposing on users. Luckily, there’s only few left, and it’s not going to be a common occurrence for people to encounter them, but we should document them: The results of database calls, directly or indirectly through other functions, cannot be used in three contexts, namely, inside class constructor functions, inside non-async generator functions, and inside callbacks to array.sort() . As a workaround, wrapping the access to db results in async functions or using async generator functions works, respectively. Concretely, this means: // For constructors: // ------------------------ // This will not work class SomeClass { constructor() { this .value = db.coll.find(); } } // Neither will this function listEntries() { return db.coll.find(); } class SomeClass { constructor() { this .value = listEntries(); } } // But this can be used instead, if it also fits the purpose here: class SomeClass { constructor() { this .value = (async() => { return db.coll.find(); })(); } } // For generator functions: // ------------------------ // This will not work function* someGenerator() { yield db.coll.find(); } // Neither will this function listEntries() { return db.coll.find(); } function* someGenerator() { yield listEntries(); } // But this can be used instead, if it also fits the purpose here: function listEntries() { return db.coll.find(); } async function* someGenerator() { yield listEntries(); } // For array.sort: // ------------------------ // This will not work db.getCollectionNames().sort((coll1, coll2) => { return db[coll1].estimatedDocumentCount() - db[coll2].estimatedDocumentCount()) }); // But this approach can be used instead (and is often more performant): db.getCollectionNames().map(collName => { return { collName, size: db[collName].estimatedDocumentCount() }; }).sort((coll1, coll2) => { return coll1.size - coll2.size; }).map(coll => coll.collName);

      A few things that we should improve:

      • Readable/useful error messages
      • Documentation of what is not supported (and workarounds if they exist)
      • Testing

      Once we feel confident that the new rewriter is ready, we can switch to that by default. Let's evaluate whether we want to keep the other one around for a bit just in case or if we want to get rid of it.

            Assignee:
            anna.henningsen@mongodb.com Anna Henningsen
            Reporter:
            massimiliano.marcon@mongodb.com Massimiliano Marcon
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: