Doctrine selecting from joins based on multiple conditions - php

I am trying to make a selection based on multiple ids in joins, each of the ids should match another condition of another join.
I need to get "Types" that have all the "Dichotomies" in "Position" 1 or 2. At the moment it gives me results that match one of the Dichotomies passed to the function, but not all of them.
$QB->select("Types","Types,ElementsPositions, Elements, Positions, Dichotomies, Quadras,TypesDescriptions,Relations")
->from($this->get_repository()[0], 'Types');
$QB->leftJoin("Types.ElementsPositions","ElementsPositions", \Doctrine\ORM\Query\Expr\Join::WITH, 'ElementsPositions.Positions = 1 OR ElementsPositions.Positions = 2');
$QB->leftJoin("ElementsPositions.Elements","Elements");
$QB->leftJoin("ElementsPositions.Positions","Positions");
$QB->leftJoin("Elements.Dichotomies","Dichotomies");
$QB->leftJoin("Types.Quadras","Quadras");
$QB->leftJoin("Types.TypesDescriptions","TypesDescriptions");
$QB->leftJoin("Types.Relations","Relations");
if(!empty($where['dichotomies'])){
foreach($where['dichotomies'] as $dichotomy){
$QB->andWhere('Dichotomies.id'.'=:dichotomy');
$QB->setParameter('dichotomy', $dichotomy['id']);
}
}
UPD.
Tables mapping - in JSON:
{
"table-name": "types",
"joins":[
{
"table-name":"elements_positions",
"type":"one-to-many"
},
{
"table-name":"quadras",
"type":"many-to-one"
},
{
"table-name":"types_descriptions",
"type":"one-to-one"
},
{
"table-name":"relations",
"type":"many-to-one"
}
]}
Elements Positions
{
"table-name": "elements_positions",
"joins":[
{
"table-name":"elements",
"type":"many-to-one"
},
{
"table-name":"positions",
"type":"many-to-one"
},
{
"table-name":"types",
"type":"many-to-one"
}
]
}
Elements
{
"table-name": "elements",
"joins":[
{
"table-name":"elements_positions",
"type":"one-to-many"
},
{
"table-name":"quadras",
"type":"many-to-many"
},
{
"table-name":"dichotomies",
"type":"many-to-many"
}
]
}
Positions
"table-name": "positions",
"joins":[
{
"table-name":"elements_positions",
"type":"one-to-many"
}
]
}
Dichotomies:
{
"table-name": "dichotomies",
"joins":[
{
"table-name":"elements",
"type":"many-to-many-inversed"
}
]
}

Your query has a two different problems.
First, multiple parameter values are bound with single parameter. Every next element of $where['dichotomies'] replaces previous value of parameter :dichotomy in the query. The method setParameters() don't really binds values to the prepared statement: it just stores them in QueryBuilder object. So, after the end of foreach-loop all conditions will be use the same value (the last of $where['dichotomies']). To avoid that you need to use different parameter names or numeric indexes.
Second, you add conditions that are contradictory: $QB->andWhere() will produce something like that:
Dichotomies.id = :dichotomy
AND Dichotomies.id = :dichotomy
AND Dichotomies.id = :dichotomy
...
One entity ID obviously cannot be equal to different values simultaneously. So, you need to replace AND by the OR operator.
But better way is to use IN clause:
Calling setParameter() automatically infers which type you are setting as value. This works for integers, arrays of strings/integers, DateTime instances and for managed entities.
Just replace the foreach-loop by the following lines:
$QB->andWhere('Dichotomies.id IN (:dichotomies)');
$QB->setParameters('dichotomies', array_column($where['dichotomies'], 'id'));
The array_column() function returns a list of IDs of all dichotomies. Doctrine generates IN expression and uses that list to generate query placeholders and bind the values.

Related

Grouping JSON object by an attribute and splitting result in arrays PHP | Laravel

I'm trying to build the following query:
Model::where('id',$id)->groupBy('type')->get();
I get the following error.
SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.table.id' which is
not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group
Below is the result without group by. I'm trying to group by type and get an array for each type with objects in the array.
{
'result':[
{
id:'1',
name:first,
type:animal
},
{
id:'2',
name:second,
type:animal
},
{
id:'3',
name:third,
type:human
}
]
}
What I'm trying to achieve:
{
[
{type:'animal',
data:[..first,second]
},
{
type:'human',
data:[...third],
}
]
}
Is there a way to achieve this with only groupBy? It doesn't have to be exact, but any way I can easily split items by type and with it as a key maybe?
You can have the Collection do the groupBy:
$items = Model:....->get();
$grouped = $items->groupBy('type');
Then you would have an element for each 'type' and inside that would be a Collection of each object matching that type.

How to find Intersection of Two Collection in monogdb?

Let say, I have 2 collection
first one :-
db.product_main
{
_id:123121,
source_id:"B4456dde1",
title:"test Sample",
price: 250
quantity: 40
}
which consist approx ~10000 objects (Array) and unique field is source_id.
Second :-
db.product_id
{
"_id":58745633,
"product_id":"B4456dde1"
}
which consist of ~500 and only have field "product_id" which is equals to "source_id" of db.product_main
now, i want to intersect two collection so that i only find those which don't exist in db.product_id.
db.product_main.aggregate({any query})
Just use the lookup stage to find the products associated with the 'product_main' collection and then match for empty array (i.e. records where no product_id was found)
db.product_main.aggregate([
{
$lookup: {
from: "product_id",
localField: "source_id",
foreignField: "product_id",
as: "products_available"
}
},
{
$match: {
products_available: {
$size: 0
}
}
}
])
On WRITE operations using aggregate pipeline You can also directly offload statistics update by using $out command and store cached result in product_stats collection (for example).
Later in web/ui/api READ operations just use this cached collection. Of cause, You can create database query methods for cached and non-cached results.

Find value with associative array without know the key in MongoDB

I have a document with this structure:
{"user":{
"nice":{
"funny":"sure"
}
,
"notnice":{
"funny":"maybe"
}
}
}
I know the keys "user","funny" and the value "sure" and "maybe" but I don't know "nice" and "notnice".
How do I do an optimized query to search through many documents.
For example, if I want to search "sure" value knowing the middle keys I do:
$document = $users->findOne([
'$or' => [
['user.nice.funny' => 'sure'],
['user.notnice.funny' => 'sure']
]
]
);
But how do I do the same without knowing "nice" and "notnice".
This should point you in the right direction:
db.collection.aggregate({
$addFields: {
"userTransformed": {
$objectToArray: "$user" // transform "user" field into key-value pair
}
}
}, {
$match: {
"userTransformed.v.funny": "sure" // just filter on the values
}
})
Frankly, this is not going to be fast for lots of documents but there is no other way. Indexes will not be used by this query. If you want to get faster you will need to change your document structure.

php json_decode not returning any results

i am trying to figure out why i am not returning any data when i use json_decode. i'm trying to get values that are in a valid json file (yes, i tested it). when i try and use:
foreach($json['designs']['filters'] as $filters) {
echo $filters['filterTypeName'];
}
i am returning the values ... that's great, but i am trying to get down to the next level of the json file to pull just the values for each array under "values". here is the json file results from file_get_contents('url'):
{"designs": {
"filters" :
[
{
"filterTypeName":"Year",
"filterProperty":"year",
"values":
[
{
"name":"2018",
"value":"2018",
"sortvalue":"1"
},
{
"name":"2017",
"value":"2017",
"sortvalue":"2"
}, (etc.)
]
},
{
"filterTypeName":"Division",
"filterProperty":"division",
"values":
[
{
"name":"Resort-Apparel",
"value":"Resort-Apparel",
"sortvalue":"1"
},
{
"name":"College-Apparel",
"value":"College-Apparel",
"sortvalue":"2"
}, (etc.)
]
}, (etc.)
essentially, i have a select options on my form that checking against the values listed above to return designs that match the criteria.
how do i specifically search for all of the years in one select group, then all of the divisions in another select group and so-on?
thanks for any and all of your help!!
Try using $json->designs-> filters for accessing filters instead of $json['designs']['filters']
As the others have said, json_decode returns an object unless true is specified as the second parameters, so your options are either:
Use $json->designs->filters
or use json_decode($json, true)
i used foreach($json['designs']['filters'][0]['values'] as $name) i forgot to specify the number of the array i was trying to access.

Remove item from array in a mongodb document using Doctrine ODM

I have a mongoDB document comprising the following structure:
{
"_id" : ObjectId("537b9731fa4634134c8b45aa"),
"kpis" : [
{
"id" : 4,
"value" : 3.78,
"entered" : Timestamp(1401377656, 9)
}
]
}
I want to remove ALL kpi documents where the id is x. This is quite simple to do on the database directly using the pull command:
db.lead.update({}, {$pull:{"kpis":{id:5}}}, {multi:true});
However my (several) attempts to match this syntax using the doctrine ODM have failed:
$qb->update()
->field('kpis')
->pull($qb->expr()->field('kpis.id')->equals($kpi->getId()))
->multiple(true)
->getQuery()
->execute();
// I've also tried..
$qb->update()
->field('kpis')
->pull($qb->expr()->field('kpis')->elemMatch(
$qb->expr()->field('kpis.id')->equals($kpi->getId())
))
->multiple(true)
->getQuery()
->execute();
Nothing is removed. Am I using the query builder correctly?
I believe you want to do the following:
$qb->update()
->field('kpis')->pull(array('id' => $kpi->getId()))
->multiple(true)
->getQuery()
->execute();
You can use the Query::debug() method to dump the actual query generated by this. You should expect the following in the dumped newObj field:
{ "$pull": { "kpis": { "id": <number> }}}
To explain why your previous examples didn't work:
->field('kpis')
->pull($qb->expr()->field('kpis.id')->equals($kpi->getId()))
Here, you're creating the following query:
{ "$pull": { "kpis": { "kpis.id": <number> }}}
This would only match:
If each embedded object in the top-level kpis array had an embedded kpis object within it, which in turn had an id field that matched the number.
If the embedded objects in the top-level kpis array had their own kpis arrays consisting of embedded objects with an id field that matched. This would be MongoDB's array element matching coming into play.
In your second example, you have:
->field('kpis')
->pull($qb->expr()->field('kpis')->elemMatch(
$qb->expr()->field('kpis.id')->equals($kpi->getId())
))
This would generate the following query:
{ "$pull": { "kpis": { "$elemMatch": { "kpis.id": <number> }}}}
I've never seen such syntax before, but I think the $elemMatch is superflous, since $pull already takes criteria to match an array element as its argument. $elemMatch is useful in a query when you're directly matching on an array field and don't mean to do a full equality match against the array elements (i.e. you want to provide criteria instead of an exact match).

Categories