select sum column and group by with Mongodb and Laravel - php

I have a issue with my query:
I want to
SELECT "user_info" with SUM "amount" and GROUP BY "user_id"
I am using Laravel 5 and jenssegers/laravel-mongodb
Thank you so much.

http://laravel.io/forum/10-05-2014-raw-select-using-jenssegers-laravel-mongodb
check link above or use this as i write.
$total = DB::table('user_info')->sum('amount')->groupBy('user_id')->get();

For better performance use the underlying MongoDB driver's aggregation framework methods as this uses the native code on the MongoDB server rather than the .groupBy() methods which basically wraps mapReduce methods.
Consider the following aggregation operation which uses the $group pipeline and the $sum operator to do the sum aggregation:
db.collectionName.aggregate([
{
"$group": {
"_id": "$user_id",
"user_info": { "$first": "$user_info" },
"total": { "$sum": "$amount" }
}
}
]);
The equivalent Laravel example implementation:
$postedJobs = DB::collection('collectionName')->raw(function($collection) {
return $collection->aggregate(array(
array(
"$group" => array(
"_id" => "$user_id",
"user_info" => array("$first" => "$user_info")
"total" => array("$sum" => "$amount")
)
)
));
});

Related

MongoDB Aggregation "group" with "max" field within a sub-array

Using Compass initially, I then need to convert it into the PHP library.
So far, I have a 1st stage that filters the documents on 2 fields using $match:
comp.id (sub-document / array)
playerId
Code is:
$match (from drop-down)
{
"comp.id" : ObjectId('607019361c071256e4f0d0d5'),
"playerId" : "609d0993906429612483cea0"
}
This returns 2 documents.
The document has a sub-array holes, for the holes played in a round of golf. This sub-array has fields (among others):
holes.no
holes.par
holes.grossScore
holes.nettPoints
So each round has 1 document, with a holes sub-array of (typically) 18 array elements (holes), or 9 for half-round. A player will play each round multiple times - hence multiple documents.
I would like to find the highest holes.nettPoints across the documents. I think I need to $group with $max on the holes.nettPoints field, so I would find the highest score for each hole across all rounds.
I have tried this, but in Compass its says its not properly formatted:
$group drop-down
{
_id: holes.no,
"highest":
{ $max: "$holes.nettPoints" }
}
"highest" can be any name I want?
EDIT FOLLOWING PROVIDED ANSWER
The answer marked as the solution was enough of a pointer for how the Aggregation Framework operates (multi-stage documents, i.e. documents as input to 1 stage become new documents as the output of that stage. And so on.
For the purposes of posterity, I ended up using the following aggregation:
[{$match: {
"comp.id" : ObjectId('607019361c071256e4f0d0d5'),
"playerId" : "609d0993906429612483cea0",
"comp.courseId" : "608955aaabebbd503ba6e116"
}
}, {$unwind: {
path : "$holes"
}}, {$group: {
_id: "$holes.no",
hole: {
$max: "$holes"
}
}}, {$sort: {
"hole": 1
}}]
In PHP speak, it looks like:
$match = [
'$match' => [
'comp.id' => new MongoDB\BSON\ObjectID( $compId ),
'playerId' => $playerId,
'comp.courseId' => $courseId
]
];
$unwind = [
'$unwind' => [
'path' => '$holes'
]
];
$group = [
'$group' => [
'_id' => '$holes.no',
'hole' => [
'$max' => '$holes'
]
]
];
$sort = [
'$sort' => [
'hole.no' => 1
]
];
$cursor = $collection->aggregate([$match, $unwind, $group, $sort]);
It is not complete (looking at adding a $sum accumulator across the courseId, not individual documents), but answers the question posted.
$match your conditions
$unwind deconstruct holes array
$sort by nettPoints in descending order
$group by no and select first holes object
[
{
$match: {
"comp.id": ObjectId("607019361c071256e4f0d0d5"),
"playerId": "609d0993906429612483cea0"
}
},
{ $unwind: "$holes" },
{ $sort: { "holes.nettPoints": -1 } },
{
$group: {
_id: "$holes.no",
highest: { $first: "$holes" }
}
}
]

Elasticsearch php : aggregations of documents with date interval

I'm trying to build a faceted search using Elasticsearch-php 6.0, but I'm having a hard time to figure out how to use a date range aggregation. Here's what I'm trying to do :
Mapping sample :
"mappings": {
"_doc": {
"properties": {
...
"timeframe": {
"properties": {
"gte": {
"type": "date",
"format": "yyyy"
},
"lte": {
"type": "date",
"format": "yyyy"
}
}
}
...
In my document, I have this property:
"timeframe":[{"gte":"1701","lte":"1800"}]
I want to be able display a facet with a date range slider, where the user can input a range (min value - max value). Ideally, those min-max values should be returned by the Elasticsearch aggregation automatically given the current query.
Here's the aggregation I'm trying to write in "pseudo code", to give you an idea:
"aggs": {
"date_range": {
"field": "timeframe",
"format": "yyyy",
"ranges": [{
"from": min(timeframe.gte),
"to": max(timeframe.lte)
}]
}
}
I think I need to use Date Range aggregation, min/max aggregation, and pipeline aggregations, but the more I read about them, the more I'm confused. I can't find how to glue this whole world together.
Keep in mind I can change the mapping and / or the document structure if this is not the correct way to achieve this.
Thanks !
As for me with the official "elasticsearch/elasticsearch" package of ES itself, I was able to find a range of my required documents with this document.
You need to read the documentation as you'll be needing the format.
$from_date = '2018-03-08T17:58:03Z';
$to_date = '2018-04-08T17:58:03Z';
$params = [
'index' => 'your_index',
'type' => 'your_type',
'body' => [
'query' => [
'range' => [
'my_date_field' => [
//gte = great than or equal, lte = less than or equal
'gte' => $from_date,
// 'lte' => $to_date,
'format' => "yyyy-MM-dd||yyyy-MM-dd'T'HH:mm:ss'Z'",
'boost' => 2.0
]
]
],
]
];
$search = $client->search($params);

union two collections in mongodb with one query

I have one collection named USER
{"_id" => "id1", ...}
and another collection named CONTACT
{"_id" => "id2", ...}
now i have an array
[id1, id2]
Can I get two data with one query?
You can use the Aggregation Framework $setUnion operator for that.
db.collection.aggregate(
[
{ $project: { id1:1, id2: 1, allValues: { $setUnion: [ "$id1", "$id2" ] } } }
]
)

Equivalent of mysql max and min functions in mongo?

How can i write the below query in mongo?
select max(priority) as max, min(priority) as min from queue group by user
I'll highly appreciate if you provide a solution in PHP.
Thank you
Queries like this are performed with the aggregation framwework and the .aggregate() method. They use a $group pipeline stage with the $min and $max operators.
db.collection.aggregate([
{ "$group": {
"_id": "$user",
"max": { "$max": "$priority" },
"min": { "$min": "$priority" }
}}
])
Or more to PHP syntax:
$collection->aggregate(array(
array(
'$group' => array (
'_id' => '$user',
'max' => array( '$max' => '$priority' ),
'min' => array( '$min' => '$priority' )
)
)
));
Also see the SQL to Aggregation Mapping Chart in the documentation

laravel mongodb push element to existing array in document_

In my mongodb collection I want to push some elements to an existing array. I use jenssegers/Laravel-MongoDB - Eloquent model and Query builder to work with lavavel and mongodb.
How can I use the $push operator in jenssegers/Laravel-MongoDB?
MongoDB entry which shall be updated (RockMongo representation):
{
"_id": ObjectId("5328bc2627784fdb1a6cd398"),
"comments": {
"0": {
"id": 3,
"score": 8
}
},
"created_at": ISODate("2014-03-18T21:35:34.0Z"),
"file": {
"file_id": NumberLong(1175),
"timestamp": NumberLong(1395178534)
}
}
Hint about array representation in rockmongo and mongo shell
RockMongo and mongo shell array representation of the documents are a little bit different. Have a look at the comments-array. The above RockMongo representation appears in the mongo shell as:
{
"_id" : ObjectId("5328c33a27784f3b096cd39b"),
"comments" : [
{
"id" : 3,
"score" : 8
}
],
"created_at" : ISODate("2014-03-18T22:05:46Z"),
"file" : {
"file_id" : NumberLong(1176),
"timestamp" : NumberLong(1395180346)
}
}
As the documentation states the $push operater to push elements to an array. This works fine in the mongo-shell:
Mongo shell
db.Images.update({'file.file_id': 1175},
{ $push: { comments: { id: 3, score: 8} }
})
But in the query-builder I struggle to incorporate the $push operator. I get the error:
localhost:27017: Modified field name may not start with $
I did not find any documentation or example that showed me how to do it..
My jenssegers/Laravel-MongoDB code, that returns the error
// $file_id = 1175
public static function addComment( $file_id ) {
$image = Images::where( 'file.file_id', '=', floatval( $file_id ) )
->update( array('$push' => array( 'comments' => array( 'id' => 4, 'score' => 9 ) ) ) );
return $image;
}
Assuming everything is okay as it works in the shell then use the provided method to push instead:
Images::where('file.file_id', '=', floatval( $file_id ))
->push('comments', array( 'id' => 4, 'score' => 9 ));

Categories