MongoDB Getting all documents that have the same foreign key - php

I have the following sets of data : supposing this is a 'category' collection, each category has a parent 'category' and belongs to a 'section' which is another collection :
{
"_id": ObjectId("5379e61b086d83de218b4568"),
"name": {
"en": "Inactive accounts"
},
"parent": ObjectId("5379e61b086d83de218b4568"),
"section": "ObjectId("5379d4f9086d835328a22fc8")
}
{
"_id": ObjectId("5379e61b086d83de218b4568"),
"name": {
"en": "second category"
},
"parent": 0,
"section": "ObjectId("5379d4f9086d835328a22fc8")
}
etc......
I'm trying to get all categories in the same section : here is my php code :
$categories = $db->categories->find(array('section' => new MongoId($section)));
In this case it returns {} an empty result, while if used findOne it returns one document.
$categories = $db->categories->findOne(array('section' => new MongoId($section)));
I'm not sure if I missed something, I may change foreign keys to strings instead of ObjectId('')s.

I solved it , I was processing the response incorrectly. the code above was working but 'find' function returns 'Cursor' while findOne return array.

Related

How to update mongodb document

Hi i'm really mongodb newbie.
I have a document like this:
{
"_id": ObjectId("53182e32e4b0feedb1dea751"),
"solutions": [
[
{
"solution": "Double Room Economy (Without Breakfast)",
"board": "Room Only",
"id": "HK-15501871",
"price": 5000,
"available": "1",
"CXL": "[]",
"unique": 0
},
{
"solution": "Double Room Economy (With Breakfast)",
"board": "Room Only",
"id": "HK-15501871",
"price": 4600,
"available": "1",
"CXL": "[]",
"unique": 1
},
{
"solution": "Double Room Economy (Room Only)",
"board": "Room Only",
"id": "HK-15501871",
"price": 5500,
"available": "1",
"CXL": "[]",
"unique": 2
}
]
]
}
And i need to update the field CXL inside the second array of solutions.
so solutions.1.CXL
This is how i take document:
$collection = $this->getCollection();
$query = array("_id"=>new MongoId($id));
$document = $collection->findOne($query);
now i need to update that field without touch the other.
How can i do?
Thanks!
SOLVED THANKS TO #Sammaye
i solved in this way:
$collection->update(
array('_id' => new MongoId('..')),
array('$set' => array('solutions.0.1.CXL' => 'something'))
);
Edit
To actually update by the first index then you can do:
$db->collection->update(
['_id' => new \MongoId($id)],
['$set' => ['solutions.0.1.CLX' => 'whatever']]
);
I misread the question in posting the information below:
So what you wanna update all CXL fields in the document (since you are only searching by top level document _id)?
That isn't possible without manually pulling this document out and iterating the subdocuments in the solutions field and then resaving it.
This is becausde there is currently no way of saying, "Update all that match"
This, however, is most likely the JIRA you would want to look for: https://jira.mongodb.org/browse/SERVER-1243
As long as you know you are going to update the second element then use the index of the array to do so. But that problem next. First you need the $set operator in order not to blow away your document and just set the field value:
db.collection.update(
{ _id: ObjectId("53182e32e4b0feedb1dea751") },
{ $set: { "solutions.0.1.CXL": [ 1, 2, 3 ] } }
)
If you just want to add to the array rather than replace the whole thing, then just use $push instead:
db.collection.update(
{ _id: ObjectId("53182e32e4b0feedb1dea751") },
{ $push: { "solutions.0.1.CXL": 4 } }
)
If you are paying attention to the notation, then you will notice that the array index values are present in the field to be updated. There is a very good reason for this, which can be read on the documentation for the positional $ operator.
The issue is that you have a nested array, which as the documentation refers to, causes a problem if you try to match items within that nested array. That problem is, if you try to use the "positional" operator to find the matched index of something you look for in a query, then it will contain the value of the first array index match that it finds.
In this case that would be your "top level" array and the "found" index is 0 and not 1 as you may expect.
Please be aware of this issue if you intend to use nested arrays.
You can update like this:
update({
_id: ObjectId("53182e32e4b0feedb1dea751"),
solutions.id: HK-15501871,
solutions.CLX: "Whatever!",")
},{
$set: {"comments.$.type": abc}
}, false, true
);
You may want to go through this once
http://docs.mongodb.org/manual/reference/method/db.collection.update/

Getting array element which has minimum value on a specific field on the element

I'm trying to remove an element from an array inside a collection. To remove the element, I have to look at the index (or date) field, and remove the one which has the lowest value. An example representation of my collection named "pages":
{
"_id": {
"$oid": "52e12df7e4b06e4ed65a554c"
},
"posts": [
{
"postId": {
"$oid": "52e12e5933a9fbec1100002d"
},
"date": 1390489177.267876,
"index": 1
},
{
"postId": {
"$oid": "52e12e5f33a9fb141800002c"
},
"date": 1390489183.277084,
"index": 2
}
],
"skillname": "Bilardo",
"skilltag": "Bilardo",
"currentIndex": 2
}
I need to remove this element from the posts array:
{
"postId": {
"$oid": "52e12e5933a9fbec1100002d"
},
"date": 1390489177.267876,
"index": 1
}
Whatever I would do, I could not manage to find the minimum "index" field in the array. The last php code I achieved to write is below:
$this->db->pages->find(array( 'skilltag' => 'Bilardo'), array('posts.index' => 1))->sort( array("posts.index" => -1));
I'm not willing to use "index" fields and also "currentIndex" field. But I put them anyways in case of not being able to work on "date" field which has timestamp values.
The above code returns an array with only one element which is an array that holds 2 elements:
{
"postId": {
"$oid": "52e12e5933a9fbec1100002d"
},
"date": 1390489177.267876,
"index": 1
},
{
"postId": {
"$oid": "52e12e5f33a9fb141800002c"
},
"date": 1390489183.277084,
"index": 2
}
Isn't there an aggregation function which does filtering on inside the array? I could not find any aggregation that returns the minimum value. I only found aggregation to return values between min and max values which must be given by the user -- which does not work in my case.
Are the embedded objects in your posts array field always ordered by their date and index, ascending? Based on your schema, I would assume that new posts are added to the document via the $push update operator.
If the element to remove is always at the front of the array, you could very easily use $pop to remove it.

searching for a way to filter results by a field defined in another linked table using Doctrine and MongoDB

I need to find a way to filter results from a MongoDB database, based on fields found in the current table, and a field found in another table which is linked to the current table. Tables format:
{
"_id": ObjectId("51af256a0da4dd7804000007"),
"enddate": ISODate("2013-06-14T21:00:00.0Z"),
"main": false,
"name": "name 1",
"photo": "image-1.jpg",
"site": {
"$ref": "Sites",
"$id": ObjectId("51ac538c5f06751414bd9f98"),
"$db": "local"
},
"startdate": ISODate("2013-04-30T21:00:00.0Z")
}
{
"_id": ObjectId("51d3d5b9caa8213b12e92c5e"),
"sitefeatured": false,
"sitename": "a",
"sitephoto": "aa.jpg",
"siteurl": "aaaa.com/"
}
what i`m trying to do in ->where clause is to search in the string formed by this.name and this.site.sitename:
my problem is that i don't know / didn't find how to reference to property sitename of the site object
$entries = $dm
->getRepository($sTable)
->createQueryBuilder('o')
->where('function() { return ( (this.name + this.site.sitename).toLowerCase().indexOf("'.$_GET['sSearch'].'".toLowerCase()) !== -1 ) ? true : false }')
->sort($aColumns[$_GET['iSortCol_0']], $_GET['sSortDir_0'])
->limit($_GET['iDisplayLength'])
->skip($_GET['iDisplayStart'])
->getQuery()
->execute();
Is there anything like SQL join that i didn't came across, or what's the solution?

MongoDB & PHP get only product that matches barcode

I have this JSON and you can see under products i have barcodes for each product what i want to do is only get the information that matches the product barcode
{
"company": "village",
"logo": "http:\/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/villagetop.png",
"products": [
{
"barcode": "236690091",
"name": "Weekday",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "12.50",
"discount": "1.50"
},
{
"barcode": "236690092",
"name": "Weekend",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "13.50",
"discount": "1.60"
},
{
"barcode": "236690093",
"name": "Gold Class",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "13.50",
"discount": "1.60"
}
],
"store_name": "movies"
}
for example If i hit 236690091 I only what the database (MongoDB) to return
"barcode": "236690091",
"name": "Weekday",
"logo-URL": "http: \/\/www.incard.com.au\/newsite\/template\/images\/movieticket\/4cinemas\/ticketpic1.png",
"price": "12.50",
"discount": "1.50"
not every product.
This is what I have tried
public function getbarcode($barcode)
{
// select a collection (analogous to a relational database's table)
$collection = $this->db->movies->products;
// find everything in the collection
$cursor = $collection->find(array("barcode" =>"{$barcode}"));
$test = array();
// iterate through the results
while( $cursor->hasNext() ) {
$test[] = ($cursor->getNext());
}
//Print Results
print json_encode($test);
}
You can't do this. MongoDB will always return the full document and will not allow you to return only a nested part that you want to search against. I would suggest to split out the products into its own collection, and then add the company info to each product. This will also circumvent the 16MB document limit in case you have lots of products for each company.
Without changing your schema, the following code should work:
public function getbarcode($barcode)
{
$products = array();
$collection = $this->db->movies->products;
foreach( $collection->find( array( 'products.barcode' => $barcode ) ) as $item )
{
foreach( $item->products as $product )
{
if ( $product['barcode'] == $barcode )
{
$products[] = $item;
}
}
}
return $products;
}
You can't do this the way you want. MongoDB will return only whole documents or some fields from the documents (if you specify them in query). You can't return only values that are matched by your query.
You can create a separate collection that will only hold the products objects (with a reference to a collection that holds the company data) where you can directly query for the product data you want.
If you can't / won't create another collection you can find all documents that have the product with specified barcode and filter them out using PHP.
For this second approach your query should be:
$collection->find(array("products.barcode" =>"{$barcode}"),
array('products' => 1));
With this query you're reaching into objects and returning only documents that have the barcode you are looking for.
Also, in this query you will only return the products property from your document and not the whole document. The products property will contain all the child objects, not just the one you are trying to find.
In your while loop you should check the values and filter them out properly.

Mongodb nested array search

document structure example is:
{
"dob": "12-13-2001",
"name": "Kam",
"visits": {
"0": {
"service_date": "12-5-2011",
"payment": "40",
"chk_number": "1234455",
},
"1": {
"service_date": "12-15-2011",
"payment": "45",
"chk_number": "3461234",
},
"2": {
"service_date": "12-25-2011",
"payment": "25",
"chk_number": "9821234",
}
}
}
{
"dob": "10-01-1998",
"name": "Sam",
"visits": {
"0": {
"service_date": "12-5-2011",
"payment": "30",
"chk_number": "86786464",
},
"1": {
"service_date": "12-15-2011",
"payment": "35",
"chk_number": "45643461234",
},
"2": {
"service_date": "12-25-2011",
"payment": "20",
"chk_number": "4569821234",
}
}
}
In PHP i want to list all those "visits" information (and corresponding "name" ) for which payment is less than "30".
I want to print only the visits with "payment" < "30" not others. Is such query possible, or do i have to get entire document first using search and then use PHP to select such visits??
In the example document, the "payment" values are given as strings which may not work as intended with the $lt command. For this response, I have converted them to integers.
Wildcard queries are not possible with MongoDB, so with the given document structure, the key (0,1,2, etcetera) of the sub-document must be known. For instance, the following query will work:
> db.test.find({"visits.2.payment":{$lt:35}})
However,
> db.test.find({"visits.payment":{$lt:35}})
Will not work in this case, and
> db.test.find({"visits.*.payment":{$lt:35}})
will also not return any results.
In order to be able to query the embedded "visits" documents, you must change your document structure and make "visits" into an array or embedded documents, like so:
> db.test2.find().pretty()
{
"_id" : ObjectId("4f16199d3563af4cb141c547"),
"dob" : "10-01-1998",
"name" : "Sam",
"visits" : [
{
"service_date" : "12-5-2011",
"payment" : 30,
"chk_number" : "86786464"
},
{
"service_date" : "12-15-2011",
"payment" : 35,
"chk_number" : "45643461234"
},
{
"service_date" : "12-25-2011",
"payment" : 20,
"chk_number" : "4569821234"
}
]
}
Now you can query all of the embedded documents in "visits":
> db.test2.find({"visits.payment":{$lt:35}})
For more information, please refer to the Mongo documentation on dot notation:
http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29
Now on to the second part of your question: it is not possible to return only a conditional sub-set of embedded documents.
With either document format, it is not possible to return a document containing ONLY the sub-documents that match the query. If one of the sub-documents matches the query , then the entire document matches the query, and it will be returned.
As per the Mongo Document "Retrieving a subset of fields"
http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
We can return parts of embedded documents like so:
> db.test2.find({"visits.payment":{$lt:35}},{"visits.service_date":1}).pretty()
{
"_id" : ObjectId("4f16199d3563af4cb141c547"),
"visits" : [
{
"service_date" : "12-5-2011"
},
{
"service_date" : "12-15-2011"
},
{
"service_date" : "12-25-2011"
}
]
}
But we cannot have conditional retrieval of some sub documents. The closest that we can get is the $slice operator, but this is not conditional, and you will have to first know the location of each sub-document in the array:
http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
In order for the application to display only the embedded documents that match the query, it will have to be done programmatically.
You may try:
$results = $mongodb->find(array("visits.payment" => array('$lt' => 30)));
But i don't know if it will work since visits is an object. BTW judging from what you posted it could be transfered to array (or should since numerical property names tends to cause confusion)
try - db.test2.find({"visits.payment":"35"})

Categories