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);
?>
Related
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?
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
I want to sort results on the release date. I want 2015 movies first and the rest of the years sorted on the has_poster value. So I get results like:
2015, 2015, 1989, 2017, 2006
etc.
So far this is what I've got.
$params['index'] = 'movies';
$params['type'] = 'movie';
$params['body']['size'] = 50;
$params['body']['sort'] = array(
array('has_poster' => "desc"),
array('_score' => "desc"),
);
$params['body']['query']['filtered'] = array(
'query' => array(
'query_string' => array(
'query' => "survivor",
'fields' => array('name', 'orig_title'),
'default_operator' => "AND"
),
)
);
I need the equivalent of ... ORDER BY FIELD(releasedate, 2015), has_poster DESC ... in MySQL for Elasticsearch.
You need a scripted sorting:
{
"sort": [
{
"_script": {
"script": "if(doc['releasedate'].date.year==2015) return 1; else return 0;",
"type": "number",
"order": "desc"
}
},
{
"has_poster": "desc"
}
]
}
Im not work =>type name is string
$params['body']['sort']= array('name' => 'desc' );
or
$params=[
'index'=>'pets',
'type'=>'bird',
'body'=>[
'query'=>[
'bool'=>[
'must'=>[],
'filter'=>[
'range'=>[
'registered'=>[
'gte' => "2020-10-01",
'lte' => "2020-11-30"]
]
]
]
],
'sort'=>[
'name'=>[
'order'=>'asc'
]
]
]
];
but not work
I have the following collection structure
{
"_id": {
"d_timestamp": NumberLong(1429949699),
"d_isostamp": ISODate("2015-04-25T08:14:59.0Z")
},
"XBT-USD-cpx-okc": [
{
"buySpread": -1.80081
}
I run the following aggregation
$spreadName ='XBT-USD-stp-nex';
$pipe = array(
array(
'$match' => array(
'_id.d_isostamp' => array(
'$gt' => $start, '$lt' => $end
)
)
),
array(
'$project' => array(
'sellSpread' =>'$'.$spreadName.'.sellSpread',
)
),
array(
'$group' => array(
'_id' => array(
'isodate' => array(
'$minute' => '$_id.d_isostamp'
)
),
'rsell_spread' => array(
'$avg' => '$sellSpread'
),
)
),
);
$out = $collection->aggregate($pipe ,$options);
and I get as a result the value 0 for rsell_spread whereas if I run a $max for instance instead of an $avg in the $group , I get an accurate value for rsell_spread , w/ the following structure
{
"_id": {
"isodate": ISODate("2015-04-25T08:00:58.0Z")
},
"rsell_spread": [
-4.49996▼
]
}
So I have two questions :
1/ How come does the $avg function does not work?
2/ How can I can a result not in an array when I use $max for example (just a regular number)?
The $avg group accumulator operator does work, it's only that in your case it is being applied to an element in an array and thus gives the "incorrect" result.
When you use the $max group accumulator operator, it returns the the highest value that results from applying an expression to each document in a group of documents, thus in your example it returned the maximum array.
To demonstrate this, consider adding a few sample documents to a test collection in mongoshell:
db.test.insert([
{
"_id" : {
"d_timestamp" : NumberLong(1429949699),
"d_isostamp" : ISODate("2015-04-25T08:14:59.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80081
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949710),
"d_isostamp" : ISODate("2015-04-25T08:15:10.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80079
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949720),
"d_isostamp" : ISODate("2015-04-25T08:15:20.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80083
}
]
},
{
"_id" : {
"d_timestamp" : NumberLong(1429949730),
"d_isostamp" : ISODate("2015-04-25T08:15:30.000Z")
},
"XBT-USD-stp-nex" : [
{
"sellSpread" : -1.80087
}
]
}
])
Now, replicating the same operation above in mongoshell:
var spreadName = "XBT-USD-stp-nex",
start = new Date(2015, 3, 25),
end = new Date(2015, 3, 26);
db.test.aggregate([
{
"$match": {
"_id.d_isostamp": { "$gte": start, "$lte": end }
}
},
{
"$project": {
"sellSpread": "$"+spreadName+".sellSpread"
}
}/*,<--- deliberately omitted the $unwind stage from the pipeline to replicate the current pipeline
{
"$unwind": "$sellSpread"
}*/,
{
"$group": {
"_id": {
"isodate": { "$minute": "$_id.d_isostamp"}
},
"rsell_spread": {
"$avg": "$sellSpread"
}
}
}
])
Output:
/* 0 */
{
"result" : [
{
"_id" : {
"isodate" : 15
},
"rsell_spread" : 0
},
{
"_id" : {
"isodate" : 14
},
"rsell_spread" : 0
}
],
"ok" : 1
}
The solution is to include an $unwind operator pipeline stage after the $project step, this will deconstruct the XBT-USD-stp-nex array field from the input documents and outputs a document for each element. Each output document replaces the array with an element value. This will then make it possible for the $avg group accumulator operator to work.
Including this will give the aggregation result:
/* 0 */
{
"result" : [
{
"_id" : {
"isodate" : 15
},
"rsell_spread" : -1.80083
},
{
"_id" : {
"isodate" : 14
},
"rsell_spread" : -1.80081
}
],
"ok" : 1
}
So your final working aggregation in PHP should be:
$spreadName ='XBT-USD-stp-nex';
$pipe = array(
array(
'$match' => array(
'_id.d_isostamp' => array(
'$gt' => $start, '$lt' => $end
)
)
),
array(
'$project' => array(
'sellSpread' =>'$'.$spreadName.'.sellSpread',
)
),
array('$unwind' => '$sellSpread'),
array(
'$group' => array(
'_id' => array(
'isodate' => array(
'$minute' => '$_id.d_isostamp'
)
),
'rsell_spread' => array(
'$avg' => '$sellSpread'
),
)
),
);
$out = $collection->aggregate($pipe ,$options);
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/