PHP Mongo Aggregate exception: pipeline element 0 is not an object - php

I'm trying to get the number of users with the same email address.
I've followed the php documentation for MongoCollection Aggregate. I've also compared my query to the others on stackoverflow. As far as I can tell my pipeline matches the one in the examples. And yet I get the error:
exception: pipeline element 0 is not an object
Which I presume is the $group element, which is an object (once converted to the json format mongo uses, isn't it?
$pipeline = array(
array(
'$group' => array(
'_id' => array( 'email' => '$email' ),
'uniqueIds' => array( '$addToSet' => '$_id' ),
'count' => array( '$sum' => 1 )
)
),
array(
'$match' => array(
'count' => array( '$gt' => 1 )
)
)
);
$options = array(
'allowDiskUse' => true
);
$results = $mongo->users_collection->aggregate( $pipeline, $options );
What have I done wrong? I've even tried running the query from php.net, I of course expected no results, but it resulted in the same error!?
EDIT so turns out it's when I allowDiskUse, but if I remove the $options from the function I get an error telling me to use it.
Thanks

I found a solution to this
$results = $mongo->command(array(
'aggregate' => 'users_collection',
'pipeline' => $pipeline,
'allowDiskUse' => true
));
It seems to me that because php allows two methods of doing this (from php.net)
public array MongoCollection::aggregate ( array $pipeline [, array $options ] )
public array MongoCollection::aggregate ( array $op [, array $op [, array $... ]] )
It is seeing my $options array as another operation $op. Maybe this is to do with the version of Mongo Module we're using or something along those lines, but changing to $mongo->command() works as a workaround.

Related

PHP - How to dynamically add array to Object request

I am doing Jira REST API calls and I am wondering how I can dynamically add more than one component to the components field using REST API in PHP. I have the following code, works when I set it static, but not sure how to do it dynamically.
Example of static component set:
$data = array(
'fields' => array(
'project' => array(
'key' => $rowAnswers["Key"]
),
'summary' => $rowAnswers["Summary"],
'description' => $rowAnswers["Description"],
'issuetype' => array(
'name' => $rowAnswers["IssueType"]
),
'components' => array(
array(
"name" => "component1"
),
array(
"name" => "component2"
)
)
),
);
My array that I want to replace the static content with:
$components = explode(",", $rowAnswers["Components"]);
$arr = array();
foreach($components as $value){
$array = array("name"=>$value);
array_push($arr,$array);
}
Replacing
'components' => array(
array(
"name" => "component1"
),
array(
"name" => "component2"
)
)
with
'components' => [
$arr
]
doesn't work, I get:
"{"error":false,"error_msg":"","data":"{\"errorMessages\":[],\"errors\":{\"components\":\"expected Object\"}}"}"
I see on an api call to get a request it looks like this:
[components] => Array
(
[0] => stdClass Object
(
[name] => component1
)
[1] => stdClass Object
(
[name] => component2
)
)
But I am unsure how to transform an array into this type of object or request in PHP. Calling with PHP-cURL and json_encoding the data it sends.
Thanks in advance!
you need to decode your json as associative array by setting the second parameter to true
check out the json_decode
assoc
When TRUE, returned objects will be converted into associative arrays.
To fix this I had to do the following:
When creating the array from the DB:
$components = explode(",", $rowAnswers["Components"]);
$arr = array();
foreach($components as $value){
$array = json_decode(json_encode(array("name"=>$value)), FALSE);
array_push($arr,$array);
}
Then to set the component in the request:
'components' => $arr
Thanks

MongoDB Nested $or query

Example of a Mongo Entry:
array(
'name' => 'blog one',
'blogCategory' => array(
'displayAndLightMeasurement' => '1',
'LEDAndDisplayTestInstrument' => '0'
)
);
A Query like this works fine:
$blogInfoRaw = $collection->find(array('blogCategory' => array('displayAndLightMeasurement' => '1')));
When I try to '$or' query like this:
$blogInfoRaw = $collection->find(array('$or' => array('blogCategory' => array('displayAndLightMeasurement' => '1')),array('blogCategory' => array('LEDAndDisplayTestInstrument' => '1'))));
I get this error:
$or requires nonempty array
What am I doing wrong?
You really meant to use "dot notation" to reference the embedded fields:
$blogInfoRaw = $collection->find(
array(
'$or' => array(
array( 'blogCategory.displayAndLightMeasurement' => '1' ),
array( 'blogCategory.LEDAndDisplayTestInstrument' => '1')
)
)
);
Otherwise the notation you are using implies that the "only" elements present in the embedded level are those that you specify. This is not true since there are multiple keys. So "dot notation" solves this problem by referencing the distinct keys.
PHP array notation does not help here, but the $or needs to be a wrapping "real" array as in [] also.
Issuing a json_encode often helps when comparing to the official MongoDB examples.

PHP ObjectId group a number of Ids

I have a two collections one of all the people I am following and another of what they have been posting on social networking sites like Twitter and Facebook.
The following collection has a subarray of the _id of the feed collection of each user which each status has the word owner and that has the ObjectId that the owner which is the same as the following key. Here is an example.
'_id' => new MongoId("REMOVED"),
'following' =>
array (
'0' => 'ObjectId("53bf464ee7fda8780c8b4568")',
'1' => 'ObjectId("53b00ab5e7fda8304b8b4567")',
),
'owner' => new MongoId("53b9ea3ae7fda8863c8b4123"),
and in the feed you will see that the following.0 status below
array (
'_id' => new MongoId("REMOVED"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
While I can loop through one by one, I can't for some reason do an $or search. I am not quite understanding how I loop through the following array and add it to the search query before I ran the query.
collection = static::db()->feed;
$where=array( '$or' => array(array('owner' => new MongoId($following.0)))));
$feed = $collection->find($where);
return $feed;
now I understand I will somehow have to loop the $where=array( '$or' => array(array('owner' => new MongoId($following.0))))); But I am just not 100% sure how to do this.
Update
As per the answer below I had to edit the array that was returned - now I have only got this working manually and can't seem to get the PHP script to do it.
Answer Returns
Array ( [owner] => Array ( [$in] => Array ( [0] => new MongoId("53bf464ee7fda8780c8b4568") [1] => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
Correct:
Array ( "owner" => Array ( '$in' => Array ( "0" => new MongoId("53bf464ee7fda8780c8b4568"), "1" => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
I am not sure how else to get this to work.
current PHP
$collection = static::db()->following;
$following = $collection->findOne(array ('owner' => new MongoId($_SESSION['user_information'][0]['_id'])));
$follow = $following['following'];
$collection = static::db()->feed;
$where=array("owner" => array( '$in' =>$follow));
print_r($where);
$feed = $collection->find($where);
print_r($feed);
return $feed;
I have fixed a small issue with the collection and now the return array shows
Array ( [owner] => Array ( [$in] => Array ( [0] => MongoId Object ( [$id] => 53bf464ee7fda8780c8b4568 ) [1] => MongoId Object ( [$id] => 53b00ab5e7fda8304b8b4567 ) ) ) )
However, I still can't get it to return the feed like this one:
array (
'_id' => new MongoId("53bf4667e7fda8700e8b4567"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
I am presuming here that this is just a PHPism in the way things are displayed and that your following array is an actual array and not a hash/map, which would generally look like this in a JSON representation:
{
"following": [
ObjectId("53bf464ee7fda8780c8b4568"),
ObjectId("53b00ab5e7fda8304b8b4567"),
],
"owner": ObjectId("53b9ea3ae7fda8863c8b4123"),
}
In which case the "following" is already an actual array, and if you just want to .find() all the "feed" items for the people you are following, then you just pass that to the $in operator for your query selection:
$where = array( "owner" => array( '$in' => $following ) );
$feed = $collection->find($where);
return $feed;
The returned cursor will only contain results from the feed where the "owner" is present in your "following" array from the other collection item.
Watch this code:
$list = array(new MongoId(), new MongoId, new MongoId());
$doc = array( "owner" => array( '$in' => $list ));
echo json_encode( $doc, JSON_PRETTY_PRINT );
Despite how this serializes for JSON by this method the equivalent JSON is:
{
"owner": {
"$in": [
ObjectId("53bf8157c8b5e635068b4567"),
ObjectId("53bf8157c8b5e635068b4568"),
ObjectId("53bf8157c8b5e635068b4569")
]
}
}
That is how the BSON will serialize and is the correct query.
(Answer added on behalf the question author to move it to the answer space).
The issue was fixed when I used the following:
var_dump(iterator_to_array($feed));

Mongo distinct sort query using PHP driver

Having some trouble issuing a simple query from the PHP Mongo driver.
This is the shell query:
db.testing.distinct('summary.type').sort();
According to the documentation, I can do a db->command to use the distinct query. Here is what I've tried:
$all_types = $db->command(
array(
"distinct" => "summary.type",
"key" => "summary.type"
)
);
var_dump($all_types);
But rather than getting back the data I expect, I only get a cursor with indexes like stats, nscanned and nscannedObjects
What am I doing wrong?
Thanks,
ns
Commands don't return a MongoCursor object, but always just one document. Just like with findOne(). In this returned document, the values array element in the returned array contains an array of all your results:
<?php
$m = new Mongo; $c = $m->demo->pubs; $c->drop();
$c->insert( array( 'name' => 'Betsy Smith', 'city' => 'London' ) );
$c->insert( array( 'name' => 'London Tavern', 'city' => 'London' ) );
$c->insert( array( 'name' => 'Lammars', 'city' => 'Manchester' ) );
$c->insert( array( 'name' => 'Weatherspoons', 'city' => 'Coventry' ) );
$r = $m->demo->command( array(
'distinct' => 'pubs',
'key' => 'city',
'query' => array( 'name' => array( '$ne' => 'Weatherspoons' ) )
) );
var_dump( $r['values'] );
?>
Which returns:
array(2) {
[0] =>
string(6) "London"
[1] =>
string(10) "Manchester"
}
The result you're looking for is actually in the values key of the command's response. If you look at the entire output of var_dump(), you'll see keys for values, stats (which you referred to), and ok.
On the JavaScript console, the collection's distinct() helper function actually handles the unwrapping for you, which you can see when viewing the source:
> db.testing.distinct
function (keyString, query) {
var res = this._distinct(keyString, query);
if (!res.ok) {
throw "distinct failed: " + tojson(res);
}
return res.values;
}
Also, I believe you have an error in the command being issued through the PHP driver. The distinct option should refer to the collection name, and key should refer to the field name. See the Distinct docs for more information on that.

get mongodb _id object after upsert with php

is it possible to get the new/updated _id after the query?
example code:
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$mongodb->db->collection->update( $key, $data, array( 'upsert' => true ) );
$key is not holding the new/old _id object and i assume that $data will not either because its just an instruction.
Yes -- It is possible using a single query.
MongoDB includes a findAndModify command that can atomically modify a document and return it (by default it actually returns the document before it's been modified).
The PHP drivers don't include a convenient method for this on the collection class (yet -- check out this bug), but it can still be used (note that my PHP is terrible, so I may very well have made a syntax error in the following snippet):
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$result = $mongodb->db->command( array(
'findAndModify' => 'collection',
'query' => $key,
'update' => $data,
'new' => true, # To get back the document after the upsert
'upsert' => true,
'fields' => array( '_id' => 1 ) # Only return _id field
) );
$id = $result['value']['_id'];
Just in case someone stumbles across this question like I did, Mongo will actually modify the input array when you call MongoCollection->save(); - appending the id to the end.
So, if you call:
$test = array('test'=>'testing');
mongocollection->save($test);
echo $test['_id'];
You will have the mongo id for that object.
I ran into this issue and worked around it by querying back the _id after the upsert. I thought I'd add some of my findings in case they're useful to anyone who comes here searching for info.
When the upsert results in a new document being created in the collection, the returned object contains the _id (here's a print_r of an example):
Array
(
[updatedExisting] => 0
[upserted] => MongoId Object
(
[$id] => 506dc50614c11c6ebdbc39bc
)
[n] => 1
[connectionId] => 275
[fsyncFiles] => 7
[err] =>
[ok] => 1
)
You can get the _id from this:
$id = (string)$obj['upserted'];
However, if the upsert resulted in an existing document being updated then the returned object does not contain _id.
Give this a shot :
function save($data, $id = null) {
$mongo_id = new MongoId($id);
$criteria = array('_id' => $mongo_id);
// Wrap a '$set' around the passed data array for convenience
$update = array('$set' => $data);
$collection->update($criteria, $update, array('upsert' => true));
}
So lets say the passed $id is null, a fresh MongoId is created, otherwise it just converts the existing $id to a MongoId object.
Hope this helps :D
The update method returns an array with the ID of the document UPSERTED:
Array
(
[ok] => 1
[nModified] => 0
[n] => 1
[err] =>
[errmsg] =>
[upserted] => MongoId Object
(
[$id] => 5511da18c8318aa1701881dd
)
[updatedExisting] =>
)
You can also set fsync to true in an update/upsert, to get the _id returned to the object that has been passed to the update.
$save = array ('test' => 'work');
$m->$collection->update(criteria, $save, array('fsync' => true, 'upsert' => true));
echo $save['_id']; //should have your _id of the obj just updated.

Categories