I'm working with mongodb in laravel using jenssegers driver and i have 3 documents likes this in my db
{
"_id" : ObjectId("594dd540bb7de924c0005583"),
"cliente" : "Luis",
"tipo" : "Luis",
"paquete" : "Luis",
"fecha" : null,
"fechaE" : null,
"content" : "fotos",
"precio" : NumberInt(200),
"estatus" : NumberInt(2),
"Abonos" : [
{
"Monto" : NumberInt(200),
"Fecha" : null
},
{
"Monto" : NumberInt(2000),
"Fecha" : null
},
{
"Monto" : NumberInt(2000),
"Fecha" : null
}
],
"updated_at" : ISODate("2017-06-24T02:58:08.000+0000"),
"created_at" : ISODate("2017-06-24T02:58:08.000+0000")}
Im using this Raw query to get que $sum of all "monto" for each document(3 documents).
public function updatearray(Request $request, $id){
$works = new work;
$result = Work::raw(function($collection)
{
return $collection->aggregate(array(
array('$unwind' => '$Abonos'),
array('$group' =>
array( "_id" => '$_id',"Monto" => array('$sum' => '$Abonos.Monto'))),
));
});}
I only want to display the results for my current document, is there a way i can match with the current $id?
-- Edit
Im getting id from a get method
public function abono($id){
$work = work::find($id);
return view('workabono', compact('work'));
}
and these are the routes that i use
Route::get('works/{work}/abono', [
'as' => 'works.abono', 'uses' => 'WorkController#abono']);
Route::put('works/{work}/', [
'as' => 'works.updateAbono', 'uses' => 'WorkController#updatearray']);
You don't even need the $unwind here. Simply $match and $project:
public function updatearray(Request $request, $id){
$works = new work;
$result = Work::raw(function($collection) use($id)
{
return $collection->aggregate(array(
array('$match' => array( '_id' => $id) ),
array('$project' =>
array( "Monto" => array('$sum' => '$Abonos.Monto') )
)
));
});}
The $sum can directly add the elements from the array.
If $id is a string you can cast to ObjectId explicitly,
$id = new \MongoDB\BSON\ObjectID($id);
Related
I'm trying to build an aggregation query in Parse's PHP SDK, and I'm stuck in the "lookup" area, I saw a JS example regarding this but it doesn't work in my case.
I have a table of users, which contains a "Tags" field of type Array, the array is actually an array of pointers, that point to a separate Tag class.
What I'm trying to achieve is to list most popular Tags based on their usage, so basically I need to query the users class and group the Tags that exist in the array, I already achieved this, but I'm stuck with the lookup part, the query currently returns an array of Tags pointers, what I want is to pull the object of those pointers.
Here's what I have currently:
$query = new ParseQuery('_User');
$pipeline = [
'project' => ['tags' => 1],
'unwind' => '$tags',
'group' => [
'objectId' => '$tags.objectId',
'count' => ['$sum' => 1]
],
'sort' => [ 'count' => -1],
'limit' => 10,
];
try {
return $query->aggregate($pipeline);
} catch (ParseException $ex) {
return $ex->getMessage();
}
And here's a snippet of what the _User collection looks like:
{
"_id" : "5BuBVo2GD0",
"email" : "test#test.com",
"username" : "test#test.com",
"lastname" : "Doe",
"firstname" : "John",
"_created_at" : ISODate("2017-01-23T09:20:11.483+0000"),
"_updated_at" : ISODate("2019-02-15T02:48:30.684+0000"),
"tags" : [
{
"__type" : "Pointer",
"className" : "Tag",
"objectId" : "St2gzaFnTr"
},
{
"__type" : "Pointer",
"className" : "Tag",
"objectId" : "LSVxAy2o74"
}
],
"_p_country" : "Country$4SE8J4HRBi",
}
And the Tag collection looks like this:
{
"_id" : "St2gzaFnTr",
"name" : "Music",
"_created_at" : ISODate("2018-10-22T20:00:10.481+0000"),
"_updated_at" : ISODate("2018-10-22T20:00:10.481+0000")
}
Any help would be appreciated!
Thanks in advance
Not sure if this is a direct answer, but here's a working aggregation on tags sorting for freq...
public function tagHistogram(Request $request, Response $response, array $args): Response {
$pipeline = [
'unwind' => '$tags' ,
'sortByCount' => '$tags',
'limit' => 1000,
];
$query = new ParseQuery('Product');
$result = $query->aggregate($pipeline);
$result = array_map(
function ($e) {
$e['name'] = $e['objectId'];
unset($e['objectId']);
return $e;
},
$result
);
return $response->withJson($result);
}
Below is my sample mongodb collection
{
"_id" : ObjectId("57ed32f4070577ec56a56b9f"),
"log_id" : "180308",
"issue_id" : "108850",
"author_key" : "priyadarshinim_contus",
"timespent" : NumberLong(18000),
"comment" : "Added charts in the dashboard page of the application.",
"created_on" : "2017-08-16T18:22:04.816+0530",
"updated_on" : "2017-08-16T18:22:04.816+0530",
"started_on" : "2017-08-16T18:21:39.000+0530",
"started_date" : "2017-08-02",
"updated_date" : "2017-08-02",
"role" : "PHP",
"updated_at" : ISODate("2017-09-29T15:27:48.069Z"),
"created_at" : ISODate("2017-09-29T15:27:48.069Z"),
"status" : 1.0
}
I need to get records with help of started_date , by default I will give two dates in that i will check $gt and $lt of started date .
$current_date = '2017-08-31';
$sixmonthfromcurrent ='2017-08-01';
$worklogs = Worklog::raw ( function ($collection) use ($issue_jira_id, $current_date, $sixmonthfromcurrent) {
return $collection->aggregate ( [
['$match' => ['issue_id' => ['$in' => $issue_jira_id],
'started_date' => ['$lte' => $current_date,'$gte' => $sixmonthfromcurrent]
]
],
['$group' => ['issue_id' => ['$push' => '$issue_id'],
'_id' => ['year' => ['$year' => '$started_date'],
'week' => ['$week' => '$started_date'],'resource_key' => '$author_key'],
'sum' => array ('$sum' => '$timespent')]
],
[ '$sort' => ['_id' => 1]
]
] );
} );
If I run this query I am getting this type of error:
Can't convert from BSON type string to Date
How to rectify this error?
The only field in your $group that I see as troubling is the field week.
The year you could extract by doing a $project before your $group aggregation:
$project: {
year: { $substr: [ "$started_date", 0, 4 ] },
issue_id: 1,
author_key: 1,
timespent: 1
}
if you know that the date string will always come at this format. Of course you cannot do a substr operation for finding out the week.
It would be easy though if your field started_date would be an actual ISODate(), then you could use exactly what you wrote as you probably already saw in the documentation.
If you need the field week very bad, which I imagine you do, then I'd suggest you convert your field started_date to an ISODate().
You can do that with a bulkWrite:
db = db.getSiblingDB('yourDatabaseName');
var requests = [];
db.yourCollectionName.find().forEach(doc => {
var date = yourFunctionThatConvertsStringToDate(doc.started_date);
requests.push( {
'updateOne': {
'filter': { '_id': doc._id },
'update': { '$set': {
"started_date": date
} }
}
});
if (requests.length === 500) {
db.yourCollectionName.bulkWrite(requests);
requests = [];
}
});
if(requests.length > 0) {
db.yourCollectionName.bulkWrite(requests);
}
Load this script directly on your mongodb server and execute there.
Hope this helps.
i have collection like this
{
"wl_total" : 380,
"player_id" : 1241,
"username" : "Robin",
"hand_id" : 292656,
"time" : 1429871584
}
{
"wl_total" : -400,
"player_id" : 1243,
"username" : "a",
"hand_id" : 292656,
"time" : 1429871584
}
as both collection have same hand_id i want to aggregate both these collection on the basis of hand_id
i want result as combine of
data=array(
'hand_id'=>292656,
'wl_total'=>
{
0=>380,
1=>-400
},
'username'=>
{
0=>"Robin",
1=>"a"
},
"time"=>1429871584
)
You basically want a $group by the "hand_id" common to all players, and then $push to different arrays in the document and then also do something with "time", I took $max. Nees to be an accumulator of some sort at any rate.
Also not sure what your underlying collection name is, but you can call this in laravel with a construct like this:
$result = DB::collection('collection_name')->raw(function($collection)
{
return $collection->aggregate(array(
array(
'$group' => array(
'_id' => '$hand_id',
'wl_total' => array(
'$push' => '$wl_total'
),
'username' => array(
'$push' => '$username'
),
'time' => array(
'$max' => '$time'
)
)
)
));
});
Which returns output ( shown in json ) like this:
{
"_id" : 292656,
"wl_total" : [
380,
-400
],
"username" : [
"Robin",
"a"
],
"time" : 1429871584
}
Personally I would have gone for a single array with all the infomation in it for the grouped "hand", but I supose you have your reasons why you want it this way.
I have an mongodb collection with following documents:
{
"_id" : ObjectId("547af6aea3f0eba7148b4567"),
"check_id" : "f5d654e7-257d-4a93-ae50-2d59dfeeb451",
"chunks" : NumberLong(200),
"num_hosts" : NumberLong(1000),
"num_rbls" : NumberLong(163),
"owner" : NumberLong(7901),
"created" : ISODate("2014-11-30T10:51:26.924Z"),
"started" : ISODate("2014-11-30T10:51:31.558Z"),
"finished" : ISODate("2014-11-30T10:57:08.512Z")
}
{
"_id" : ObjectId("54db19a858a5d395a18b4567"),
"check_id" : "9660e510-1349-43f3-9d5e-8bf4b06179be",
"chunks" : NumberLong(2),
"num_hosts" : NumberLong(10),
"num_rbls" : NumberLong(166),
"owner" : NumberLong(7901),
"created" : ISODate("2015-02-11T08:58:17.118Z"),
"started" : ISODate("2015-02-11T08:58:18.78Z"),
"finished" : ISODate("2015-02-11T08:58:47.486Z")
}
{
"_id" : ObjectId("54db267758a5d30eab8b4567"),
"check_id" : "9660e510-1349-43f3-9d5e-8bf4b06179be",
"chunks" : NumberLong(2),
"num_hosts" : NumberLong(10),
"num_rbls" : NumberLong(166),
"owner" : NumberLong(7901),
"created" : ISODate("2015-02-11T09:52:55.388Z"),
"started" : ISODate("2015-02-11T09:52:56.109Z"),
"finished" : ISODate("2015-02-11T09:53:22.095Z")
}
What I need is to get the result and produce an array similar to this:
Array
(
[2015-02-11] => array
(
//array with results from 2015-02-11
)
[2014-11-30] => array
(
//array with results from 2014-11-30
)
)
I know that it's possible to just perform simply collection->find and then loop through results and use php logic to achieve my goal but is it possible to make it using mongo? Maybe using aggregation framework?
EDIT: I want to group results by "created" date
Any help will be highly appreciated.
Monogo aggregation mongo aggregation group used for this, so below query may solve your problem
db.collectionName.aggregate({
"$group": {
"_id": "$created",
"data": {
"$push": {
"check_id": "$check_id",
"chunks": "$chunks",
"num_hosts": "$num_hosts",
"num_rbls": "$num_rbls",
"owner": "$owner",
"started": "$started",
"finished": "$finished"
}
}
}
}).pretty()
Or
db.collectionName.aggregate({
"$group": {
"_id": "$created",
"data": {
"$push": "$$ROOT"
}
}
}).pretty()
Also in mongo 2.8 $dateToString provide facility to convert ISO date to string format so below query also work
db.collectionName.aggregate([
{
"$project": {
"yearMonthDay": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$created"
}
},
"check_id": "$check_id",
"chunks": "$chunks",
"num_hosts": "$num_hosts",
"num_rbls": "$num_rbls",
"owner": "$owner",
"started": "$started",
"finished": "$finished"
}
},
{
"$group": {
"_id": "$yearMonthDay",
"data": {
"$push": "$$ROOT"
}
}
}
]).pretty()
I have managed to solve this using the aggregation framework. Here is the answer, in case anyone need it.
$op = array(
array(
'$project' => array(
'data' => array(
'check_id' => '$check_id',
'chunks' => '$chunks',
'num_hosts' => '$num_hosts',
'num_rbls' => '$num_rbls',
'owner' => '$owner',
'started' => '$started',
'finished' => '$finished',
),
'year' => array('$year' => '$created' ),
'month' => array('$month' => '$created' ),
'day' => array('$dayOfMonth' => '$created'),
)
),
array(
'$group' => array(
'_id' => array('year' => '$year', 'month' => '$month', 'day' => '$day'),
'reports_data' => array('$push' => '$data'),
)
),
);
$c = $collection->aggregate($op);
I have this collection
> db.test.find()
{ "_id" : ObjectId("5398ddf40371cdb3aebca3a2"), "name" : "ahmed", "qte" : 30 }
{ "_id" : ObjectId("5398de040371cdb3aebca3a3"), "name" : "demha", "qte" : 35 }
{ "_id" : ObjectId("5398de140371cdb3aebca3a4"), "name" : "ahmed", "qte" : 50 }
{ "_id" : ObjectId("5398de210371cdb3aebca3a5"), "name" : "ahmed", "qte" : 60 }
i would like to sum "qte" where "name"= "ahmed" and print the sum with php
i know how to do with SQL but i have no idea how it is in mongodb.
Thanks :)
Use the aggregation framework.
Assuming you have an the current collection as $collection
result = $collection->aggregate(array(
array(
'$match' => array(
'name' => 'ahmed'
)
),
array(
'$group' => array(
'_id' => NULL,
'total' => array(
'$sum' => '$qte'
)
)
)
));
The two parts are the $match to meet the criteria, and the $group to arrive at the "total" using $sum
See other Aggregation Framework Operators and the Aggregation to SQL Mapping chart for more examples.
This is done with an aggregate statement:
db.test.aggregate([
{
$match: {
name: "ahmed"
}
},
{
$group: {
_id:"$name",
total: {
$sum: "$qte"
}
}
}
])