narcoticfresh
narcoticfresh

Reputation: 282

Doctrine 2 ODM: Querying ID field with MongoRegex with LIKE

I have a bigger project using Doctrine 2 ODM in a Symfony context.

Given a simple ODM entity as this (XML definition):

<document name="\Document\App" repository-class="\Repository\AppRepository">
  <field fieldName="id" type="string" id="true" strategy="UUID"/>
  <field fieldName="name" type="string"/>
</document>

I want to query for App documents using a MongoRegex expression on the _id field.

Now, I'm aware of the "string versus MongoId" problem - all our ID's are proper strings.

When I try to do this via MongoDB shell (using Robomongo as GUI); it's all fine, as this expression successfully returns the objects I'm searching for:

App.find({'_id': /^ad.*$/i})

But in the PHP context it's different. There is special logic in Doctrine 2 ODM that treats equals() search on identifier fields different from normal equals.

Querying a normal field Doctrine 2 ODM behavior

We have the normal field name in our App entity. If we want to make a like search on that, we do (assuming $builder is instanceof QueryBuilder)

$builder->field("name")->equals(new \MongoRegex("/^ad.*$/i"));

If we then check what QueryBuilder has done calling $builder->getQueryArray(), we see:

array (size=1)
 'name' => 
    object(MongoRegex)[628]
    public 'regex' => string '^ad.*$' (length=6)
    public 'flags' => string 'i' (length=1)

That's nice and it works.. We have our MongoRegex instance in there..

Querying _id field with MongoRegex Doctrine 2 ODM behavior

It's all different on an identifier field.

Let's do this:

$builder->field("id")->equals(new \MongoRegex("/^ad.*$/i"));

Now let's check $builder->getQueryArray() again:

array (size=1)
  '_id' => string '/^ad.*$/i' (length=9)

Hm, no MongoRegex instance in the query. And indeed, the query doesn't work..

Finding the cause

This question is not how does this happen. I know how - but not why.. Let's see the Doctrine 2 ODM code.

This conversion happens in Doctrine\ODM\MongoDB\Persisters\DocumentPersister (see code here).

Excerpt:

// Process identifier fields
    if (($class->hasField($fieldName) && $class->isIdentifier($fieldName)) || $fieldName === '_id') {
        $fieldName = '_id';
        if ( ! $prepareValue) {
            return array($fieldName, $value);
        }
        if ( ! is_array($value)) {
            return array($fieldName, $class->getDatabaseIdentifierValue($value));
        }

If the query value is not an array (what it isn't, it's a MongoRegex), it will converted to one and the value will be replaced with the return of getDatabaseIdentifierValue() of ClassMetaDataInfo (see call here) of the data type (in this case string) - which will then be a normal string instead of a MongoRegex instance.

My question

So we know how it happens but not why - what is the reason for this conversion in equals() operations on identifier fields? Is there any need to do that?

How can one query with a Regex on an identifier field using Doctrine 2 ODM? We know it works in the MongoDB shell, why wouldn't it here?

I posted this as a SO question first instead of a Doctrine 2 ODM issue as this is still a question. If nobody knows a reason why this is done, I'll try to raise a Github issue for the ODM maintainers.

Upvotes: 3

Views: 1404

Answers (1)

narcoticfresh
narcoticfresh

Reputation: 282

After creating this I created a Github issue explaining the problem and it has been marked as a bug. So it is indeed a buggy behavior..

Upvotes: 1

Related Questions