projection in php mongodb - mongodb

I have a collection with document like
{
"_id": ObjectId("5b4dd622d2ccda10c00000f0"),
"Code": "Route-001",
"Name": "Karan Nagar - Jawahar Nagar",
"StopsDetails": [
{
"StopId": "cb038446-bbad-5460-79f7-4b138024968b",
"Code": "Stop- 001",
"Name": "Lane market",
"Fee": "600"
},
{
"StopId": "2502ce2a-900e-e686-79ea-33a2305abf91",
"Code": "Stop-002",
"Name": "City center",
"Fee": "644"
}
],
"StopsTiming" :
[
....
]
}
I want to fetch all embedded documents "StopDetails" only. I am trying to tweak below lines of codes but I am not able get what to write in $query "StopsDetails" in order to fetch only StopsDetails embedded document.
Please help !!!
$query = ['_id' => new MongoDB\BSON\ObjectID($this->id),
'StopsDetails' => ];
try
{
$cursor = $this->collection->find($query);
}
catch (Exception $e)
{
}
return $cursor->toArray();

You need to use projection in the second parameter of the query
$query = ['_id' => new MongoDB\BSON\ObjectID($this->id)]
$projection = ['StopsDetails' => true]
$cursor = $this->collection->find($query, $projection);
which is similar to the javascript query
db.collection.find({ '_id': ObjectId }, { 'StopDetails': 1 })

YourModel.findOne({_id:yourId},function(err,result){
console.log(result); //will return all data
console.log(result.StopsDetails); //will retrun your stop details
}

If you want to fetch specific embedded fields in MongoDB you can use projection
Projection
Try it mongodb playground
db.col.find({"_id" : ObjectId("5b4dd622d2ccda10c00000f0")},{"StopsDetails" : 1});

Related

Return an array element of an aggregation in an MongoDB Atlas (4.2) trigger function

So I am currently testing with returning an array element of a an aggregation in an MongoDB Atlas (4.2) trigger function:
exports = function(changeEvent) {
const collection = context.services.get(<clusterName>).db(<dbName>).collection(<collectionName>);
var aggArr = collection.aggregate([
{
$match: { "docType": "record" }
},
..,
{
$group: {
"_id": null,
"avgPrice": {
$avg: "$myAvgPriceFd"
}
}
}
]);
return aggArr;
};
Which outputs:
> result:
[
{
"_id": null,
"avgPrice": {
"$numberDouble": "18.08770081782988165"
}
}
]
> result (JavaScript):
EJSON.parse('[{"_id":null,"avgPrice":{"$numberDouble":"18.08770081782988165"}}]')
As you can see this is returned as one object in an array (I then intend to use the avgPrice value to update a field in a document in the same collection). I have tried to extract the object from the array with aggArr[0] or aggArr(0) - both resulting in:
> result:
{
"$undefined": true
}
> result (JavaScript):
EJSON.parse('{"$undefined":true}')
or by using aggArr[0].avgPrice as per this solution which fails with:
> error:
TypeError: Cannot access member 'avgPrice' of undefined
> trace:
TypeError: Cannot access member 'avgPrice' of undefined
at exports (function.js:81:10(163))
at function_wrapper.js:5:30(18)
at <eval>:13:8(6)
at <eval>:2:15(6)
Any pointers are most welcome because this one has me stumped for now!
I had the same problem, and figured it out. You have to append the .toArray() function to the aggregation call, where you have.
collection.aggregate(pipeline_steps).toArray()
Here's an example:
const user_collection = context.services
.get("mongodb-atlas")
.db("Development")
.collection("users");
const search_params = [
{
"$search": {
"index": 'search_users',
"text": {
"query": value,
"path": [
"email", "first_name", "last_name"
],
"fuzzy":{
"prefixLength": 1,
"maxEdits": 2
}
}
}
}
];
const search_results = await user_collection.aggregate(search_params).toArray();
const results = search_results
return results[0]
Here's the documentation showing how to convert the aggregation to an array.

can't update DB using put request in express.js

I'm trying to update the languages_known through post request to the mongodb collection using the body of the request but its not updating i'm newbie any help would be really appreciated.
mongodb document :
{
"_id": {
"$oid": "5d63e81c342987154cdc698e"
},
"name": "anjana",
"email": "anju#gmail.com",
"age": "22",
"languages_known": [
"Bengali",
"Kannada"
],
"phone": "684684684846"
}
server code :
app.put('/students/add',function(req,res){
var myobj={
$set:{_id:require('mongodb').ObjectID(req.body.id),languages_known:req.body.remove}
}
db.collection('students').updateOne({languages_known:req.body.add},myobj,function(err,result){
if(err){throw err}
console.log("updated")
})
})
request body :
{
"id":"5d63e81c342987154cdc698e",
"remove": ["Bengali", "Kannada"],
"add": ["Tamil", "Punjabi"]
}
I've expected to update the languages_known field using this req through postman to this id
Ok this issue is with the query, you can't update _id of a document like that, also as your requirement is not to do that, Please try this :
server code :
// replace languages_known with new languages based on _id, in case if you wanted to update the array you can use $push
app.put('/students/add', function (req, res) {
const id = require('mongodb').ObjectID(req.body.id)
let updateQuery = {
$set: { languages_known: req.body.add }
}
db.collection('students').updateOne({ _id: id }, updateQuery, function (err, result) {
if (err) { throw err }
console.log("updated")
})
})

Get Mongo _id as string and not ObjectId in find query without aggregation [duplicate]

Here I have created a collection with a single document
db.getCollection('example').insert({"example":1});
I have tried to use Projection, and I get back the _id.
db.getCollection('example').find({"example":1},{"_id":1});
{
"_id" : ObjectId("562a6300bbc948a4315f3abc")
}
However, I need the below output as shown below.
id and not _id
ObjectId("562a6300bbc948a4315f3abc") vs "562a6300bbc948a4315f3abc"
{
"id" : "562a6300bbc948a4315f3abc"
}
Although I can process #1 and #2 on my app server(PHP based) to get the desired ouput, I am looking if there is a way to get the expected result on querying from mongo itself
MongoDB 4.0 adds the $convert aggregation operator and the $toString alias which allows you to do exactly that:
db.getCollection('example').aggregate([
{ "$match": { "example":1 } },
{ "$project": { "_id": { "$toString": "$_id" } } }
])
A main usage would most likely be though to use the _id value as a "key" in a document.
db.getCollection('example').insertOne({ "a": 1, "b": 2 })
db.getCollection('example').aggregate([
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[{
"k": { "$toString": "$_id" },
"v": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": ["$$this.k", "_id"] }
}
}
}
}]
]
}
}}
])
Which would return:
{
"5b06973e7f859c325db150fd" : { "a" : 1, "b" : 2 }
}
Which clearly shows the string, as does the other example.
Generally though there is usually a way to do "transforms" on the cursor as documents are returned from the server. This is usually a good thing since an ObjectId is a 12-byte binary representation as opposed to a 24 character hex "string" which takes a lot more space.
The shell has a .map() method
db.getCollection('example').find().map(d => Object.assign(d, { _id: d._id.valueOf() }) )
And NodeJS has a Cursor.map() which can do much the same thing:
let cursor = db.collection('example').find()
.map(( _id, ...d }) => ({ _id: _id.toString(), ...d }));
while ( await cursor.hasNext() ) {
let doc = cursor.next();
// do something
})
And the same method exists in other drivers as well ( just not PHP ), or you can just iterate the cursor and transform the content as is more likely the best thing to do.
In fact, whole cursor results can be reduced into a single object with great ease by simply adding to any cursor returning statement, when working in the shell
.toArray().reduce((o,e) => {
var _id = e._id;
delete e._id;
return Object.assign(o, { [_id]: e })
},{ })
Or for full ES6 JavaScript supporting environments like nodejs:
.toArray().reduce((o,({ _id, ...e })) => ({ ...o, [_id]: e }),{ })
Really simple stuff without the complexity of what needs to process in the aggregation framework. And very possible in any language by much the same means.
You need to use the .aggregate() method.
db.getCollection('example').aggregate([ { "$project": { "_id": 0, "id": "$_id" } } ]);
Which yields:
{ "id" : ObjectId("562a67745488a8d831ce2e35") }
or using the .str property.
db.getCollection('example').find({"example":1},{"_id":1}).map(function(doc) {
return {'id': doc._id.str }
})
Which returns:
[ { "id" : "562a67745488a8d831ce2e35" } ]
Well if you are using the PHP driver you can do something like this:
$connection = new MongoClient();
$db = $connection->test;
$col = $db->example;
$cursor = $col->find([], ["_id" => 1]);
foreach($cursor as $doc) { print_r(array("id" => $doc["_id"])); }
Which yields:
Array
(
[id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
Or using again the MongoCollection::aggregate method.
$result = $col->aggregate(array(["$project" => ["id" => "$_id", "_id" => 0]]))
Then using the foreach loop:
Array
(
[_id] => MongoId Object
(
[$id] => 562a6c60f850734c0c8b4567
)
)
One simple solution for traversing MongoCursor on PHP side is to use Generators as well as foreach or array_map($function, iterator_to_array($cursor)).
Example:
function map_traversable(callable $mapper, \Traversable $iterator) {
foreach($iterator as $val) {
yield $mapper($val);
}
}
You can meet more at PHP documentation about generators syntax.
So, now you can use/reuse it (or similar implementation) for any propose of "projecting" your data on PHP side with any amount of mapping (just like pipeline does in aggregate) but with fewer iterations amount. And this solution is pretty convenient for OOP in a case of reusing your map functions.
UPD:
Just for your case example below:
$cursor = $db->getCollection('example')->find(["example":1],["_id":1]);
$mapper = function($record) {
return array('id' => (string) $record['_id']); //see \MongoId::__toString()
}
$traversableWithIdAsStringApplied = map_traversable($mapper, $cursor);
//...
now you can proceed with more mappings applied to $traversableWithIdAsStringApplied or use just iterator_to_array for simple array retrieving.

How to use id, find an element and only return one field in Meteor + MongoDB

How to use id, find an element and only return one field in Meteor + MongoDB. I wanted to only return status but this doesnt work it return the whole docs? what am I missing?
stuCourse.classId = awquMqKMrYKqNueGx
stuCourse.courseId = m7pcWesZnhWxJgojG
client side
const clas = Col_AllClasses.findOne({
_id: stuCourse.classId,
"courseList.courseId": stuCourse.courseId
}, {
field: {
"courseList.status": 1
}
})
mongodb data
{
"_id": "awquMqKMrYKqNueGx",
"title": "haha1",
"password": "123",
"courseList": [
{
"courseId": "52Eo6XJ33CMGLo4rL",
"status": 0
},
{
"courseId": "m7pcWesZnhWxJgojG",
"status": 0
}
],
}
your are writing incorrect query related to what you wanted, you need to replace field keyword with fields then your Meteor mongo query will appear like
Col_AllClasses.findOne({
_id: stuCourse.classId,
"courseList.courseId": stuCourse.courseId
}, {
fields: {
"courseList.status": 1
}
});
field: {
"courseList.status": 1
}
should be
fields: {
"courseList.status": 1
}

How to find by referenced document in Doctrine ODM with MongoDB?

I have one document in my "params" collection like this:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"$ref": "productTypes",
"$id": ObjectId("4d120a2d2b8d8d3010000000"),
"$db": "test"
}
]
}
the referenced document is this:
{
"_id": ObjectId("4d120a2d2b8d8d3010000000"),
"code": "car"
}
I'm using DoctrineODM to fetch the "param" documents which referenced "productType" is "car". I'm using this code:
$query = $dm->createQuery('Cms\Model\Param');
$query->field('productTypes.code')->equals('car');
$result = $query->execute();
var_dump($result);
but the result is an empty array. How can i do this?
If you using ReferenceMany or ReferenceOne you can't query by any reference document field, except reference document id.
If you need query on code from referenced collection you should use EmbedMany instead of ReferenceMany.
In this case your document will be:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"_id": ObjectId("4d120a2d2b8d8d3010000000"),
"code": "car"
}
]
}
And following query will work:
$query = $dm->createQuery('Cms\Model\Param');
$query->field('productTypes.code')->equals('car');
$result = $query->execute();
var_dump($result);
Also if your ProductType code is unique you can use it instead of MongoId, in this case you can query on $id:
{
"_id": ObjectId("4d124cef3ffcf6f410000037"),
"code": "color",
"productTypes": [
{
"$ref": "productTypes",
"$id": 'car',
"$db": "test"
}
]
}
Referenced document:
{
"_id": 'car'
}
Query:
$query->field('productTypes.$id')->equals('car');
You must use the references() method of Query Builder for a #MongoDB\ReferenceOne like https://doctrine-mongodb-odm.readthedocs.org/en/latest/reference/query-builder-api.html
$productType = $dm->getRepository('Cms\Model\ProductTypes')->findOneByCode('car');
$queryBuilder = $dm->getRepository('Cms\Model\Param')->createQueryBuilder()
->field('productTypes')->references($productType);
$results = $queryBuilder->getQuery()->execute();
PS: use includesReferenceTo() a #MongoDB\ReferenceMany
->field('productTypes.code')->equals(new \MongoRegex('/.*car.*/i'))