Symfony Return child objects depend of some property - symfony

Im using Symfony 4 + Api Platform. I have an entity (Insurance) that it is relationed with another entity (MediaObject). When I try to GET some item of Insurance, it is returning all the MediaObjects items that they are relationed with it. However, I dont want to return all the MediaObjects. I need MediaObjects where deletedAt is null.
How can I fix that?
I have a DoctrineExtension where I coded:
$queryBuilder->join(sprintf('%s.mediaObjects', $rootAlias), 'mo');
$queryBuilder->andWhere(sprintf('%s.deletedAt IS NULL', $rootAlias));
$queryBuilder->andWhere('mo.deletedAt IS NULL');
but nothing happens. It is still returning all items.
[
{
"id": 1,
"mediaObjects": [
{
"id": 5,
"mimetype": "image/jpeg",
"type": "INSURANCE",
"createdAt": "2018-07-20T09:36:05+02:00",
"updatedAt": "2018-07-20T09:36:05+02:00",
"deletedAt": "2018-07-20T14:20:34+02:00"
},
{
"id": 10,
"mimetype": "image/jpeg",
"type": "INSURANCE",
"createdAt": "2018-07-20T11:01:38+02:00",
"updatedAt": "2018-07-20T14:04:42+02:00",
"deletedAt": "2018-07-20T14:20:34+02:00"
},
{
"id": 4,
"mimetype": "image/jpeg",
"type": "INSURANCE",
"createdAt": "2018-07-20T09:35:56+02:00",
"updatedAt": "2018-07-20T14:04:00+02:00",
"deletedAt": "2018-07-20T14:15:09+02:00"
},
{
"id": 6,
"mimetype": "image/jpeg",
"type": "INSURANCE",
"createdAt": "2018-07-20T09:36:13+02:00",
"updatedAt": "2018-07-20T14:15:49+02:00",
"deletedAt": null
}
],
"expirationDate": "2018-07-19T08:25:29+02:00",
"policy": "Verduras",
"producerName": "Aseguradoras S.L.",
"contactEmail": "testsingfact+aseguradora1#gmail.com",
"contactPhone": "700000001",
"quantity": 1200,
"type": "CARGO",
"coverageExceptions": "Caidas",
"createdAt": "2018-07-19T10:27:22+02:00",
"updatedAt": "2018-07-19T13:06:31+02:00",
"deletedAt": null
}
]

You should use conditional join and apply filter to the joining collection not to the whole set:
$queryBuilder->join(sprintf('%s.mediaObjects', $rootAlias), 'mo', 'WITH', 'mo.deletedAt IS NULL');
$queryBuilder->andWhere(sprintf('%s.deletedAt IS NULL', $rootAlias));
Keep in mind this query will not return insurance entities that do not have non-deleted media objects because of join. If you want it to also return objects with empty media collection change join to leftJoin

Related

JsonPath: get root element depending on first level sub-element

I need all elements (including "id" and all attributes) of the json sample below where "type" has the value "state_machine_state", but only if "type" is on the first level. I don't want the middle element "order_transaction" in the result, although it has "type": "state_machine_state" in the second hierarchy level.
[
{
"id": "f45aa035eb424d26a0529d25b3647c32",
"type": "state_machine_state",
"attributes": {
"technicalName": "cancelled",
"name": "Cancelled",
"stateMachineId": "7553ad2630044fa589d786135d5000ad",
"customFields": null,
"createdAt": "2020-06-05T14:23:30.503+02:00",
"updatedAt": null,
"translated": {
"name": "Cancelled",
"customFields": []
},
"apiAlias": null
}
},
{
"id": "2d24ed4179824dbe92eee2f3d4f885b1",
"type": "order_transaction",
"relationships": {
"stateMachineState": {
"data": {
"type": "state_machine_state",
"id": "21f236e8955f45d3931fc9e44615088a"
}
}
},
"meta": null
},
{
"id": "d08e73da41b0473d83ea378a57a2fa1f",
"type": "state_machine_state",
"attributes": {
"technicalName": "completed",
"name": "Completed",
"stateMachineId": "7553ad2630044fa589d786135d5000ad",
"customFields": null,
"createdAt": "2020-06-05T14:23:30.482+02:00",
"updatedAt": null,
"translated": {
"name": "Completed",
"customFields": []
},
"apiAlias": null
},
"meta": null
}
]
With the query below I get all three elements
$..[?(#.type == 'state_machine_state')]
I just can't manage to select only the first level elements.
Could anyone please help?
Some implementations will allow you to select several items, but you're not going to be able to get the values with their keys.
For example, if you do
$..[?(#.type == 'state_machine_state')['id','attributes']
You'll just get a result set that contains all of the id values and all of the attributes values.
[
"f45aa035eb424d26a0529d25b3647c32",
{
"technicalName": "cancelled",
"name": "Cancelled",
"stateMachineId": "7553ad2630044fa589d786135d5000ad",
"customFields": null,
"createdAt": "2020-06-05T14:23:30.503+02:00",
"updatedAt": null,
"translated": {
"name": "Cancelled",
"customFields": []
},
"apiAlias": null
},
"2d24ed4179824dbe92eee2f3d4f885b1",
"d08e73da41b0473d83ea378a57a2fa1f",
{
"technicalName": "completed",
"name": "Completed",
"stateMachineId": "7553ad2630044fa589d786135d5000ad",
"customFields": null,
"createdAt": "2020-06-05T14:23:30.482+02:00",
"updatedAt": null,
"translated": {
"name": "Completed",
"customFields": []
},
"apiAlias": null
}
]
JSON Path isn't designed for transformation. JMES Path might be able to do what you're looking for, but I'm not as familiar with it.
Sometimes it helps to take a step back and think another way.
With this query I get exactly what I want:
$..[?(#.type == 'state_machine_state' && #.attributes)]
The elements I need have attributes, the others don't, so I just check for that in the filter.

GraphDb Indexing Policies

Consider the following json responses..
If you run the graph query g.V().hasLabel('customer'), the response is:
[
{
"id": "75b9bddc-4008-43d7-a24c-8b138735a36a",
"label": "customer",
"type": "vertex",
"properties": {
"partitionKey": [
{
"id": "75b9bddc-4008-43d7-a24c-8b138735a36a|partitionKey",
"value": 1
}
]
}
}
]
If you run the sql query select * from c where c.label = 'customer', the response is:
[
{
"label": "customer",
"partitionKey": 1,
"id": "75b9bddc-4008-43d7-a24c-8b138735a36a",
"_rid": "0osWAOso6VYBAAAAAAAAAA==",
"_self": "dbs/0osWAA==/colls/0osWAOso6VY=/docs/0osWAOso6VYBAAAAAAAAAA==/",
"_etag": "\"2400985f-0000-0c00-0000-5e2066190000\"",
"_attachments": "attachments/",
"_ts": 1579181593
}
]
Q: With this difference in structure around the partitionKey section, should this be referenced as /properties/partitionKey/*, or /partitionKey/? in the indexing policy?
Currently i have hedged by bets with...
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [{
"path": "/properties/partitionKey/*"
},{
"path": "/partitionKey/?"
},{
"path": "/label/?"
}
],
"excludedPaths": [{
"path": "/*"
},{
"path": "/\"_etag\"/?"
}
]
}
TIA! 😁
This should be stored as "/partitionKey" in your index policy, not "/properties/partitionKey"
btw, another thing to point out here is it's generally better to only exclude paths you will never query on rather than have to include those you will. This way, if you add properties to your graph you won't have to rebuild the index to query on the new properties.

How can I select a filtered child document collection when querying a top level document in cosmos db

I'm trying to filter the child documents returned when querying a parent document using the sql api in cosmos db.
For example given this document:
{
"customerName": "Wallace",
"customerReference": 666777,
"orders": [
{
"date": "20181105T00:00:00",
"amount": 118.84,
"description": "Laptop Battery"
},
{
"date": "20181105T00:00:00",
"amount": 81.27,
"description": "Toner"
},
{
"date": "20181105T00:00:00",
"amount": 55.12,
"description": "Business Cards"
},
{
"date": "20181105T00:00:00",
"amount": 281.00,
"description": "Espresso Machine"
}]
}
I would like to query the customer to retrieve the name, reference and orders over 100.00 to produce a results like this
[{
"customerName": "Wallace",
"customerReference": 666777,
"orders": [
{
"date": "20181105T00:00:00",
"amount": 118.84,
"description": "Laptop Battery"
},
{
"date": "20181105T00:00:00",
"amount": 281.00,
"description": "Espresso Machine"
}]
}]
the query I have so far is as follows
SELECT c.customerName, c.customerReference, c.orders
from c
where c.customerReference = 666777
and c.orders.amount > 100
this returns an empty set
[]
and if you remove "and c.orders.amount > 100" it matches the document and returns all orders.
To reproduce this issue I simply set up a new database, added a new collection and copied the json example in as the only document. The index policy is left as the default which I've copied below.
{
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*",
"indexes": [
{
"kind": "Range",
"dataType": "Number",
"precision": -1
},
{
"kind": "Range",
"dataType": "String",
"precision": -1
},
{
"kind": "Spatial",
"dataType": "Point"
}
]
}
],
"excludedPaths": []
}
Cosmos DB doesn't support the deep filtering in the way I attempted in my original query.
To achieve the results described you need to use a subquery using a combination of ARRAY and VALUE as follows:
SELECT
c.customerName,
c.customerReference,
ARRAY(SELECT Value ord from ord in c.orders WHERE ord.amount > 100) orders
from c
where c.customerReference = 666777
note the use of 'ord' - 'order' is a reserved word.
The query then produces the correct result - eg
[{
"customerName": "Wallace",
"customerReference": 666777,
"orders": [
{
"date": "20181105T00:00:00",
"amount": 118.84,
"description": "Laptop Battery"
},
{
"date": "20181105T00:00:00",
"amount": 281.00,
"description": "Espresso Machine"
}
]
}]

Hydrate joined Entities with Api-Platform

I m building my first rest API with Api-Platform for an AngularJs app,
I want to get all my Project entity like juste here (with my url /projects):
{
"#context": "/contexts/Project",
"#id": "/projects",
"#type": "hydra:PagedCollection",
"hydra:totalItems": 19,
"hydra:itemsPerPage": 30,
"hydra:firstPage": "/projects",
"hydra:lastPage": "/projects",
"hydra:member": [
{
"#id": "/projects/1",
"#type": "Project",
"name": "test1",
"parent": null,
"createdAt": "2014-12-22T11:38:13+01:00",
"updatedAt": null,
"deletedAt": null
},
{
"#id": "/projects/2",
"#type": "Project",
"name": "test2",
"parent": null,
"createdAt": "2014-12-22T17:02:50+01:00",
"updatedAt": null,
"deletedAt": null
},
{
"#id": "/projects/3",
"#type": "Project",
"name": "test3",
"parent": "/projects/2",
"createdAt": "2014-12-22T18:28:50+01:00",
"updatedAt": null,
"deletedAt": null
}
]
}
But as you can see my Projects can have parent, so I got a reference of my parent Project ( like this /projects/2 )
Can I get directly a Project Object in Json instead of reference like this ?
{
"#id": "/projects/3",
"#type": "Project",
"name": "test3",
"parent": {
"#id": "/projects/2",
"#type": "Project",
"name": "test2",
"parent": null,
"createdAt": "2014-12-22T17:02:50+01:00",
"updatedAt": null,
"deletedAt": null
},
"createdAt": "2014-12-22T18:28:50+01:00",
"updatedAt": null,
"deletedAt": null
}
This is a good pratical of Rest APi ?
API Platform has a built-in function for embedding relations in the parent JSON document.
Your entity will look like:
namespace AppBundle\Entity;
use Symfony\Component\Serializer\Annotation\Groups;
class Project
{
private $id;
/** #Groups({"embed"}) */
private $parent;
/** #Groups({"embed"}) */
private $name;
/** #Groups({"embed"}) */
private $createdAt;
// ...
}
And the service definition:
# app/config/services.yml
services:
# ...
resource.offer:
parent: api.resource
arguments: [ 'AppBundle\Entity\Offer' ]
calls:
- method: initNormalizationContext
arguments: [ { groups: [ embed ] } ]
tags: [ { name: api.resource } ]
Be careful, it will embed the parent as well as the parent of the parent and so on. If you want to change this, you need to create a custom normalizer. It will be more straightforward when Symfony 3.1 will be released thanks to the new #MaxDepth annotation.

Controlling output on MQL query -- search by a field, but not output it

I am curious if I can control the results of a query so that it will not display the a particular field in the query. For instance...
query:
[{
"id": null,
"name": null,
"type": "/people/person"
}]
result:
{
"result": [
{
"type": "/people/person",
"id": "/en/jack_abramoff",
"name": "Jack Abramoff"
},
{
"type": "/people/person",
"id": "/en/bob_ney",
"name": "Bob Ney"
},...
I have tried this...
[{
"id": null,
"name": null,
"type": [{
"id": "/people/person",
"limit": 0
}]
}]
Which gives me ...
{
"result": [
{
"type": [],
"id": "/en/jack_abramoff",
"name": "Jack Abramoff"
},
{
"type": [],
"id": "/en/bob_ney",
"name": "Bob Ney"
},...
I am wondering if there is a way to just get this
{
"result": [
{
"id": "/en/jack_abramoff",
"name": "Jack Abramoff"
},
{
"id": "/en/bob_ney",
"name": "Bob Ney"
},...
No, there is no way to do this. Why would you need to do this? Once you parse the JSON data in your application its very easy to just ignore any values that you don't need. The APIs support using gzip compression so you don't have to worry about the response size either. If you're really optimizing for speed you might consider switching to the Search API which looks like this:
https://www.googleapis.com/freebase/v1/search?filter=(all+type:/people/person)

Resources