How to query mongoDB with the "find" - php

I have this problem. I have this dataBase in mongoDB:
{
"_id" : ObjectId("585fe33d3c63b4a81e00002b"),
"class" : [
{
"name" : "class 1",
"people" : [
{
"id" : "58596",
"name" : "mark",
},
{
"id" : "45643",
"name" : "Susan",
},
{
"id" : "85952",
"name" : "Loris",
}
},
{
"name" : "class 2",
"people" : [
{
"id" : "58456",
"name" : "Sissi",
},
{
"id" : "45643",
"name" : "Susan",
}
]
}
]
}
I use php and I would like to know the names of the class with a specific name inside and save them in an array.
For example if I choose Susan i would like to have an array with ["class 1" , ["class 2"].
I have used findOne but this time i need to use find.

You can make use of MongoDB aggregation framework.
Here is a query which will give you the date in the desired form. However, I believe that there will be some better and efficient way to handle this but this is what I came up with.
db.collection.aggregate([
{"$unwind":"$class"},
{"$unwind":"$class.people"},
{"$match": {
"class.people.name":"Susan"
}
},
{"$group":{
"_id":"$class.people.name",
"classes":{"$push":"$class.name"}
}
}
])
Result :-
{ "_id" : "Susan", "classes" : [ "class 1", "class 2" ] }
Try to $match before first $unwind operation to avoid the unnecessary results in the pipeline before $unwind.
Refer Aggregation Pipeline Optimization for improved performance.

Related

Querying two MongoDB collections with PHP, first returns correct result, but second is empty

I run two MongoDB queries from PHP, one on a first collection (named "products"), second on another collection (named stats).
Some datas of the two collections :
{
"_id" : "20711122",
"labels_hierarchy" : [
"en:pgi"
],
"languages_hierarchy" : [
"en:french"
],
"labels_prev_hierarchy" : [
"en:pgi"
],
"languages" : {
"en:french" : 5
},
"countries_tags" : [
"en:france"
],
"purchase_places_debug_tags" : [],
"photographers_tags" : [
"tacite"
]
}
Other collection :
{
"_id" : "7613035010550",
"purchases" : [
{
"date" : ISODate("2017-04-15T14:15:00.000Z"),
"coords" : {
"lon" : 43.729604,
"lat" : 1.416017
},
"metar" : {},
"quantity" : 1,
"price" : 2.31
},
{
"date" : ISODate("2017-05-02T16:23:00.000Z"),
"coords" : {
"lon" : 43.722862,
"lat" : 1.415837
},
"metar" : {},
"quantity" : 6,
"price" : 12
},
{
"date" : ISODate("2017-05-02T18:32:00.000Z"),
"coords" : {
"lon" : 46.307353,
"lat" : 3.28937
},
"metar" : {},
"quantity" : 2,
"price" : 5
}
],
"rates" : [
{
"value" : 5
},
{
"value" : 4
},
{
"value" : 5
},
{
"value" : 2
}
]
}
As u see, collections are different but the "_id" key...
When i run a query on the first collection (products), everything is ok, but the second with the same "_key" returns empty results, here's the trace of the two queries (made with executeQuery() of MongoDB::Driver) :
<pre><code>Interrogation de la collection : stats
Avec le filtre :
{"id":"7613035010550"}
<pre><code>Interrogation de la collection : products
Avec le filtre :
{"id":"7613035010550"}
Results :
{"ean":"7613035010550","title":"Eau Min\u00e9rale Naturelle","image":"https:\/\/static.openfoodfacts.org\/images\/products\/761\/303\/501\/0550\/front_fr.11.400.jpg","brands":"Vittel","categories":["Boissons","Eaux","Eaux min\u00e9rales","Eaux min\u00e9rales naturelles","Boissons non sucr\u00e9es"],"quantity":"1.5 l."}
As u can see, only the first query returns results, not the second...
Any idea of this curious behaviour ?
Thx 4 help,
JL
I'm just stupid or tired...
Second query was execute with "id" key but not "_id" key... So, always returns empty results...
Just take another 4 shots coffee and next...
JL

MongoDB aggregate sub-array as group _id

I'm having some trouble using the MongoDB aggregation framework to count event types in my database. How do I calculate the sum of the value.count field for each unique 3rd index of the _id.val field?
The basic structure of my data looks like:
{ _id: { evt: "click", val: [ "default", "125", "311", "1" ] }, value: { count: 1 } }
{ _id: { evt: "click", val: [ "default", "154", "321", "2" ] }, value: { count: 2 } }
{ _id: { evt: "click", val: [ "default", "192", "263", "1" ] }, value: { count: 4 } }
The values in the val field denote ["type","x","y","time"], respectively.
I'm trying to extract the 3rd index, or time value of the _id.val key. The output I'm looking to achieve:
1: 5
2: 2
I've been trying to do it via this PHP:
$ops2 = array(
array(
'$match' => $q2
),
array(
'$group' => array(
'_id' => array(
'evt' => '$_id.evt',
'time' => '$_id.val.3'
),
'count' => array('$sum' => '$value.count' )
)
)
);
But it doesn't appear to like the 3 index in the group array
The data you are working with looks like it has come as the output of a mapReduce operation already, since it has that specific "_id" and "value" structure that mapReduce prodcues. As such you may be better off going back to the logic of how that process is implemented and follow the same to just extract and total what you want, or at least change it's output form to this:
{
_id: {
evt: "click",
val: { "type": "default", "x": "125", "y": "311", "time": "1" }
},
value: { count: 1 }
},
{
_id: {
evt: "click",
val: { "type": "default", "x": "154", "y": "321", "time": "2" }
},
value: { count: 2 }
},
{
_id: {
evt: "click",
val: { "type": "default", "x": "192", "y": "263", "time": "1" }
},
value: { count: 4 }
}
As the problem is that the aggregation framework "presently" lacks the ability to address the "indexed" position of an array ( real "non-associative" array and not PHP array ) and would always return null when you try to do so.
Lacking the ability to go back to the original source or mapReduce operation, then you can write a mapReduce operation on this data to get the expected results ( shell representation since it's going to be a JavaScript anyway ):
db.collection.mapReduce(
function() {
emit({ evt: this._id.evt, time: this._id.val[3] }, this.value.count)
},
function(key,values) {
return Array.sum(values)
},
{ out: { inline: 1 } }
)
Which returns typical mapReduce output like this:
{
"_id" : {
"evt" : "click",
"time" : "1"
},
"value" : 5
},
{
"_id" : {
"evt" : "click",
"time" : "2"
},
"value" : 2
}
If you were able to at least transform the current output collection to the form suggested at first above, then you would run with the aggregation framework like this instead ( again common representaion ):
{ "$group": {
"_id": {
"evt": "$_id.evt",
"time": "$_id.val.time"
},
"count": { "$sum": "$value.count" }
}}
Which of course would yield from the altered data:
{ "_id" : { "evt" : "click", "time" : "2" }, "count" : 2 }
{ "_id" : { "evt" : "click", "time" : "1" }, "count" : 5 }
In future releases of MongoDB, there will be a $slice operator which allows the array handling, so with your current structure you could do this instead:
{ "$group": {
"_id": {
"evt": "$_id.evt",
"time": { "$slice": [ "$_id.val", 3,1 ] }
},
"count": { "$sum": "$value.count" }
}}
Which allows picking of the "third" index element from the array, albeit that this will of course still return an "array" as the element like this:
{ "_id" : { "evt" : "click", "time" : [ "2" ] }, "count" : 2 }
{ "_id" : { "evt" : "click", "time" : [ "1" ] }, "count" : 5 }
So right now, if you can change your initial mapReduce output then do it. Either to the form as shown here or just work with modifications to the initial query to get the end result you want here. Modifying to the recommened form will at least allow the .aggregate() command to work as is shown in the second example here.
If not, then mapReduce is still the only way at present for writing, as shown in the "first" example.
At first, I think you may have something wrong in your understanding of Mongo...Because each document in mongo should have its unique _id, to identify itself from others. So I have add a _id to each object, and change your origin "_id" field to "data". Now the structure is:
/* 1 */
{
"_id" : "ubLrDptWvJE7LZqDF",
"data" : {
"evt" : "click",
"val" : [ "default", "125", "311", "1" ]
},
"value" : {
"count" : 1
}
}
/* 2 */
{
"_id" : "C2QCEhvCsp3xG6EKZ",
"data" : {
"evt" : "click",
"val" : [ "default", "154", "321", "2" ]
},
"value" : {
"count" : 2
}
}
/* 3 */
{
"_id" : "bT72z7gMKoyX5JfHL",
"data" : {
"evt" : "click",
"val" : [ "default", "192", "263", "1" ]
},
"value" : {
"count" : 4
}
}
I am not sure how to do this query in PHP, Because I only know a little PHP...... But I could give you an example of using aggregation in Javascript, its code and output are as follows:
Here are some useful link: using mongo in PHP
I wish it can help you solve your problem perfectly :-)

MongoDb return only one element from the array

So my collection looks like this:
{
"_id" : ObjectId("52722429d874590c15000029"),
"name" : "Bags",
"products" : [{
"_id" : ObjectId("527225b5d87459b802000029"),
"name" : "Prada",
"description" : "Prada Bag",
"points" : "234",
"validDate" : 1382562000,
"link" : "dasdad",
"code" : "423423424",
"image" : null
}, {
"_id" : ObjectId("5272307ad87459401a00002a"),
"name" : "Gucci",
"description" : "Gucii bag",
"points" : "2342",
"validDate" : 1383170400,
"link" : "dsadada",
"code" : "2342",
"image" : null
}]
}
and I want to get only the product with the _id 527225b5d87459b802000029, I tried this:
$this->find(array(
'_id' => new \MongoId('52722429d874590c15000029'),
'products._id' => new \MongoId('527225b5d87459b802000029')
));
But it returns the entire array for that collection, and I only want one...can this be done in mongo?
As mentioned in comments, you have to add a projection, and more precisely an $elemMatch. No need to use the aggregation framework in that case.
Example :
find( { _id: 1, "products._id": 4 }, { products: { $elemMatch: { _id: 4 } } } ).pretty()

How to search a string in inner array using mongodb?

How to search value in multidimensional array,
for example I want to search example keyword in the following data in mongodb
I used to fetch all data from command
>db.info.find()
{
"_id" : ObjectId("4f74737cc3a51043d26f4b90"),
"id" : "12345",
"info" : [
{
"sno" : 1,
"name" : "ABC",
"email" : "abc#example.com"
},
{
"sno" : 2,
"name" : "XYZ",
"email" : "xyz#example.com"
},
{
"sno" : 3,
"name" : "XYZ",
"email" : "xyz#demo.com"
},
{
"sno" : 4,
"name" : "ABC",
"email" : "abc#demo.com"
},
{
"sno" : 5,
"name" : "Rohan",
"email" : "rohan#example.com"
}
]
}
Now, to find data having example I used command
>db.info.find({"info.email":"example"})
and it gives
{
"_id" : ObjectId("4f74737cc3a51043d26f4b90"),
"id" : "12345",
"info" : [
{
"sno" : 1,
"name" : "ABC",
"email" : "abc#example.com"
},
{
"sno" : 2,
"name" : "XYZ",
"email" : "xyz#example.com"
},
{
"sno" : 3,
"name" : "XYZ",
"email" : "xyz#demo.com"
},
{
"sno" : 4,
"name" : "ABC",
"email" : "abc#demo.com"
},
{
"sno" : 5,
"name" : "Rohan",
"email" : "rohan#example.com"
}
]
}
But I want only 3 out of 5 sub rows like
{
"_id" : ObjectId("4f74737cc3a51043d26f4b90"),
"id" : "12345",
"info" : [
{
"sno" : 1,
"name" : "ABC",
"email" : "abc#example.com"
},
{
"sno" : 2,
"name" : "XYZ",
"email" : "xyz#example.com"
},
{
"sno" : 5,
"name" : "Rohan",
"email" : "rohan#example.com"
}
]
}
Rohan, MongoDB always returns the whole document that you are searching on. You can't just make it return the array elements in which your keyword was found. If you want to do that, then you need to make sure all all embedded documents in the "info" field are in their own collection. And that might mean that you need to link them back to the original document in your "info" collection. Perhaps something like:
{
"sno" : 1,
"name" : "ABC",
"email" : "abc#example.com"
"info_id" : "12345",
},
Alternatively, you can of course do post-processing in PHP to obtain only the rows that you want.
Perhaps this is a good idea?
http://php.net/manual/en/class.mongoregex.php
I tried Map Reduce Function and it works on this type of problems the code is something like that:
Write a map function
map=function ()
{
filter = [];
this.info.forEach(function (s) {if (/example/.test(s.email)) {filter.push(s);}});
emit(this._id, {info:filter});
}
Write a reduce function
reduce=function(key, values) { return values;}
MapReduce Function
res=db.info.mapReduce(map,reduce,{out:{inline:1}})
And The Output look likes:
"results" : [
{
"_id" : ObjectId("4f9a2de0ea4a65c3ab85a9d3"),
"value" : {
"info" : [
{
"sno" : 1,
"name" : "ABC",
"email" : "abc#example.com"
},
{
"sno" : 2,
"name" : "XYZ",
"email" : "xyz#example.com"
},
{
"sno" : 5,
"name" : "Rohan",
"email" : "rohan#example.com"
}
]
}
}
],
"timeMillis" : 1,
"counts" : {
"input" : 3,
"emit" : 3,
"reduce" : 0,
"output" : 3
},
"ok" : 1,
Now you can find your search data from
printjson(res.results)
Did you try $ (projection)?
db.info.find({"info.email":"example"}, {"info.email.$":1})
document

Insert data in nested array in mongodb [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
MongoDB updating fields in nested array
I have data like:
{
"_id" : ObjectId("4f855061dd53351011000b42"),
"act_mgr" : [{ "sales" : {"agent" : ["rohan#walkover.in" ], "last_interacted" : "rohan#walkover.in" } } ],
"email" : "aman#asasas.com", "name" : "Aman",
"sales" : [{"sno" : 1, "message" : "description","status" : "open"},{"sno" : 12,"message" : "assad","status" :"open"}]
}
I want to add new agent and update last_interacted in act_mgr:sales something like that
"act_mgr" : [{ "sales" : {"agent" : ["rohan#walkover.in","abc#walkover.in" ],
"last_interacted" : "abc#walkover.in" } } ]
Also if I add new act_mgr like developer then it would be like
"act_mgr" : [{ "sales" : {"agent" : ["rohan#walkover.in","abc#walkover.in" ], "last_interacted" : "abc#walkover.in" } },
{ "developer" : {"agent" : ["newdeveloper#walkover.in" ], "last_interacted" : "newdeveloper#walkover.in" } } ]
I dont know how to add these fields
You can update the embedded "sales" document inside of the "act_mgr" array with the following update statement:
> db.sales.update({"act_mgr.sales.last_interacted":"rohan#walkover.in"}, {$push:{"act_mgr.$.sales.agent":"abc#walkover.in"}, $set:{"act_mgr.$.sales.last_interacted":"abc#walkover.in"}})
> db.sales.find().pretty()
{
"_id" : ObjectId("4f855061dd53351011000b42"),
"act_mgr" : [
{
"sales" : {
"agent" : [
"rohan#walkover.in",
"abc#walkover.in"
],
"last_interacted" : "abc#walkover.in"
}
}
],
"email" : "aman#asasas.com",
"name" : "Aman",
"sales" : [
{
"sno" : 1,
"message" : "description",
"status" : "open"
},
{
"sno" : 12,
"message" : "assad",
"status" : "open"
}
]
}
>
You can add the embedded document containing the "developer" information to the array like so:
> db.sales.update({"_id" : ObjectId("4f855061dd53351011000b42")}, {$push:{"act_mgr":{ "developer" : {"agent" : ["newdeveloper#walkover.in" ], "last_interacted" : "newdeveloper#walkover.in" } }}})
> db.sales.find().pretty()
{
"_id" : ObjectId("4f855061dd53351011000b42"),
"act_mgr" : [
{
"sales" : {
"agent" : [
"rohan#walkover.in",
"abc#walkover.in"
],
"last_interacted" : "abc#walkover.in"
}
},
{
"developer" : {
"agent" : [
"newdeveloper#walkover.in"
],
"last_interacted" : "newdeveloper#walkover.in"
}
}
],
"email" : "aman#asasas.com",
"name" : "Aman",
"sales" : [
{
"sno" : 1,
"message" : "description",
"status" : "open"
},
{
"sno" : 12,
"message" : "assad",
"status" : "open"
}
]
}
>
The documentation on the $push and $set modifiers may be found in the "Updating" documentation:
http://www.mongodb.org/display/DOCS/Updating
More information on creating and updating embedded documents with Mongo db may be found in the documentation titled "Dot Notation (Reaching into Objects)"
http://www.mongodb.org/display/DOCS/Dot+Notation+%28Reaching+into+Objects%29
Information on updating embedded documents using the "$" positional operator may be found in the "The $ positional operator" section of the "Updating" documentation.
http://www.mongodb.org/display/DOCS/Updating#Updating-The%24positionaloperator
A word of caution: It is generally more common to have embedded documents all match the same structure, so that individual embedded documents may be referenced more easily. Your "sales" array is a good example of this; each embedded document contains the same keys, "sno", "message", and"status"
However, the embedded documents inside your "act_mgr" array contain different keys; the first contains "sales", and the second contains "developer". Instead, maybe consider the following structure:
"act_mgr" : [
{
"title" : "sales",
"agent" : [
"rohan#walkover.in",
"abc#walkover.in"
],
"last_interacted" : "abc#walkover.in"
},
{
"title": "developer",
"agent" : [
"newdeveloper#walkover.in"
],
"last_interacted" : "newdeveloper#walkover.in"
}
]
Now, each embedded documents contain the same keys, "title", "agent", and "last_interacted".
You could update sub-documents with the following command.
> db.sales.update({"act_mgr.title":"sales"}, {$push:{"act_mgr.$.agent":"abc#walkover.in"}, $set:{"act_mgr.$.last_interacted":"abc#walkover.in"}})
Hopefully this will allow you to make the updates that you need to, and perhaps give you some food for thought regarding schema design. Good luck!

Categories