0
votes

I am currently trying to convert this JS code:

db.Issue.aggregate( [
  { // Filter out issues that have been worked on after our given week = All worklogs muste have been before date
      $match : { 
          worklogs: { $all: [
            { 
                "$elemMatch" : {
                    date: { $lte: endDate }
                }    
            },
          ] }
      }
  }
] )

(Based on official documentation: Use $all with $elemMatch

Into Doctrine ODM code using their aggregation builder:

$builder = $this->createAggregationBuilder();

$aggregation = $builder
    ->match()
        ->field('worklogs')
        ->all([
            $builder->matchExpr()->elemMatch(
               $builder->expr()->field('date')->lte($week->getEndDate())
               )
       ])
;

However, apparently I am unable to match the $all correctly - meaning in a way that allows me to ensure that all entries in a collection fulfilled the requirements checked by $elemMatch.

The original MongoDB query in JS seems to do the trick, but I cannot manage to receive the same results in PHP. By now I am not even sure Doctrine can handle that combination of $all and $elemMatch.

UPDATE:

Thank you all for your comments so far! I am however still struggling with the current query syntax (in PHP!) for the $not / ->not() operator. I have already checked the docs here: Doctrine ODM Docs but could not find anything useful. (Please also note, that I am using version 2.0 here).

Right now my query looks like:

->match()
    ->field('worklogs')
        ->not([
            $builder->matchExpr()->elemMatch(
                $builder->matchExpr()->field('date')->gt($week->getEndDate())
            )
        ])

But leads to this error:

1) App\Tests\Repository\IssueRepositoryTest::testGetEstimationsPerWeek MongoDB\Driver\Exception\CommandException: $not needs a regex or a document /var/www/html/vendor/mongodb/mongodb/src/Operation/Aggregate.php:263 /var/www/html/vendor/mongodb/mongodb/src/Collection.php:223 /var/www/html/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php:168 /var/www/html/vendor/doctrine/mongodb-odm/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php:35 /var/www/html/src/Repository/IssueRepository.php:85

1
Just a PHP Translation error. That's actually ->all([ [$builder->.... ] ]), noting the additional []. Just the singular [] ( as you have ) means {} in translation, So you want [ { ... } ] and therefore the additional [] or array() in older speak.Neil Lunn
You don't need neither $all nor $elemMatch in this particular case. {$match: {"worklogs.date": {$lte: endDate}}} does exactly the same.Alex Blex
@AlexBlex would that not also match results where only ONE entry in the collection fulfils the requirements ? Whereas I want a query that matches ALL elements ?Jan
You have only 1 $elemMatch in the array, so it is ALL of 1 element. I think you misunderstand what $all does. If you already read the page behind the link you posted in the question, scroll to the top and re-read again paying attention to each word. Also "collection" in mongodb is used to refer to a collection of documents - in your case "Issue" is a collection. "worklogs" is not a collection but an array of documents, subdocuments, or embedded documents. Please stick to terminology to avoid confusion.Alex Blex
Neither form actually returns where ALL elements in the array match the condition. If that's what you wanted you would in fact reverse the condition to $gt and use $not. i.e "worklogs": { "$not": { "$elemMatch": { "date": { "$gt": endDate } } } }. The $all example you point to in the documentation actually applies as "multiple conditions", and not "ALL array elements" as you have presumed.Neil Lunn

1 Answers

0
votes

So thanks to the help of @neil-lunn, I was able to figure out the correct query:

->match()
    ->field('worklogs')
        ->not(
            $builder->matchExpr()->elemMatch(
                $builder->matchExpr()->field('date')->gt($week->getEndDate())
            )
        )