Convert strings to MongoId in array of objects - mongodb

We have a collection which stores a list of sub objects:
{
_id: MongoID,
title: 'Some title',
items: [
{
sub_type: 'MongoId'
}
]
}
Annoyingly it turns out we've been storing the item.sub_type as both MongoId Objects and as strings.
I've created a query to find all the string versions: (I'm using a regex as some old items were just numbers instead of Ids, they can be ignored).
$query = array(
'items' => array(
'$elemMatch' => array(
'sub_type' => array(
'$type' => 2,
'$regex' => new MongoRegex('/^[a-f\d]{24}$/i')
)
)
)
);
$results = $mongo->collection->find( $query );
I'm now trying to come up with the best way to convert these to MongoIds. I can easily loop through them in PHP and update them, but this seems like a waste.
Is there a better way of doing this?

I just realised I didn't really understand the question. Here goes an update. This one should do it.
Using mongo console...
db.collection.find({}).forEach(function (x) {
x.items.forEach(function (y) {
y.sub_type = ObjectId(y.sub_type);
});
db.collection.save(x);
});

Related

Find query with and operator in PHP

Hi i am working on backend of web application & want to find the documents from mongodb database that contain key active_status with value set to both 1 & 2. With mongodb PHP i am confused of how to find with both parameters in single query.
My query was this:
$mongoDb = MongoDbConnector::getCollection("endusers");
$endUserData = $mongoDb->find(array('active_status' => 1, '$and' => array('active_status' => 2)));
I have to fetch the users whose active_status should be 1 & 2. The above query doesnt seems to work. What is the right one for that?
Thanks on advance for quick response.
You have $and the wrong way around. Both arguments need to be included:
$endUserData = $mongoDb->find(array(
'$and' => array(
array( 'active_status' => 1 )
array( 'active_status' => 2 )
)
));
And since that would only make sense when looking for both elements within an array element, then you should instead use $all, which is shorter syntax:
$endUserData = $mongoDb->find(array(
'active_status' => array( '$all' => array(1,2) )
));
I should add that unless you intend to match a document like this:
{ "active_status" => [1,2] }
The you do not in fact want $and at all, but rather you want $or or better yet $in for multiple possible values on the same field:
$endUserData = $mongoDb->find(array(
'active_status' => array( '$in' => array(1,2) )
));
This matches documents like this:
{ "active_status": 1 },
{ "active_status": 2 }

Find query MongoDB using CakePHP

The following is the json array I have in a collection called claims in MongoDB.
{
"xmllisting_id": "537f371fb2e380922fff0e2c",
"pharmacyfiles_id": "537f3402b2e380732aa6032d",
"claim": {
"MemberID": "097110330047532601",
"PatientShare": "0",
},
"modified": ISODate("2014-05-23T13:12:17.191Z"),
"created": ISODate("2014-05-23T13:12:17.192Z")
}
I need to find all claims with a specified MemberID.I have tried the following in CakePHP without any success.
$claims = $claimobj->find(
'all',
array(
'conditions' => array(
'claim' => array('MemberID' => '097110330047532601')
)
)
);
How can I do it?
Finding "nested" details in MongoDB usually requires "dot notation". Otherwise you are actually asking for an object that has "exactly" the key and "only" the key you are specifying to match. Which of course it does not, as there is more information there:
$claims = $claimobj->find(
'all',
array(
'conditions' => array(
'claim.MemberID' => '097110330047532601'
)
)
);
So the path is "claim.MemberID" and not 'claim' => array('MemberID' => '097110330047532601' ) as you have written.

With cakephp2.3 and mongoDB how to add array in a document?

how can I work with lists within the mongoDB?
For example, I have a document (class), and a list of students in this class, ie a subdocument (students).
What do not you, is to add more students to a class.
in Model:
public function salvar($name,$idClass){
$new = array(
"_id" => $idClass,
"student"=> array(
'idStudent' => new MongoId(),
'name' => $name));
return $this->save($new);
}
But when you add a new student, he is not working that way.
How to perform a $push update
The answer you've provided clarifies what you've been trying to do. However, the solution is a little convoluted. To perform a $push update with MongoDB, simply specify you want to perform a push update:
$this->Foo->save(array(
'_id' => $id,
'$push' => array(
"images" => array(
'id' => new MongoId(),
'name' => $name,
'size' => $size
)
)
);
You can use the mongoNoSetOperator property to achieve the same thing but
It's indirect, therefore not obvious
With multiple calls to save in the same request it can be confusing if it's not reset to $set if it's modified
It prevents using multiple operators in a single call.
There's more information about using different update operators in the documentation.
the answer to the question was:
public function salvar($name,$size,$id){
$this->mongoNoSetOperator = '$push';
// fixed array data structure
$susp = array(
"_id" => $id,
"images"=> array(
array(
'id' => new MongoId(),
'name' => $name,
'size' => $size
)
)
);
return $this->save($susp);
}
Note: Thank Garamon from github, by clear and objective answers.

MongoDB (via Fuelphp): Adding entries on an Array

I would appreciate the help here. For the purpose of this discussion I have an example here (I will use paste bin for the codes):
http://pastebin.com/VPuyKn6W
I am trying to produce this output:
http://pastebin.com/4iMLacRu
I understand that I need to use $push to make this work. But upon testing, it doesn't seem to do anything. I am following the instructions as prescribed in the docs, but instead of using $Id, I am using user_id for finding the document in the collection. Here is my model:
http://pastebin.com/QB94tbZn
Am I misunderstanding something, or I am not using the $push operator properly, or something to do on how I created the document?
After walking outside, I finally got my answer.
public static function create_mongo()
{
$data = array(
'user_id' => '123895',
'First_Name' => 'John',
'Last_name' => 'Doe',
'sites' => array(
array(
'title' => 'Sankaku Complex',
'site' => 'http://sankakucomples.com'
)
)
);
$db = Fuel\Core\Mongo_Db::instance();
$db->insert('test_collection',$data);
}
sites should be an array carrying an array variable.

MongoDB & PHP - Returning a count of a nested array

Imagine I have a MonogDB collection containing documents as follows:
{name: 'Some Name', components: {ARRAY OF ITEMS}}
How can I return the name and the count of items in components?
Do I have to use a map/reduce?
I am using PHP's Mongo extension.
EDIT: Snippet of current code in PHP (working) but I just want count of the components
$fields = array(
'name', 'components'
);
$cursor = $this->collection->find(array(), $fields);
$cursor->sort(array('created_ts' => -1));
if (empty($cursor) == true) {
return array();
} else {
return iterator_to_array($cursor);
}
Thanks,
Jim
You could use map-reduce or you could use a simple group query as follows. Since I am assuming that your name property is a unique key, this should work even though it isn't a reason that you'd normally use the group function:
db.test.group({
key: { name:true },
reduce: function(obj,prev) {
var count = 0;
for(k in obj.components)
count++;
prev.count = count;
},
initial: { count: 0}
});
You mentioned that you have an array of components, but it appears that you are storing components as an object {} and not and array []. That is why I had to add the loop in the reduce function, to count all of the properties of the components object. If it were actually an array then you could simply use the .length property.
In PHP it would look something like this (from the Manual):
$keys = array('name' => 1);
$initial = array('count' => 0);
$reduce =<<<JS
function(obj,prev) {
var count = 0;
for(k in obj.components)
count++;
prev.count = count;
},
JS;
$m = new Mongo();
$db = $m->selectDB('Database');
$coll = $db->selectCollection('Collection');
$data = $coll->group($keys, $initial, $reduce);
Finally, I would strongly suggest that if you are trying to access the count of your components on a regular basis that you store the count as an additional property of the document and update it whenever it changes. If you are attempting to write queries that filter based on this count then you will also be able to add an index on that components property.
You could use db.eval() and write the calculation in JavaScript.
Jim-
These are two separate operations; Unless you want to leverage PHP's count on the results you get which you would then do something like:
$m = new Mongo();
$db = $m->selectDB('yourDB');
$collection = $db->selectCollection('MyCollection');
$cursor = $collection->find(array(), array("name"=>1, "components"=>1));
foreach($cursor as $key){
echo($key['name'].' components: '.count($key['components']);
}
Ran across this today, If your using the new driver with aggregate you can do this in php, ( given this schema )
{name: 'Some Name', components: {ARRAY OF ITEMS}}
In PHP:
$collection = (new Client())->db->my_collection;
$collection->aggregate([
'$match' => ['name' => 'Some Name'],
'$group' => [
'_id' => null,
'total'=> ['$sum' => "\$components"]
]
]);
The trick here with PHP is to escape the $ dollar sign, this is basically what the mongo documentation says when using size or sum
https://docs.mongodb.com/manual/reference/operator/aggregation/size/
https://docs.mongodb.com/manual/reference/operator/aggregation/sum/
The problem I had is mongo puts fields in as "$field" and PHP doesn't like that at all because of the way it does variable interpolation. However, once you escape the $, it works fine.
I think for this particular case you'd need to do something similar but with $project instead of $group Like this
$collection = (new Client())->db->my_collection;
$collection->aggregate([
'$match' => ['name' => 'Some Name'],
'$project' => [
'name' => "\$name",
'total'=> ['$sum' => "\$components"]
]
]);
This is an old question but seeing as there is no answer picked, I'll just leave this here.