Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to implement $$ROOT #41

Closed
minicuper opened this issue Jan 25, 2017 · 12 comments
Closed

How to implement $$ROOT #41

minicuper opened this issue Jan 25, 2017 · 12 comments

Comments

@minicuper
Copy link

minicuper commented Jan 25, 2017

How one could implement $$ROOT variable as in here https://docs.mongodb.com/manual/reference/operator/aggregation/group/#group-documents-by-author?

It's in mongo since 2.6.

Do 'custom operators' fit for this?

I tried to implement it this way, but it doesn't work.

Mingo.addOperators(Mingo.OP_PIPELINE, function (m) {
  return {
    $$ROOT: function (obj) {
      return obj;
    }
  }
})

It throws the error: "Invalid operator name '$$ROOT'"

@minicuper
Copy link
Author

minicuper commented Jan 25, 2017

It looks like the easiest way to add the feature is to add the code into the very beginning of the resolve function:

    if (selector === '$ROOT') {
      return obj;
    }

But it's not generic enough. What do you think?

@kofrasa
Copy link
Owner

kofrasa commented Jan 25, 2017

From the MongoDB documentation $$ROOT is not an operator but rather a system variable.
I deliberately skipped system variables because most of them require the $redact operator which has not been implemented.

I think it is time to revisit implementing $redact and also design a streamlined way to add system variables.

Suggestions and PRs are welcome.

@minicuper
Copy link
Author

Thanks for your answer. Let me take a look what the $redact is.

@minicuper
Copy link
Author

$redact uses the aggregation expressions, so all of the $$ variables we should add to the part of engine.

I like the way you implemented 'custom operators', so I think the API should be something like this:

Mingo.addAggregationVariables(function (m) {
  return {
    $$ROOT: function(doc , stageRootExpression){
     // we can here triverse all the subdocs and array in case of $$DESCEND
     // using the stageRootExpression (this should be $cond in case of $reduct)
      return doc; // either obj or undefined if we don't want to include the doc into the output
    }    
  }
})

@kofrasa
Copy link
Owner

kofrasa commented Feb 3, 2017

I have implemented $redact on the development branch. Looking good so far and should be out in the next version. With regards to the system variables they can be handled directly within the value resolution functions. I don't think it is a good idea to allow defining new ones at present.

After careful consideration, I still found the ones available to $redact more useful though $$ROOT can be implemented quite easily. $$CURRENT can also be handled but will require a way to rebind the variable as per the documentation.

@zag2art Out of curiousity, what is your use case for requiring $$ROOT?

@minicuper
Copy link
Author

Looks cool, thanks. I'l take a look more carefully tomorrow.
My use case is absolutely similar the example in the above link:

Data set:

{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }

Aggregation:

db.books.aggregate(
   [
     { $group : { _id : "$author", books: { $push: "$$ROOT" } } }
   ]
)

Result:

{
  "_id" : "Homer",
  "books" :
     [
       { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
       { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
     ]
}

{
  "_id" : "Dante",
  "books" :
     [
       { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
       { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
       { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }
     ]
}

So, I use $$ROOT in the $group stage, not in the $reduct.

@minicuper
Copy link
Author

It seems that $reduct expression can be resolved only to the three variables:

  • $$DESCEND
  • $$PRUNE
  • $$KEEP

The three are only for $reduct, and can not be used in other stages.

On the other hand, the other two ($$ROOT and $$CURRENT) have different nature. They are accessible in the most of the stages. Look at the above example.

So, I think, maybe it's better not to include the $reduct 'pseudo' variables into the SYS_VARIABLES but use them only in the $reduct stage.

@kofrasa
Copy link
Owner

kofrasa commented Feb 6, 2017

I concur. I found my preliminary implementation to a bit brittle and have removed the $reduct specific variables at the moment. I am working on making the a context available for the internal functions to avoid using globals.

@kofrasa kofrasa closed this as completed in 5a8f26e Feb 6, 2017
@minicuper
Copy link
Author

Cool, thanks. It works like a charm with $$ROOT.

By the way, I guess the code is wrong a little bit:

      var sysCurrent = "$$CURRENT";
      if (expr.indexOf(sysCurrent + ".") === 0) {
        return resolve(obj, expr.slice(sysCurrent.length + 2));
      }

It slices one extra character from the expression.

Also, It seems that $$ROOT should work in most cases like the $$CURRENT. So, one should be able to extract subfields from the $$ROOT the same way $$ROOT.field.

I think the $$CURRENT differs from the $$ROOT only for $reduct stage because of its recursive nature. At least I haven't found another such stages in docs.

@minicuper
Copy link
Author

minicuper commented Feb 7, 2017

Just tried it in mongo 3.4. $$ROOT works like $$CURRENT and it can take 'subpath' after itself.

So, this worked fine for me:
db.auths.aggregate([{$group: {_id: null, names: {$push: '$$ROOT.name'}}}])

@minicuper
Copy link
Author

I've added a few tests here - #46

@kofrasa kofrasa reopened this Feb 8, 2017
@kofrasa
Copy link
Owner

kofrasa commented Feb 8, 2017

Still some more work to do then. My implementation did not handle subpaths for $$ROOT.

I will make the update in the next minor release. Thanks for the tests.

@kofrasa kofrasa closed this as completed in 0cdff0c Feb 8, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants