I have some mongoDB documents with the following structure:
[_id] => MongoId Object (
[$id] => 50664339b3e7a7cf1c000001
)
[uid] => 1
[name] => Alice
[words] => 1
[formatIds] => Array (
[0] => 1
[1] => 4
)
What I want to do is find all documents which has the value 1 in formatIds[ ]. I think it's possible to accomplish that. How can I do it in PHP?
UPDATE
Thanks for the help. It works fine now. Here is how i wrote the search,
$id=$_POST['id'];
$query = array('formatIds'=> "{$id}" );
$result = $stations_table->find($query); //where $stations_table = $db->stations;
MongoDB treats queries on array values the same way as queries on standard values, as per the docs.
Querying for array('formatIds' => 1) should work.
As MongoDB "transform" array into multi value for a same key :
$cursor = $mongo->base->collection->find(array('formatIds' => 1));
With correct definition of you mongo object and setting base/collection string.
It depends on whether you only want the documents or values that match your query or not.
If you don't mind pulling out the entire array and then searching client side for it you can of course use:
$c = $db->col->find(array('formatIds' => 1))
Since a 1-dimensional array in MongoDB can be searched like a single field.
But now to get only those that match your query since the above query will pick out all:
$db->command(array(
'aggregate' => 'col',
'pipeline' => array(
array('$unwind' => "$formatIds"),
array('$match' => array('formatIds' => 1)),
array('$group' => array(
'_id' => '$_id',
'formats' => array('$push' => '$formatIds'))
)
)
)
Use something like that.
This would give you a result of the _id being the _id of the document and a field of formats with only rows of the value 1 in the array.
Related
Is there a way to format the $this->find('all') array into the $this->find('list') in the view? The reason I ask is so that I can pass that new array into the form helper options and then use the $this->find('all') array to build some additional things I need?
Array (
[0] => Array ( [School] => Array ( [id] => 0 [name] => Nature [address] => 112 Main [max_students] => 25 [application_level] => 5 ) )
[1] => Array ( [School] => Array ( [id] => 1 [name] => Math [address] => 112 Smith [max_students] => 25 [application_level] => 0 ) )
[2] => Array ( [School] => Array ( [id] => 2 [name] => Art [address] => 112 Lane [max_students] => 25 [application_level] => 0 ) )
)
So this is the array I get when I do a find('all'). I want to build the array so it looks like:
Array (
[0] => 'Nature'
[1] => 'Math'
[2] => 'Art'
)
This is usually done by the $this->find('list') function. Though the reason I want the whole array is because I need to add the application_level into $this->Form->input() function. This is because I need to add the option of class with the application level attached so I show only the shows with the application level based on the previous selection.
EDIT: Can't I just do $this->find('list', [insert parameters here?]);? I just don't understand how you set up the additional parameters?
If your query isn't overly complicated and isn't going to return a excessive number of results, just run it twice (once for find all and once for find list).
Find all, list, first, whatever are all the same in terms of the paramaters you pass in. E.g.:
$this->Model->find('all', array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
));
... you literally replace all with list. In your case, probably easiest to do it twice, once for each. Like this:
$parameters = array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
);
$alldata = $this->Model->find('all', $parameters);
$listdata = $this->Model->find('list', $parameters);
Otherwise, you can loop through it and populate your own list:
$list = array();
foreach($findall as $row) {
$id = $row['id'];
$name = $row['name'];
$list[$id] = $name;
}
$this->set('listdata', $list);
Short answer to your question is that there's no quick, easy way to select all and list from the same query, but you can re use your parameters (conditions, order etc) by passing them in as predefined arrays, or populate your own list.
An alternative answer to creating the results formatted like find('list') from results from find('all') using CakePHP's hash utility:
//where $data is the result of find all
App::uses('Hash', 'Utility');
$ids = Hash::format($data, array('{n}.Model.id'), '{0}'); //ids in an array.
$names = Hash::format($data, array('{n}.Model.name'), '{0}'); //names in an array
$dataAsList = array_combine($ids, $names);
To improve on kai's answer. The Hash class has a method called combine that can do what you're trying to do in only one line
$list = Hash::combine($data,'{n}.Model.id','{n}.Model.name');
the $list will be a flat array like data from find('list');
My data like this:
Array
(
[_id] => MongoId Object
(
[$id] => 51a6fca3f348aca011000000
)
[first_name] => Phan
[last_name] => Chuong
[interests] => Array
(
[0] => football
[1] => swimming
[2] => PHP
[3] => music
)
)
I want edit value PHP to PHP1 or set PHP to null. Please help me how can i do it? Thanks all!
You could use the MongoCollection::update() method like this:
// $c is your MongoCollection Object
$updatedata = array('$set' => array("interests.2" => "PHP1"));
$id = new MongoID('51a6fca3f348aca011000000')
$c->update(array("_id" => $id), $updatedata);
The update method needs two arrays, the first one will tell the DB which Object to update ( In this example where id = our id, always use the MongoID Object, always!), the second array defines what to update, note that you can get to values in nested arrays wit the dot like above. $set is an Field Update Operator, read more on them here:
http://docs.mongodb.org/manual/reference/operator/update-field/
Or you could just get the entire Array, change it, and save it back.
$cursor = $c findOne("_id",4cb30f560107ae9813000000);
$cursor['interests'][2] = 'whatever';
$c->update($cursor);
I'm querying my database using aggregation and pipeline, with two separate queries:
$groups_q = array(
'$group' => array(
'_id' => '$group_name',
'total_sum' => array('$sum' => 1)
)
);
$statuses_q = array(
'$group' => array(
'_id' => '$user_status',
'total_sum' => array('$sum' => 1)
)
);
$data['statuses'] = $this->mongo_db->aggregate('users',$statuses_q);
$data['groups'] = $this->mongo_db->aggregate('users',$groups_q);
And I'm getting what I want:
Array
(
[statuses] => Array
(
[result] => Array
(
[0] => Array
(
[_id] => Inactive
[total_sum] => 2
)
[1] => Array
(
[_id] => Active
[total_sum] => 5
)
)
[ok] => 1
)
[groups] => Array
(
[result] => Array
(
[0] => Array
(
[_id] => Accounting
[total_sum] => 1
)
[1] => Array
(
[_id] => Administrator
[total_sum] => 2
)
[2] => Array
(
[_id] => Rep
[total_sum] => 1
)
)
[ok] => 1
)
)
I don't want to query my database twice. Is there is a better way to do it?
How can I accomplish it with one query? Should I use $project operator?
You can't use a single aggregate() to do two grouped counts with your desired result format. Once the data has been grouped the first time you no longer have the details needed to create the second count.
The straightforward approach is to do two queries, as you are already doing ;-).
Thoughts on alternatives
If you really wanted to get the information in one aggregation query you could group on both fields and then do some manipulation in your application code. With two fields in the group _id, results are going to be every combination of group_name and status.
Example using the mongo shell :
db.users.aggregate(
{ $group: {
_id: { group_name: "$group_name", status: "$status" },
'total_sum': { $sum: 1 }
}}
)
That doesn't seem particularly efficient and lends itself to some convoluted application code because you have to iterate the results twice to get the expected groupings.
If you only wanted the unique names for each group instead of the names + counts, you could use $addToSet in a single group.
The other obvious alternative would be to do the grouping in your application code. Do a single find() projecting only the group_name and status fields, and build up your count arrays as you iterate the results.
I'm in a situation where I need to be able to run a direct mongodb query from inside of PHP, and am having troubles with the execute() function.
The following code will correctly execute and return a result from the database:
$m = new MongoClient();
$db = $m-><dbName>;
print_r($db->execute('db.<collection>.count()'));
However, if I replace count() with find() I get the following result:
Array
(
[retval] => Array
(
[value] => DBQuery: <dbName>.<collection>-> undefined
)
[ok] => 1
)
Why does one of these queries work, and the other fail? And how can I overcome this issue? I've thought about writing something that will convert a MongoDB query into the necessary array format for the PHP Mongo library to work with, but that would be a lot of work that I don't want to go through.
This is the same problem that was brought up here: How to access MongoDB profile in PHP? - but no answers were given at that time.
This question: PHP MongoDB execute() locking collection utilizes the execute() method with find() and they say it works for them, but I'm not sure how.
Update:
So I'd updated mongo in pecl, but that didn't solve anything. However, I also did a YUM update, and that provided a separate package update for php-pecl-mongo-1.2.12-1.el6.x86_64 that brought me to 1.3.4-1.
Now, $db->execute('db.<collection>.find()) returns something...but not at all what I expect. Instead of returning a MongoCursor object instead an Array is returned, and while it has a retval field, there's no actual information in there from the query performed. It looks like this:
Array
(
[retval] => Array
(
[_mongo] => Array
(
[slaveOk] =>
[host] => EMBEDDED
)
[_db] => Array
(
[_mongo] => Array
(
[slaveOk] =>
[host] => EMBEDDED
)
[_name] => test
)
[_collection] => Array
(
[_mongo] => Array
(
[slaveOk] =>
[host] => EMBEDDED
)
[_db] => Array
(
[_mongo] => Array
(
[slaveOk] =>
[host] => EMBEDDED
)
[_name] => test
)
[_shortName] => hits
[_fullName] => test.hits
)
[_ns] => test.hits
[_query] => Array
(
)
[_fields] =>
[_limit] => 0
[_skip] => 0
[_batchSize] => 0
[_options] => 0
[_cursor] =>
[_numReturned] => 0
[_special] =>
)
[ok] => 1
)
As you can see, there's not actually anything from the database there: how do I get to my actual rows?
$m = new MongoClient();
$db = $m-><dbName>;
print_r($db->execute('db.<collection>.toArray()'));
Suppose your databse name is test and collection name is foo .
print_r($m->test->execute('return db.foo.find().toArray()'));
I had the same problem, this is my solution using execute:
$m = new MongoClient();
$db = $m-><dbName>;
print_r($db->execute('return { count : db.<collection>.count() }'));
My result:
Array
(
[retval] => Array
(
[count] => 10
)
[ok] => 1
)
How can I get all the keys from within an associative array in a mongodb collection using
the PHP driver.
Document:
[_id] => 1
[campaigns] => Array (
[4e3b924d18153] => Array (
[name] => Campaign One,
[flag] => 1,
),
[4e3b924d18154] => Array (
[name] => Campaign Two,
[flag] => 1,
),
[4e3b924d18155] => Array (
[name] => Campaign Three,
),
)
I need to get any flag fields from within the associative array campaigns.
Flag may not necessarily exist.
Basically, I want to know which campaigns have the flag set.
I can use the following query, but it will return only the flag key for the first campaign
array('_id' => array('$in' => $projectIds)), array('campaigns.0.flag')
I would like to return the flag index for any campaign that is exists in.
if you need to do that with php the following lines should help you:
$flaged_compaigns = array();
foreach ($campaigns as $compaign->$data) {
if (isset($data['flag']) and $data['flag']) {
$flaged_compaigns[$compaign] = $data;
}
}
You can do it when run query in db.
Init mongo and get your collection.
Run find with the following conditions:
array('_id' => array('$in' => $projectIds), 'flag' => array('$exists' => true)).
One drawback you can't use index by this field 'flag' in exists in versions 1.8.x or older.
See http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24exists .