I have started work with lithium framework + mongoDB recently. I want to do a really simple query which contains multiple or statements.
I have articles in the DB with publish_up and publish_down fields. I want to fetch only those records/documents which pulbis_down field is highert than now OR null AND publish_up field lower than now OR null.
$items = $article::find('all', array(
'conditions' => array(
'$or' => array(
'$gt' => array('publish_down', $mongoDateNow),
'publish_down' => $mongDateNull
),
'$or' => array(
'$lt' => array('publish_up', $mongoDateNow),
'publish_up' => $mongDateNull
),
)
));
Of course this snippet is wrong hence the second or statement overwrites the first one (because the same array key).
I tried to wrap them into an individual array but gives error.
Any idea?
This query will fetch articles with (publish_down > now OR publish_down = null) AND (publish_up < now OR publish_up = null)
$items = Articles::find('all', array(
'conditions' => array(
'$or' => array(
array('publish_down' => array('$gt' => $mongoDateNow)),
array('publish_down' => null)
),
'$or' => array(
array('publish_up' => array('$lt' => $mongoDateNow)),
array('publish_up' => null)
),
)
));
I don't know about lithium but in PHP you can use multiple $OR as below
$items = $article::find('all', array(
'conditions' => array(
'$or' => array(
array(
'$gt' => array('publish_down', $mongoDateNow),
'publish_down' => $mongDateNull
),
array(
'$lt' => array('publish_up', $mongoDateNow),
'publish_up' => $mongDateNull
)
),
)
));
I think the correct answer is:
'$and' => array(
array(
'$or'=>array(
array('publish_up' => null),
array('publish_up' => array('$lt' => $mongoDateNow))
)
),
array(
'$or'=>array(
array('publish_down' => null),
array('publish_down' => array('$gt' => $mongoDateNow))
)
)
)
Related
I want to do an aggregation query where I can group by a dbref. This is what I've tried.
$aggregation = array(
array(
'$group' => array(
'_id' => '$foreign.$id',
'doc' => array(
'$last' => '$$ROOT'
)
)
),
array(
'$sort' => array(
'date' => -1
)
)
);
return $this->getDocumentManager()->getDocumentCollection('AppBundle:Collection')->aggregate($aggregation)->toArray();
But this attempt fails because you're not allowed to use the second $ sign in '$foreign.$id'. How can I do this query?
After a lot of searching I've found this Bug Ticket on the monogdb Jira which deals with basically this / an similar issue.
The last comment on this ticket offers an workaround to resolve the issue as an command line code. I've taken this solution and built it into an aggregation query which solved my problem.
$aggregation = array(
array(
'$addFields' => array(
'foreignId' => array(
'$arrayToObject' => array(
'$map' => array(
'input' => array(
'$objectToArray' => '$foreign'
),
'in' => array(
'k' => array(
'$cond' => array(
array(
'$eq' => array(
array(
'$substrCP' => array(
'$$this.k', 0, 1
)
),
array(
'$literal' => '$'
)
)
),
array(
'$substrCP' => array(
'$$this.k',1,['$strLenCP' => '$$this.k']
)
),
'$$this.k'
)
),
'v' => '$$this.v'
)
)
)
)
)
),
array(
'$group' => array(
'_id' => '$foreignId',
'doc' => array(
'$last' => '$$ROOT'
)
)
),
array(
'$sort' => array(
'date' => -1
)
)
);
$this->getDocumentManager()->getDocumentCollection('AppBundle:Collection')->aggregate($aggregation)->toArray();
This query gives me the correct and expected result.
$getdataPipeline = array(
array(
'$match' => array(
'project_id' => array('$in' => $mysql_project_id) // Validating project ID
),
'$match' => array('project_id' => $project_id)
),
array(
'$group' => array(
'_id' => array('pro_id' => '$project_id', 'uid' => '$user_id'),
"wh" => array('$subtract' => array(array('$sum' => '$toltal_timein_minutes'), array('$sum' => '$holding_durationin_minutes')))
))
);
Running query:
$ValidProjectIdInMongo = $collection->aggregate($getdataPipeline);
I'm getting an error like
Uncaught exception 'MongoResultException' with message 'localhost:27017: exception: unknown group operator '$subtract''
The $sum is the accumulator to be used with $group, so it must be the top level operator used. Therefore your other operations need to happen "inside" the $sum:
$getdataPipeline = array(
array(
'$match' => array('project_id' => $project_id)
),
array(
'$group' => array(
'_id' => array('pro_id' => '$project_id', 'uid' => '$user_id'),
"wh" => array(
'$sum' => array(
'$subtract' => array(
'$toltal_timein_minutes',
'$holding_durationin_minutes'
)
)
)
)
)
);
You can do it here because this is basic subtraction, but more complex operations would generally require a separate $project stage after the $group.
Also note that yout $match pipeline stage is incorrect and will actually be interpretted just as I have re-written above. You perhaps mean in $in condition for both the possible values, which is a logical $or.
You also have what looks like typing errors in the field names.
I am a novice in cake and I'm having trouble with a query I want to do in a tables.
$options['joins'] = array(
array('table'=>'users_views',
'alias' => 'UserView',
'type' => 'inner',
'conditions' => array(
'View.id = UserView.view_id',
'UserView.user_id' => $user_id,
'UserView.like' => 1
),
)
);
$options['order'] = array('View.created' => 'desc');
$options['limit'] = 10;
That's my query so far, but what I need now add an OR condition. In this condition OR need to add
'UserView.user_id' => $user_id,
'UserView.like' => 0
I need the query returns the data that matches or are mine and the LIKE value is 0 if it is very difficult to understand please tell me.
Any help or advice will be well received.
Build the conditions like -
'conditions' => array(
0 => array('View.id = UserView.view_id'),
1 => array('UserView.user_id' => $user_id),
3 => 'UserView.like => 1 OR UserView.like => 0'
),
Or with using OR -
'conditions' => array(
0 => array('View.id = UserView.view_id'),
1 => array('UserView.user_id' => $user_id),
2 => 'UserView.like IN (0, 1)'
),
The following should work:
$options['joins'] = array(
array('table'=>'users_views',
'alias' => 'UserView',
'type' => 'inner',
'conditions' => array(
'View.id = UserView.view_id',
'OR' => array(
'UserView.user_id' => $user_id,
'UserView.like' => 1
)
),
)
);
$options['order'] = array('View.created' => 'desc');
$options['limit'] = 10;
I'm using containable behavior and the result of my find('all') is:
array(
(int) 0 => array(
'User' => array(
'id' => '106',
'email' => 'daje#daje.it',
'pwd' => '0433c024cb08be13000d59a347e640482843f46f177e95749dc6599c259617fd3491dcb940b47693cbbc7f65a2cc5ef62deca2e600c1be133ad54170f7d1fbd1',
'role_id' => '3',
'active' => '1'
),
'Lead' => array(
'id' => '6'
),
'Estimate' => array(
(int) 0 => array(
'lead_id' => '6',
'Estimate' => array(
(int) 0 => array(
'TOT_count' => '2'
)
)
)
)
)
)
I need to to count how many estimates there are in the lead.
The total (2) is correct, but i see nested 'Estimated' array, why ?
The result i would like to get is:
array(
(int) 0 => array(
'User' => array(
'id' => '106',
'email' => 'daje#daje.it',
'pwd' => '0433c024cb08be13000d59a347e640482843f46f177e95749dc6599c259617fd3491dcb940b47693cbbc7f65a2cc5ef62deca2e600c1be133ad54170f7d1fbd1',
'role_id' => '3',
'active' => '1'
),
'Lead' => array(
'id' => '6'
),
'Estimate' => array(
'TOT_count' => '2'
)
)
)
This is the find:
$options = array(
'contain' => array(
'User',
'Estimate' => array(
'fields' => 'COUNT(*) AS TOT_count'
)
),
'conditions' => array('Lead.id' => 6),
'fields' => 'User.*',
'limit' => 1
);
debug($this->Lead->find('all', $options));
How can i do it?
Thanks!
When you use a "custom" AS statement, in your case TOT_count, Cake will always put this in a result key called 0. You can avoid this by defining TOT_count as a virtualField in your model. That way it will be nested directly under the model name in your resultset.
Secondly, the lead_id is forcedly retrieved, because it is "needed" to make the join with the Lead model. It can not properly retrieve all the data without that piece of information there.
As the title says, I'm having troubles with joins in my find-query, with errors appearing like:
preg_match() expects parameter 2 to be string, array given [CORE/cake/libs/model/behaviors/containable.php, line 301]
I've tried adding joins to the array over there, but it didn't change anything.
These are the options I'm passing to the find method.
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'joins' => array(
$this->votesJoin()
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
),
'User',
'Tag' => array()
),
'joins' => array(
$this->votesJoin()
),
'conditions' => array(
'Question.id' => $id
)
);
return $this->find('first', $options);
with votesJoin() returning the following array.
(
[table] => (SELECT Vote.node_id, SUM(value) as total FROM votes AS `Vote` WHERE 1 = 1 GROUP BY `Vote`.`node_id` )
[alias] => Vote
[conditions] => Vote.node_id = Question.id
)
What I'm trying to do:
Each user can up/downvote a node (question/answer/comment). With the join I'm trying to add the sum of those votes.
database http://github.com/navale/QA/wiki/img/datamodel.png
You should use "joins" only for things that can't be done with Cake model relationship and Containable. I don't know the details of your database, but I think the find operation can be simplified. Why don't you post the schema for these tables on here?
try this:
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
),
'User',
'Tag' => array()
),
'conditions' => array(
'Question.id' => $id
)
);
You get the Vote sum value for each answer, comment, and comment for answer. (You might need to add 'hasMany' Vote in the Node model if you haven't done that yet)
If instead you want to get one single total sum of Vote for the question, then I'd suggest:
get the list of the answers and comments of the question:
$lvl1 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$id))
then get list of the comments of the answers
$lvl2 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$lvl1))
then just combine the 2 array then do a sum over that.