How to keep orders in MongoDB? - mongodb

In my MongoDB document I have object like this
[_id] => MongoId Object (
[$id] => 52a46b44aabacb5c218b4567
)
[results] => Array (
[http://google.com] => Array (
[position] => 1
[data] => 42672
)
[http://bing.com] => Array (
[position] => 2
[data] => 9423
)
[http://yandex.com] => Array (
[position] => 3
[data] => 5513
)
)
I would like to change data parameter in "bing.com" from 9423 to for instance 300. Moreover, I have to keep order of the sites. It have to looks like this
[_id] => MongoId Object (
[$id] => 52a46b44aabacb5c218b4567
)
[results] => Array (
[http://google.com] => Array (
[position] => 1
[data] => 42672
)
[http://bing.com] => Array (
[position] => 2
[data] => 300
)
[http://yandex.com] => Array (
[position] => 3
[data] => 5513
)
)
Is this achievable in Mongo?

The reordering of fields issue has been fixed as of MongoDB v2.5.2 (2.6 release). Having said that one way you can avoid the issue completely is having results as an array instead of a (sub)document. Also note you should not use "." as part of the key name either.
With 2.4, with the following you will see there is reodering in the case of _id=1 (subdocument) but not in the case of _id=2 (array).
$document = array("_id" => 1, "results" => array('http://google.com' => array('position' => 1, 'data' => 42672),
'http://bing.com' => array('position' => 2, 'data' => 9423),
'http://yandex.com' => array('position' => 3, 'data' => 5513)));
$coll->insert($document);
$document = array("_id" => 2, "results" => array(array('site' => 'http://google.com', 'data' => 42672),
array('site' => 'http://bing.com', 'data' => 9423),
array('site' => 'http://yandex.com', 'data' => 5513)));
$coll->insert($document);
$coll->update(array("_id" => 1), array('$set'=>array("results.http://bing.com.data"=>300)));
$coll->update(array("_id" => 2, 'results.site' => 'http://bing.com'), array('$set'=>array('results.$.data'=>300)));

I've included examples below using the mongo shell for clarity, but the PHP equivalent should be straightforward to work out.
I notice you originally modelled your list of sites as an embedded document, however the order of fields within an embedded document is currently not guaranteed to be preserved so you should instead use an array.
Additionally, you cannot use field names with embedded dots (.) in MongoDB so you should not plan to store urls as field names (see: Field name restrictions).
In order to find an element in an array you need to search by a value (not a field name) so your schema should look more like:
{
_id: ObjectId("52a46b44aabacb5c218b4567"),
results: [
{
site: 'http://google.com',
position: 1,
data: 42762
},
{
site: 'http://bing.com',
position: 2,
data: 9423
},
{
site: 'http://yandex.com',
position: 3,
data: 5513
}
]
}
Assuming the array site elements are unique, you can use the positional operator $ to find and update the matching embedded document in place.
For example, to perform your update of the "bing.com" data value:
db.sites.update(
// Match criteria
{
_id:ObjectId("52a46b44aabacb5c218b4567"),
'results.site':'http://bing.com'
},
// Update
{ $set: {
'results.$.data': 300 }
}
)
In MongoDB 2.4+ you have the option of pushing to a sorted array which could also be a useful approach to maintaining your array in sorted order when you add new entries.
It's worth noting that if you plan to store many (i.e. thousands) of items in an array this can impose a significant performance penalty due to document growth and the complexity of updating large arrays.

I am pretty sure that (as every other DBMS) you can't and should't rely on records orders.
Instead I would advice you to add index (on position, i.e. db.people.ensureIndex( { position: 1 } )) and query your record sorted by that field, i. e.: db.collection.find().sort( { position: 1 } )

Related

PHP MongoDB Search in Sub-array

Data stored in collection as below
[field_1] => Array
(
[fields] => Array
(
[0] => MongoInt64 Object
(
[value] => 1233
)
[1] => MongoInt64 Object
(
[value] => 1234
)
)
)
I need to search 1234 in field.
I used below code in php to search
$param = array('field_1.fields.$' => 1234);
But this is not working
You need to use the $in query criteria, to find elements within an array
$cursor = $collection->find(array("field_1.fields" => array('$in' => array("1234"))));
This will find all items that have 1234 within "fields"
$in doc: https://docs.mongodb.org/v3.0/reference/operator/query/in/

Convert strings to MongoId in array of objects

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);
});

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.

Querying array elements with Mongo

How can I query the smoothies that have apple in them? (below is a collection with 3 documents)
_id => 1
name => 'best smoothie'
ingredients => Array
(
[0] => apple
[1] => raspberry
[2] => orange
[3] => banana
)
_id => 2
name => 'summer smoothie'
ingredients => Array
(
[0] => lemon
[1] => mint
)
_id => 3
name => 'yogurt smoothie'
ingredients => Array
(
[0] => apple
[1] => blueberry
)
If you simply run the below query MongoDB is smart enough to figure out what you're trying to do.
{ ingredients: "apple" }
Mongo will see that ingredients is a list and only return documents that contain "apple" some where in that list.
From the documentation:
Note: Fields containing arrays match conditional operators, if only one
item matches.
Therefore, the following query:
db.collection.find( { field: { $gt:0, $lt:2 } } );
Will match a document that contains the
following field:
{ field: [-1,3] }
Why do people write scalable applications for smoothies?
db.find({"ingredients":{$in: "apple"}});