Reputation: 282
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
.
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..
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..
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.
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
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