-
-
Notifications
You must be signed in to change notification settings - Fork 502
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
DiscriminatorMap field values injected too often #250
Comments
Is the When working through a recent issue with discriminator fields, I spoke to @jwage about mapping a discriminator field as a PHP property in my document model (in order to have a getter for it). I believe he said it's purposely not hydrated and cannot be used as a property-mapped field. I'm not sure you can refer to the discriminator field in a query, though. From your example:
I believe it's only to be set after inferring the class name against the discriminator map. That could explain the odd behavior here. If it's not to be supported, perhaps ODM needs to throw an exception to be explicit. |
I'm not clear what your question regarding mapped fields means - there is no "map" except DiscriminatorMap in any of the classes related to the User document being used (User, Brand, Merchant). Granted, updating a discriminatory field is probably an edge case. The first query in the ticket is purely because the User object deliberately doesn't have a setter for this property (but I need to update it). However, it doesn't matter what field is in the update query, e.g. attempting to update name with the same syntax as above yields:
And in any event, criteria which contain an I did take a look at what changes would be required but it looks like a none trivial change to prevent this as and I don't want to waste your time providing a patch that is useless (since I'm not familiar with the doctrine code). |
By mapped field I meant something like this:
That was something I was playing around with two weeks ago, and it resulted in an exception because In your last example, it's clear that |
Hi, can you provide a test case for the expected behavior? and I will work on a fix. |
Possibly, though this is a good reason not to:
|
Any news/progress? |
Hi, I don't have any test case or anything to work from. Can you help provide something for us to work against? |
<?php
/**
* @MongoDB\Document(collection="articles", repositoryClass="Acme\AcmeBundle\Document\ArticleRepository")
* @MongoDB\DiscriminatorField(fieldName="type")
* @MongoDB\DiscriminatorMap({"article"="Article", "gallery"="\Acme\AcmeBundle\Document\Gallery"})
*/
class Article now I have in <?php
namespace Acme\AcmeBundle\Document;
use Doctrine\ODM\MongoDB\DocumentRepository;
/**
* ArticleRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ArticleRepository extends DocumentRepository
{
/**
* Find tag for ajax action
*
* @param string $value
* @return array
*/
public function findAjaxValue($value)
{
// simple validation
if (empty($value) || !is_string($value)) {
return array();
}
$value = (string) $value;
return $this->findBy(array(
'public' => true,
'$or' => array(
array('_id' => $value), // można wkleić ID
array('title' => array('$regex' => '^' . preg_quote($value) . '.*')),
)), array('guid' => true), 20);
}
} And as result I've got exception "$and/$or/$nor must be a nonempty array/500 Internal Server Error - MongoCursorException". The query: db.articles.find({
"public": true,
"$or": {
"0": { "_id": "p" },
"1": { "title": { "$regex": "^p.*" } },
"type": { "$in": [ "article" ] } // !!!!!????
},
"type": { "$in": [ "article" ] }
}).sort({ "guid": true }); I think that lines are ambiguous. You can't inject |
Can you make a phpunit test case so I can run it? |
I don't know if it's correct test case, but it works (I mean it doesn't ;) <?php
// (...)
use Documents\Functional\SameCollection1;
class PersistenceBuilderTest extends BaseTest
{
// (...)
public function testFindWithOrOnCollectionWithDiscriminatorMap()
{
$id = '4f28aa84acee41388900000a';
$ids = array($id);
/**
* @var \Doctrine\ODM\MongoDB\Query\Builder $qb
*/
$qb = $this->dm->createQueryBuilder('Documents\Functional\SameCollection1');
$qb
->addOr($qb->expr()->field('id')->in($ids))
->select('id')->hydrate(false);
/**
* @var \Doctrine\ODM\MongoDB\Query\Query $query
* @var \Doctrine\ODM\MongoDB\Cursor $results
*/
$query = $qb->getQuery();
$results = $query->execute();
$debug = $query->debug();
$this->assertEquals($id, (string) current($debug['$or'][0]['_id']['$in']));
$this->assertInstanceOf('Doctrine\MongoDB\Cursor', $results);
$this->assertEquals(0, $query->count());
}
// (...)
} |
Is it fixable? :) |
Most likely, I just haven't had a chance to look at it yet. I will try and make time to work on the odm tonight after work. |
Thanks in advance :) |
A quick update on what causes this. It looks like this part here gets run on every pass and adds in the discriminator values to all commands. if ($this->class->hasDiscriminator() && ! isset($query[$this->class->discriminatorField['name']])) {
$discriminatorValues = $this->getClassDiscriminatorValues($this->class);
$query[$this->class->discriminatorField['name']] = array('$in' => $discriminatorValues);
} I've created a quick hack which involves only running that section of code when the query is being generated for the final time. library/Doctrine/ODM/MongoDB/Query/Builder.php
And then look for that being true when preparing the query. I'll paste the full patch if anyone is interested, but I think this is a pretty hacky way to do it. I'm sure there's a better way of detecting when that discriminator code should be added. |
I think this would be better: diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
index 907d8df..a8aa512 100644
--- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
+++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
@@ -780,11 +780,13 @@ class DocumentPersister
if (is_scalar($query) || $query instanceof \MongoId) {
$query = array('_id' => $query);
}
- if ($this->class->hasDiscriminator() && ! isset($query[$this->class->discriminatorField['name']])) {
+ static $queryNestLevel = 0;
+ if (!$queryNestLevel && $this->class->hasDiscriminator() && ! isset($query[$this->class->discriminatorField['name']])) {
$discriminatorValues = $this->getClassDiscriminatorValues($this->class);
$query[$this->class->discriminatorField['name']] = array('$in' => $discriminatorValues);
}
$newQuery = array();
+ ++ $queryNestLevel;
if ($query) {
foreach ($query as $key => $value) {
if (isset($key[0]) && $key[0] === $this->cmd && is_array($value)) {
@@ -795,6 +797,7 @@ class DocumentPersister
}
$newQuery = $this->convertTypes($newQuery);
}
+ -- $queryNestLevel;
return $newQuery;
} It will add discriminator thing only once to the most top of the query (and don't inject in values). What do you think? It's also connected with #283 issue. |
@jwage we are waiting for your patch ;) |
I was hoping someone would send a pull request with the failing test since we use this awesome system with that feature :) I guess I will resort to copying and pasting the test and committing it up myself :( |
see the test commited in 03d3bd7, I have injected value even in $newObj object in update method, could you look? |
Any progress? :) |
I haven't had time to look yet. |
Using a document with the following annotations:
And code like so:
Executes a query like so (profiler output):
expected results would be a query ala:
The problem isn't restricted to updates, any query which does /not/ have the discriminatory field as a top-level array key will have it auto-injected generating invalid syntax, for example:
Will execute a query like so (profiler output):
expected query conditions would be:
The text was updated successfully, but these errors were encountered: