doctrine / mongodb-odm

The Official PHP MongoDB ORM/ODM
https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/
MIT License
1.09k stars 504 forks source link

Selective Hydration #1020

Closed renatomefi closed 8 years ago

renatomefi commented 9 years ago

Hello,

I'm using mongodb within an api, when I'm using hydrating to get a value this is my response:

{
    "id": "54c8ca42eabc88400d8b4568",
    "language": {
        "id": "54b7bf0654064d33048b456d",
        "last_update": {
            "sec": 1421328134,
            "inc": 2
        },
        "key": "pt-br",
        "translations": {
           ...
           ...
           ...
           ...
           ...
           ...
           "Lots of infos"
        }
    },
    "key": "pt-br-key-test-4",
    "value": "pt-br-value-test-4"
}

When the hydrate is false I get rid of the lot of infos, but my id is now "protected" as _id. Is there a way to hydrate only some references? And why id is a reference? Is it a mongodb way of work?

{
    "_id": {
        "$id": "54c8ca42eabc88400d8b4568"
    },
    "key": "pt-br-key-test-4",
    "language": {
        "$ref": "Language",
        "$id": {
            "$id": "54b7bf0654064d33048b456d"
        },
        "$db": "angular_symfony"
    },
    "value": "pt-br-value-test-4"
}

Another thing that I was thinking about, is there a way to choose the depth of hydration? The main reason that I'm not hydrating this query is because of crossed references bringing more data that I want, if I was able to choose only 1 level of depth this would be great!

Thank you

renatomefi commented 9 years ago

Is there any other solution to achieve the same result?

malarzm commented 9 years ago

When the hydrate is false I get rid of the lot of infos, but my id is now "protected" as _id.

This is how field is named in db and since hydration is disabled you are getting data in a way it is in database (but it is kind of protected since its value can't be changed in db

Is there a way to hydrate only some references?

Not sure if that's what you meant, but you can select only certain fields to be fetched using $qb->select()

And why id is a reference?

It is not, if you take a look in the database you will see that _id has value of ObjectId("xxxxxxx") and

    "_id": {
        "$id": "54c8ca42eabc88400d8b4568"
    }

is just a way it's serialized from there.

Another thing that I was thinking about, is there a way to choose the depth of hydration?

I'm afraid there is no such functionality in ODM

Is there any other solution to achieve the same result?

I think that the best is selective query and manual preparation for serialization during which you will get rid of unwanted fields. If you are not using it already take a look at https://github.com/schmittjoh/serializer (and https://github.com/schmittjoh/JMSSerializerBundle if you are using Symfony)

renatomefi commented 9 years ago

Hello @malarzm, thanks for the answer!

I was trying some solutions but I had no success, look at my query:

        $result = $dm
            ->createQueryBuilder('FormBundle:Protocol')
            ->hydrate(true)
            ->select('id', 'createdAt')
            ->field('id')->equals($protocol->getId())
            ->getQuery()
            ->getSingleResult();

Debugging the query:

db.Protocol.find({ "_id": ObjectId("55427c1578a3245b048b456e") }, { "_id": 1, "createdAt": 1 }).limit(1).limit();

It should return this:

{
  "id":"554278e078a3245b048b456a",
  "created_at": 2015-04-30T18:48:00.025Z"
}

But since I used the hydrate it returned everything:

{
    "comment": [],
    "created_at": {
        "sec": 1430420501,
        "usec": 538000
    },
    "field_values": [],
    "file": [],
    "form": {
        "created_at": "2015-04-30T11:59:44-0300",
        "fields": [
            {
                "created_at": "2015-04-30T11:59:45-0300",
                "id": "5542436178a3249e0b8b45b3",
                "name": "1_1",
                "options": {
                    "estadual": "Estadual",
                    "federal": "Federal"
                }
            },
            ......... many, many items
            {
                "created_at": "2015-04-30T11:59:45-0300",
                "id": "5542436178a3249e0b8b4803",
                "name": "23_10"
            }
        ],
        "id": "5542436078a3249e0b8b459a",
        "name": "inspecao-estabelecimentos-penais",
        "pages": [
            {
                "created_at": "2015-04-30T11:59:44-0300",
                "id": "5542436078a3249e0b8b459b",
                "number": 1,
                "title": "Estrutura Organizacional"
            },
            ......... 22 items
            {
                "created_at": "2015-04-30T11:59:44-0300",
                "id": "5542436078a3249e0b8b45b2",
                "number": 24,
                "title": "Valora\u00e7\u00e3o sobre os itens inspecionados"
            }
        ],
        "template": "inspecao-estabelecimentos-penais"
    },
    "id": "55427c1578a3245b048b456e",
    "non_user": [],
    "user": []

Is is the right behavior? When the hydrate is false it will respect the select fields.

Thank you again

malarzm commented 9 years ago

@renatomefidf was the queried object fetched before the selective query?

lalop commented 9 years ago

Hi, Not sure but I think you can define a new entity querying the same Collection and hydrating only the field you need for your api.

renatomefi commented 9 years ago

@malarzm I believe not, how can I check it?

renatomefi commented 9 years ago

@lalop I have no idea how to do this, can you guide me?

lalop commented 9 years ago

@renatomefidf http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/basic-mapping.html#persistent-classes You can define 2 entity with the same collection but differents fields

<?php

namespace Documents;

/** @Document(db="my_db", collection="users") */
class User
{
}
<?php

namespace Documents;

/** @Document(db="my_db", collection="users") */
class ApiUser
{
}

In the ApiUser class you can define only the field that you want for api

malarzm commented 9 years ago

@renatomefidf if you have queried for the object before, UnitOfWork will return already fetched (hydrated) object even when using Query and QueryBuilder alone unless refresh() was used

malarzm commented 8 years ago

Closing since the question has been answered and no further update from @renatomefidf was received