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 ));
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);
}
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")
)
)
));
});
I'm using MongoChef to construct an aggregation pipeline command that performs a $match, then a $group then a $project.
The following code produces the correct output and is confirmed working in MongoDB itself:
db.collection_name.aggregate(
[
{
$match: {
":energy_mon_id" : 9
}
},
{
$group: {
"_id" : {
"Date" : "$:date"
},
"avg_voltage_a" : {
"$avg" : "$:voltage_a"
},
"avg_voltage_b" : {
"$avg" : "$:voltage_b"
},
"avg_voltage_c" : {
"$avg" : "$:voltage_c"
}
}
},
{
$project: {
"avg_volt" : {
"$add" : [
"$avg_voltage_a",
"$avg_voltage_b"
]
}
}
},
{
$match: {
}
}
]
);
The output that is produced is this (note that the avg_volt values are integers):
{ "_id" : { "Date" : "2016-06-06" }, "avg_volt" : 779 }
{ "_id" : { "Date" : "2016-06-08" }, "avg_volt" : 779 }
Now the issue I'm faced with is getting this to run correctly in PHP.
My code in PHP is below:
$collection = $this->db->testdb->collection_name;
$aggrCommand = array(
array(
'$match' => array( ":energy_mon_id" => 9)
),
array(
'$group' => array(
"_id" => array(
"Date" => "$:date"
),
"avg_voltage_a" => array(
'$avg' => "$:voltage_a"
),
"avg_voltage_b" => array(
'$avg' => "$:voltage_b"
),
"avg_voltage_c" => array(
'$avg' => "$:voltage_c"
),
)
),
array(
'$project' => array(
"avg_volt" => array(
'$add' => ["$avg_voltage_a","$avg_voltage_b" ]
)
)
)
);
$list = $collection->aggregate( $aggrCommand);
The error I get is for this very last line when I try to submit a GET request via postman:
Slim Application Error
The application could not run because of the following error:
Details
Type: MongoDB\Driver\Exception\RuntimeException
Code: 16554
Message: $add only supports numeric or date types, not String
File: C:\wamp\www\DRM\vendor\mongodb\mongodb\src\Operation\Aggregate.php
Line: 168
This makes no sense at all since the output in MongoDB is an integer value, and I can't spot any errors in my conversion to PHP.
One thing to note is that my field names do actually contain a colon at the front (its not a mistake), but I don't think that is the issue here.
I'm really stumped with this, can someone provide any advice on how to figure this issue out? The output from my MongoDB command is clearly not a string, yet in PHP it says the values of avg_voltage_a and avg_voltage_b are strings. This makes no sense at all.
Try with single quotes in your "$add" statement to prevent PHP interpreting "$avg_voltage_a" as a variable :
'$add' => ['$avg_voltage_a','$avg_voltage_b' ]
I would like to filter the Categories embedded Array to get only those which have a parent key.
{
"_id": ObjectId("5737283639533c000978ae71"),
"name": "Swiss",
"Categories": [
{
"name": "Management",
"_id": ObjectId("5738982e39533c00070f6a53")
},
{
"name": "Relations",
"_id": ObjectId("5738984a39533c000978ae72"),
"parent": ObjectId("5738982e39533c00070f6a53")
},
{
"name": "Ambiance",
"_id": ObjectId("57389bed39533c000b148164")
}
]
}
I've tried with the find but without success.
After some research it seems that it can be done via the aggregation command but I don't like the way it works, I would prefer to use only the find command.
Also, I'm asking myself if in term of performances it wouldn't be better to store each Categories in a new collection, would it be ?
Edit, I would like to get something like this as find output :
[
{
"name": "Relations",
"_id": ObjectId("5738984a39533c000978ae72"),
"parent": ObjectId("5738982e39533c00070f6a53")
}
]
The optimal way to do is in MongoDB 3.2 using the aggregation framework. All you need is project your documents and use the $filter operator to return a subset of the "Categories" array that match your criteria, but to do this you will need to use $ifNull operator give a "default" value to the "parent" field in all those sub-documents where that field is missing then use the $ne in your cond expression which determine where a give element should be included in the subset.
db.collection.aggregate([
{ "$project" : {
"_id": 0,
"Categories": {
"$filter": {
"input": "$Categories",
"as": "catg",
"cond": {
"$ne": [
{ "$ifNull": [ "$$catg.parent", false ] },
false
]
}
}
}
}}
])
From version 3.0 backwards, you need a different approach. Instead you need to use the $map operator to return a give element if it matches your criteria or false then use the $setDifference operator to filter out all those element in the returned array which are equal to false. Of course $setDifference is fine as long as the data being filtered is "unique".
db.collection.aggregate([
{ "$project" : {
"_id": 0,
"Categories": {
"$setDifference": [
{ "$map": {
"input": "$Categories",
"as": "catg",
"in": {
"$cond": [
{ "$ne": [
{ "$ifNull": [ "$$catg.parent", false ] },
false
]},
"$$catg",
false
]}
}
},
[ false ]
]
}
}}
])
Translation in PHP gives:
db.collection.aggregate(
array(
array("$project" => array(
"_id" => 0,
"Categories" => array(
"$filter" => array(
"input" => "$Categories",
"as" => "catg",
"cond" => array(
"$ne" => array(
array("$ifNull" => array("$$catg.parent", false),
false
)
)
)
)
))
)
)
And something this:
db.collection.aggregate(
array(
array("$project" => array(
"_id" => 0,
"Categories" => array(
"$setDifference" => array(
"$map" => array(
"input" => "$Categories",
"as" => "catg",
"in" => array(
"$cond" => array(
"$ne" => array(
array("$ifNull" => array( "$$catg.parent", false ) ),
false
),
"$$catg",
false
)
),
array(false)
)
)
))
)
)
As a solution according to above mentioned description please try executing following query
db.mycoll.find({Categories:{$elemMatch:{parent:{$exists:true}}}},
{Categories:{$elemMatch:{parent:{$exists:true}}}})
The above example uses $elemMatch operator to filter elements in an embedded document.
I have the following code in PHP to connect to my MongoDB instance:
$connection_string = "mongodb://XXXXXXX:XXXXXX#XXXXX:XXX/myDB";
$mongo_or = new Mongo($connection_string);
$db_or = $mongo_or->selectDB("myDB");
# Pick a collection
$collection_or = $db_or->myCollection;
$findArray = array('$and' => array('user.uid' => 1, 'friend_id' => 2));
$findArray2 = array('$and' => array('user.uid' => 2, 'friend_id' => 1));
$collection_or->remove($findArray);
$collection_or->remove($findArray2);
This never removes any results - even know the content is still there it never get's removed.
if you want the good structure for a $and query just json_decode this :
{
"$and" : [{
"$or" : [{
"civility" : 2
}, {
"civility" : 3
}]
}]
}