MongoDB PHP Aggregation empty results with date ($lte, $gte) in $match - php

I've the following Aggregation:
$pipeline = array(
array('$match' => array(
'matchDate' => array(
'$lte' => $dateEnd, // DateTime object
'$gte' => $dateStart // DateTime object
)
)),
array('$group' => array(
'_id' => '$sport',
'count' => array('$sum' => 1)
))
);
$m = new \MongoClient('localhost');
$c = $m->selectDB('test_database')->selectCollection('Match');
$t = $c->aggregate($pipeline);
This aggregation will return an empty Result.
If I run the aggregation on my MongoDB directly, it works without problems and gives the expected results.
Here is the native query.
db.Match.runCommand({
"aggregate": "Match",
"pipeline": [
{
"$match": {
"matchDate": {
"$lte": new ISODate("2015-10-07T23:59:59+02:00"),
"$gte": new ISODate("2015-10-06T00:00:00+02:00")
}
}
},
{
"$group": { "_id": "$sport", "count": { "$sum": 1 } }
}
]});
The problem occurs only with aggregation. Find queries with date ($lte, $gte) works also without problems.
Here is an example document.
{
"_id": ObjectId("5613bbb79042ad801f0041ab"),
"sport": ObjectId("5613bbb79042ad801f0041a8"),
"matchDate": new Date("2015-09-26T13:45:00+0200")
}
Has someone an idea whats happen here?
I'm Using
MongoDB Support 1.6.11
PHP 5.6.13
MongoDB 3.0.6

It'll work with MongoDate.
$pipeline = array(
array('$match' => array(
'matchDate' => array(
'$lte' => new MongoDate($dateEnd->getTimestamp()),
'$gte' => new MongoDate($dateStart->getTimestamp())
)
)),
array('$group' => array(
'_id' => '$sport',
'count' => array('$sum' => 1)
))
);
$m = new \MongoClient('localhost');
$c = $m->selectDB('test_database')->selectCollection('Match');
$t = $c->aggregate($pipeline);
Thanks #Blakes Seven

Related

How to convert MySQL search criteria to Elasticsearch query

I am trying to convert MySQL search criteria to Eleasticsearch (version 7) query, however, both queries (MySQL and Elasticsearch) gives different results. On the SQL statement, it shows a single record, and on Elasticsearch, it shows 0 records. Any help or guidance is much appreciated on this.
SQL criteria
COUNT(*) WHERE (document_title like '%never listened%' or document_content like '%never listened%') and documnent_tone = 'negative'
Converting to Elasticsearch - using PHP Elasticsearch library
use Elasticsearch\ClientBuilder;
...
$elasticServer = $this->getOption('elasticsearch')->getElasticServer1();
$hosts = array(
$elasticServer['host'] . ':' . $elasticServer['port']
);
$client = ClientBuilder::create()
->setHosts($hosts)
->build();
$params = array (
"index" => "index_politics",
"body" => array(
"query" => array(
"bool" => array(
"must" => array(
array(
"wildcard" => array(
"document_title" => "never listened",
)
),
array(
"wildcard" => array(
"document_content" => "never listened"
)
),
array(
"match" => array(
"document_tone" => "negative"
)
)
)
)
)
)
);
$response = $client->count($params);
$negative = $response['count'];
var_dump($negative);
After much digging, the following worked for me
use Elasticsearch\ClientBuilder;
...
$elasticServer = $this->getOption('elasticsearch')->getElasticServer1();
$hosts = array(
$elasticServer['host'] . ':' . $elasticServer['port']
);
$client = ClientBuilder::create()
->setHosts($hosts)
->build();
$params = array (
"index" => "index_politics",
"body" => array(
"query" => array(
"bool" => array(
"must" => array(
array(
"multi_match" => array(
"query" => "never listened",
"fields" => array("document_title", "document_content")
)
)
),
"filter" => array(
"term" => array(
"document_tone" => "negative"
)
)
)
)
)
);
$response = $client->count($params);
$negative = $response['count'];
var_dump($negative);
Converted to Elasticsearch query
GET /index_politics/_count
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "never listened",
"fields": [
"document_title",
"document_content"
]
}
}
],
"filter": {
"term": {
"document_tone": "negative"
}
}
}
}
}

Convert Mongodb shell query with map and aggregate to php

I wrote a mongodb query that I am having a hard time converting to php code:
var geoips = db.geoip.find().map(function(like){ return like.ip; });
var result = db.audit.aggregate([
{ $match: { ip: { $nin: geoips } } },
{ $group: {
_id: "$ip",
count: { $sum: 1 }
}}
]);
UPDATE:
The above query is the equivalent of the following Relation Database Query
Select ip,count(*)
from audit
where ip not in (select ip from geoip)
group by ip
Since I had to make this query in mongodb version 3.0, I was unable to take advantage of $lookup as suggested in an answer.
The below PHP code accomplishes the above objective and works as expected. It gets the distinct ips from geoip collection. It passes that result and does an aggregate on the audit collection to get the desired result.
$geoipcolln = $this->dbConn->selectCollection('geoip');
$geoips = $geoipcolln->distinct('ip');
$match = array('ip' => array('$nin' => $geoips));
$result = $this->collection->aggregate(
array(
'$match' => $match
),
array('$group' => array(
'_id' => '$ip',
'count' => array('$sum' => 1.0),
))
);
This can be done in one aggregation query using the $lookup operator as follows:
var result = db.audit.aggregate([
{
"$lookup": {
"from": "geoip",
"localField": "ip",
"foreignField": "ip",
"as": "geoips"
}
},
{ "$match": { "geoips.0": { "$exists": false } } },
{ "$group": {
"_id": "$ip",
"count": { "$sum": 1 }
}}
])
which can then be translated to PHP as:
<?php
$m = new MongoClient("localhost");
$c = $m->selectDB("yourDB")->selectCollection("audit");
$ops = array(
array(
"$lookup" => array(
"from" => "geoip",
"localField" => "ip",
"foreignField" => "ip",
"as" => "geoips"
)
),
array( "$match" => array( "geoips.0" => array( "$exists" => false ) ) ),
array( "$group" => array(
"_id" => "$ip",
"count" => array( "$sum" => 1 )
))
);
$results = $c->aggregate($ops);
var_dump($results);
?>

mongodb php multi group stage

I want to execute below mongodb script using php:
db.getCollection('play_history').aggregate(
[ { $match : { At : {$gte : 1470307490, $lt : 1470307497649}, moneyType:1 } } ,
{
$group:
{
_id: { gameType: "$gameType", sessionId: "$sessionId"},
}
},
{
$group:
{
_id: "$_id.gameType",
count: { $sum: 1}
}
}
])
Below is the php snip code:
$match = [ 'moneyType' => 1,
'At' => ['$gt' => 12, '$lt' => 1470307497649]];
$command = new MongoDB\Driver\Command([
'aggregate' => 'play_history',
'pipeline' => [
['$match' => $match],
['$group' =>
['_id' => ["gameType" => '$gameType', "sessionId" => '$sessionId']]],
['$group' =>
['_id' => '$_id.gameType' ],
'countGame' => ['$sum' => 1 ]],
],
'cursor' => new stdClass,
]);
$cursor = $manager->executeCommand('falcon', $command);
The above code give me the following error:
A pipeline stage specification object must contain exactly one field
I have tried to change several grouping orders but it didn't work. Is that a limitation of PHP to just has only one group stage per command?

mongodb $nin not work in php

Current Code:
$doc = array('ooxx' => array(1,2,3,4,5));
datamodel()->insert($doc);
$doc2 = array('ooxx' => array(6,7,8,9));
datamodel()->insert($doc2);
$macher = array('ooxx'=>array('$exists' => true), 'ooxx' => array('$nin'=>array(6)));
$res = datamodel()->findOne($macher);
print_r($res);
When I replace the $macher with bellow, it does work well, why? is this a bug of mongodb?
$macher = array( 'ooxx' => array('$nin'=>array(6)), 'ooxx'=>array('$exists' => true));
It doesn't work because the keys have the same name and one overwrites the other. So the "keys" need to be unique.
If you have two conditions for the same key you use the $and operator which takes an array of arguments:
$matcher = array(
'$and' => array(
array( 'ooxx' => array( '$nin' => array(6) ) ),
array( 'ooxx' => array( '$exists' => true ) )
)
)
Or for the JSON minded:
{
"$and": [
{ "ooxx": { "$nin": [6] } },
{ "ooxx": { "$exists": true } }
]
}
Which is a valid structure where what you are writing is not.

MongoDB aggregate query using PHP driver

I have a working MongoDB aggregate query which I can run via the MongoDB shell. However, I am trying to convert it to work with the official PHP Mongo driver (http://php.net/manual/en/mongocollection.aggregate.php).
Here is the working raw MongoDB query:
db.executions.aggregate( [
{ $project : { day : { $dayOfYear : "$executed" } } },
{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }
] )
Here is my attempt (not working) in PHP using the Mongo driver:
$result = $c->aggregate(array(
'$project' => array(
'day' => array('$dayOfYear' => '$executed')
),
'$group' => array(
'_id' => array('day' => '$day'),
'n' => array('$sum' => 1)
),
'$sort' => array(
'_id' => 1
),
'$limit' => 14
));
The error from the above PHP code is:
{"errmsg":"exception: wrong type for field (pipeline) 3 != 4","code":13111,"ok":0}
Any ideas? Thanks.
The parameter in your Javascript is an array of 4 objects with one element each, in your PHP it's an associative array (object) with 4 elements. This would represent your Javascript:
$result = $c->aggregate(array(
array(
'$project' => array(
'day' => array('$dayOfYear' => '$executed')
),
),
array(
'$group' => array(
'_id' => array('day' => '$day'),
'n' => array('$sum' => 1)
),
),
array(
'$sort' => array(
'_id' => 1
),
),
array(
'$limit' => 14
)
));
In addition, if you have at least PHP5.4, you can use simpler array syntax. Transformation to PHP is then trivial, you simply replace curly braces with square brackets and colons with arrows:
$result = $c->aggregate([
[ '$project' => [ 'day' => ['$dayOfYear' => '$executed'] ] ],
[ '$group' => ['_id' => ['day' => '$day'], 'n' => ['$sum' => 1] ] ],
[ '$sort' => ['_id' => 1] ],
[ '$limit' => 14 ]
]);
You can also use the JSON array directly:
$json = [
'{ $project : { day : { $dayOfYear : "$executed" } } },
{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
];
$pipeline = [];
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
You can also use combination like this:
use function MongoDB\BSON\toRelaxedExtendedJSON;
use function MongoDB\BSON\fromPHP;
use function MongoDB\BSON\toPHP;
use function MongoDB\BSON\fromJSON;
$operator = '$dayOfYear';
$json = [];
array_push($json, toRelaxedExtendedJSON(fromPHP(
array('$project ' => array('day' => array($operator => '$executed')))
)));
array_push($json,
'{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
);
$pipeline = [];
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
or
$operator = '$dayOfYear';
$json = [];
array_push($json,
'{ $group : { _id : { day : "$day" }, n : { $sum : 1 } } } ,
{ $sort : { _id : -1 } } ,
{ $limit : 14 }'
);
$pipeline = [];
array_push($pipeline, array('$project ' => array('day' => array($operator => '$executed'))));
foreach ($json as $stage) {
array_push($pipeline, toPHP(fromJSON($stage)));
}
$result = $c->aggregate($pipeline);
If your aggregation query is dynamic or based on user-input be aware of NoSQL-Injection!
https://www.acunetix.com/blog/web-security-zone/nosql-injections/

Categories