I am using codeigniter with mongodb library https://github.com/intekhabrizvi/Codeigniter-mongo-library
below is my collection "users". I want to count all the badges which has badge_slug = 100_club for userid 57b83ae9faa76bac338b4579 from users collection.
{
"_id" : ObjectId("57b83ae9faa76bac338b4579"),
"displayname" : "test",
"email" : "test#gmail.com",
"badges" : [
{
"awarded_at" : ISODate("2015-04-21T05:52:06Z"),
"object_id" : "",
"badge_slug" : "100_club"
},
{
"awarded_at" : ISODate("2015-04-21T06:12:14Z"),
"object_id" : "",
"badge_slug" : "100_club"
},
{
"awarded_at" : ISODate("2015-04-21T07:09:55Z"),
"object_id" : "",
"badge_slug" : "reader"
}
]
}
what I have done is
$ops = array(
array(
"$project" => array(
"count" => array(
"$size" => array(
"$filter" => array(
"input" => "$badges",
"as" => "badge",
"cond" => array("$eq" => => array("$$badge.badge_slug", "100_club") )
)
)
)
)
),
array(
"$group" => array(
"_id" => null,
"total" => array( "$sum" => "$count" )
)
)
);
$this->mongo_db->aggregate("users", $ops);
So the result should be 2 for given document.
But its returning count for all users. Where should I need to give userid condition?
You can use $match to filter documents before they're passed to next stage.
Related
MongoDB schema:
{
"_id" : ObjectId("60057ff5c34000008a00214a"),
"Name" : "Test Store",
"AFM" : "099360666",
"DOY" : "ffa",
"Address" : "faf",
"TEL" : "sss",
"Products" : [
{
"_id" : ObjectId("600584b8c34000008a00214d"),
"pID" : "099360666/1",
"pNAME" : "old scholl 2",
"pPRICE" : "55",
"pBRAND" : "vans",
"pDESCRIPTION" : "eco frinedly",
"pSIZE" : "45,44",
"pDEPARTMENT" : "Men/Shoes/Trainers",
"pTHUMBNAIL" : "http://127.0.0.1/pricedoc/assets/img/products/p1.jpg",
"pQUANTITY" : "7",
"pCOMMENTS" : {
"Comment" : [ ]
}
}
],
"nextProductCounter" : 2
}
I want to add comments through a html form using php to add comment at db.
The best I've got is this, but seems to not work
$new_comm = array(
"username" => "guest",
"date" => new \MongoDB\BSON\UTCDateTime(strtotime(date('d-m-Y 00:00'))),
"text" => "excellent product",
"rating" => "4"
);
$collection->updateOne(
array("Products._id" => new MongoDB\BSON\ObjectId("60058012c34000008a00214b")),
array('$push' => array("Products.pCOMMENTS" => array("Comment"=>$new_comm)))
);
Why the pCOMMENTS array isn't updating?
Expected results
"pCOMMENTS" : {
"Comment" : {
"username" : "guest",
"date" : "18/1/2021",
"text" : "excellent product",
"rating" : "4"
}
}
You need to use positional operator
Reference
$collection->updateOne(
array("Products._id" => new MongoDB\BSON\ObjectId("60058012c34000008a00214b")),
array('$push' => array("Products.$.pCOMMENTS" => array("Comment"=>$new_comm)))
);
I am working on mongoDB with Laravel using jenssegers. And i am trying to sum based on Abonos.price.
I have a MongoDb document like this:
`{
"_id" : ObjectId("594a89358b85112444002924"),
"cliente" : "blabla",
"tipo" : "bla bla",
"paquete" : "bla bla",
"date" : "2017-06-14",
"content" : "picture",
"cost" : NumberInt(200),
"status" : NumberInt(2),
"Abonos" : [
{
"price" : "200", `enter code here`
"date" : "2017-06-21"
},
{
"price" : "300",
"date" : null
}
], }`
I want to sum Abonos.price to get "500" (200+300)
I have this function in my laravel controller:
$result = Work::raw(function($collection){
return $collection->aggregate(array(
array('$unwind' => '$Abonos'),
array('$group' => array(
"_id" => '$_id',
"total" => array('$sum' => '$Abonos.price')
)),
));});
return $result;
But i get
[{"_id":"594a95cc8b85112444002925","total":0}
instead of
[{"_id":"594a95cc8b85112444002925","total":500}"
I would like to make $return = 500
So here is my attempt it did not work, I am not sure what I need to do, and the docs explaining this are just confessing.
so I am hoping someone on here knows how to do this.
Basic concept: I have stored the users latest profile picture ID in the users main profile collection
members collection
{
"_id" : ObjectId("aaaat656a464"),
"email" : "russell#ipet.xyz",
"personal" : {
"name" : {
"firstname" : "Russell",
"lastname" : "Harrower"
},
"profile_id" : ObjectId("333a0e2b7acebe9b869b1b0a")
},
"kst" : ObjectId("111111111111g")
}
Now I want to get that profile_id from members_media collection which _id = profile_id
This is the members_media collection
{
"_id" : ObjectId("333a0e2b7acebe9b869b1b0a"),
"data" : {
"url" : "https://scontent.xx.fbcdn.net/v/t1.0-1/11659354_1625058617782022_7822649569017662262_n.jpg?oh=6e2b74b33c1c4ea4cdc0094a1ae35e14&oe=59DF809A",
"type" : "facebook/image",
"date" : ISODate("2017-06-21T06:16:18.580Z"),
"profile_pic" : true
},
"uid" : ObjectId("111111111111g")
}
So here is my attempt
$check = $db->aggregate([
['$unwind'=> "$members_media"],
['$unwind' => ['path'=>"$members_media", 'includeArrayIndex'=>"arrayIndex"]],
['$lookup'=>
[
'from'=>"members_media",
'localField'=>"personal.profile_id",
'foreignField'=>"_id",
'as'=>"profilepic"
]
]
]);
However when I run the following it does not get the members collection
$db = static::db()->members;
/*$check = $db->findOne(['kst' => New MongoDB\BSON\ObjectId($_SESSION["ipet_user"]), 'personal'=>['$exists'=>true]],
['personal.profile_id' => 1]
);*/
$check = $db->aggregate([
['$lookup' =>
[
'from'=>"members_media",
'localField'=>"members.personal.profile_id",
'foreignField'=>"_id",
'as'=>"data"
]
],
['$match' =>
['members.kst' => New MongoDB\BSON\ObjectId($_SESSION["user"]), 'members.personal'=>['$exists'=>true]]
],
['$unwind' => ['path' => '$members_media.data', 'includeArrayIndex' => 'arrayIndex']],
['$project' =>
['members.profile_path'=>'$data.url']
]
]);
my result is
object(MongoDB\Driver\Cursor)#19 (9) { ["database"]=> string(4) "ipet" ["collection"]=> NULL ["query"]=> NULL ["command"]=> object(MongoDB\Driver\Command)#18 (1)
We can do like this
$resultData = $db->collectionName->aggregate([
['$lookup' => [
'from' => 'members_media',
'localField' => 'personal.profile_id',
'foreignField' => '_id',
'as' => 'profileDetail',
]]]);
$myData = array();
foreach($resultData as $result) {
//var_dump($result);
array_push($myData, $result);
}
print_r($myData);
If I have the following schema:
users = {
_id: MongoID
name: String,
email: String,
password: String,
online: Boolean, // Tells if user is online or not
friends: Array, // Storing _ids of friends
request: Array
}
Now, I want to get the name of all the friends which are online, sorted by name.
MongoDB database:
{
"_id" : ObjectId("572e43d71ccbdd080f00002b"),
"name" : "Bot 3",
"email" : "bot3#gmail.com",
"password" : "202cb962ac59075b964b07152d234b70",
"online" : false,
"friends" : [
ObjectId("572efe481ccbdd9c12000029")
],
"requests" : []
}
{
"_id" : ObjectId("572efe481ccbdd9c12000029"),
"name" : "Bot 4",
"email" : "bot4#gmail.com",
"password" : "202cb962ac59075b964b07152d234b70",
"online" : false,
"friends" : [
ObjectId("573074e81ccbddc816000029"),
ObjectId("572e43d71ccbdd080f00002b")
],
"requests" : [ ]
}
{
"_id" : ObjectId("573074e81ccbddc816000029"),
"name" : "Bot 5",
"email" : "bot5#gmail.com",
"password" : "202cb962ac59075b964b07152d234b70",
"online" : true,
"friends" : [
ObjectId("572efe481ccbdd9c12000029")
],
"requests" : [ ]
}
I want some query like:
var arr = db.users.find({"_id": ObjectId("572efe481ccbdd9c12000029")},
{"friends": 1, "_id": 0});
db.users.find({$in: arr["friends"]}, {"name": 1, "online": 1}).sort({"name": 1});
I tried this php code on the above example (not even close to requirement):
<?php
$cursor1 = $users->find(
array(
"_id" => new MongoID($id),
),
array(
"friends" => 1
)
);
$cursor1->next();
$doc1 = $cursor1->current();
$friend_arr = $doc1['friends'];
foreach($friend_arr as $f_id){
$cursor2 = $users->find(
array("_id" => new MongoID($f_id)),
array("name"=>1));
$cursor2->next();
$doc2 = $cursor2->current();
echo "<div>".$doc2['name']."</div>";
}
?>
But I know it does not satisfy the needs. That's why I am asking for help.
I am new to MongoDB with php.
Thank you so much in advance for any help.
Ok guys, I got the solution after some thinking.
$cursor1 = $users->find(
array(
"_id" => new MongoID($id),
),
array(
"friends" => 1
)
);
$cursor1->next();
$doc1 = $cursor1->current();
$friend_arr = $doc1['friends'];
$cursor2 = $users->find(
array(
"_id"=> array(
'$in' => $friend_arr
)
),
array(
"name" => 1
)
);
$cursor2->sort(array("name"=>1));
foreach($cursor2 as $doc2){
echo "<div>".$doc2['name']."</div>";
}
This code works properly. I tested it before posting.
I need to join more than two fields in two collections using aggregate $lookup. is it possible to join? please let me know if it is possible. Here i have two collections:
For Example:
"people" collections fields "city,state,country" in "country" collection fields "city_id,state_id,country_id", I want to join this three fields in following collections.
"People"
{
"_id" : 1,
"email" : "admin#gmail.com",
"userId" : "AD",
"userName" : "admin",
"city" : 1,
"state" : 1,
"country" : 1
}
"country"
{
"country_id" : 1,
"userId" : "AD",
"phone" : "0000000000",
"stateinfo":[{
"state_id" : 1,
"state_name" : "State1"
},{
"state_id" : 2,
"state_name" : "State2"
}
],
"cityinfo":[{
"city_id" : 1,
"city_name" : "city1"
},{
"city_id" : 2,
"city_name" : "city2"
}
]
}
This is probably a lot more simple than you think, considering that of course all of the "three" fields are contained within the one "country" document. So it's just a matter of doing the $lookup by "country_id" and then using the retrived content to populate the other fields.
var pipeline = [
{ "$lookup": {
"from": "country",
"localField": "country",
"foreignField": "country_id",
"as": "country"
}},
{ "$project": {
"email": 1,
"userId": 1,
"userName": 1,
"country": {
"$arrayElemAt": [
{ "$filter": {
"input": {
"$map": {
"input": "$country",
"as": "country",
"in": {
"country_id": "$$country.country_id",
"userId": "$$country.userId",
"phone": "$$country.phone",
"stateInfo": {
"$arrayElemAt": [
{ "$filter": {
"input": "$$country.stateInfo",
"as": "state",
"cond": { "$eq": [ "$$state.state_id", "$state" ] }
}},
0
]
},
"cityinfo": {
"$arrayElemAt": [
{ "$filter": {
"input": "$$country.cityinfo",
"as": "city",
"cond": { "$eq": [ "$$city.city_id", "$city" ] }
}},
0
]
}
}
}
},
"as": "country",
"cond": { "$eq": [ "$$country.userId", "$userId" ] }
}},
0
]
}
}}
]
db.people.aggregate(pipeline)
That should give you a result like:
{
"_id" : 1,
"email" : "admin#gmail.com",
"userId" : "AD",
"userName" : "admin",
"country" : {
"country_id" : 1,
"userId" : "AD",
"phone" : "0000000000",
"stateinfo": {
"state_id" : 1,
"state_name" : "State1"
},
"cityinfo": {
"city_id" : 1,
"city_name" : "city1"
}
}
So once the array is matched in by $lookup it all comes down to using $filter to do the matcing and $arrayElemAt to get the first match from each filtered array.
Since the outer array has "inner" arrays, you want to use $map for the "outer" source and apply $filter to each of it's "inner" arrays.
You can get more fancy with $let to get that "reduced" array content down to the returned sub-document and then just directly reference the resulting properties for an even "flatter" response, but the general concept of "matching" the array elements remains the same as above.
For a PHP structure translation:
$pipeline = array(
array(
'$lookup' => array(
'from' => 'country',
'localField' => 'country'
'foreignField' => 'country_id',
'as' => 'country'
)
)
array(
'$project' => array(
'email' => 1,
'userId' => 1,
'userName' => 1,
'country' => array(
'$arrayElemAt' => array(
array(
'$filter' => array(
'input' => array(
'$map' => array(
'input' => '$country',
'as' => 'country',
'in' => {
'country_id' => '$$country.country_id',
'userId' => '$$country.userId',
'phone' => '$$country.phone',
'stateInfo' => array(
'$arrayElemAt' => array(
array(
'$filter' => array(
'input' => '$$country.stateInfo',
'as' => 'state',
'cond' => array( '$eq' => array( '$$state.state_id', '$state' ) )
)
),
0
)
),
'cityinfo' => array(
'$arrayElemAt' => array(
array(
'$filter' => array(
'input' => '$$country.cityinfo',
'as' => 'city',
'cond' => array( '$eq' => array( '$$city.city_id', '$city' ) )
)
),
0
)
)
}
)
),
'as' => 'country',
'cond' => array( '$eq' => array( '$$country.userId', '$userId' ) )
)
),
0
)
)
)
)
);
$people->aggregate($pipeline);
You can usually check your PHP matches a JSON structure when you are working from a JSON example by dumping the pipeline structure:
echo json_encode($pipeline, JSON_PRETTY_PRINT)
And that way you cannot go wrong.
As another final note here, the process after the $lookup is done is quite "complex" even if very efficient. So I would advise that unless there is some need to take this aggregation pipeline further and actually "aggregate" something, then you are probably better off doing that "filtering" in client code rather than doing it on the server.
The client code to do the same thing is far less "obtuse" than what you need to tell the aggregation pipeline to do. So unless this "really" saves you a lot of bandwidth usage by reducing down the matched array, or indeed you if can just "lookup" by doing another query instead, then stick with doing it in code and/or do the seperate query.