How to return a specific field in mongodb? - mongodb

Here is my code, it searches the word 'test' through all documents in 'subs' collection and return them.
The thing is I just need two specific fields (id and name).
app.get('/', (req, res) => {
db.collection('subs')
.find({
$text: { $search: 'test' },
})
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});

So you can use a projection:
db.collection('subs').find({$text: { $search: 'test' }}, {name: 1 } ).
Read more about it here: https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/#return-the-specified-fields-and-the-_id-field-only

you can set the fields you need in additional argument to the find method :
db.collection('subs').find({
$text: { $search: 'test' }
},
{
name: 1,
otherColumn: 1
}); // select only the "name" & the "otherColumn" column
The _id column is always returned by default, but you could disable it by adding _id: 0.
Hope this solve your question.

Finally I found the answer! :
.find(
{
name: { $in: ['Prison Break', 'Dexter'] },
$text: { $search: 'kill' },
},
{
projection: { name: 1 },
}
)

Related

How to change a document in mongodb with findOneAndUpdate depending if it's an insert or an update

I have this function that upserts in database.
upsert = (req, res) => {
return Promise.all(req.body.map(resource => {
return Resource.findOneAndUpdate({
resource_id: resource.id
},
{
$set: {
title: resource.title,
seller_id: resource.seller_id,
initial_quantity: resource.quantity,
quantity: 0
}
},
{
upsert: true,
new: true
});
}))
.then(
res.status(200).json({ message: "OK" })
)
.catch(err => {
console.log(err);
res.status(500).json({ message: "Error" });
});
}
It is working as expected. Now I want to be able to:
Save 0 in quantity, if its a new document
Save quantity(db) + resource.quantity (request), if it is an update
How could I do this?
Use the pipeline form of update to use aggregation operators in the update:
Resource.findOneAndUpdate(
{"resource_id": resource.id},
[{$set: {
quantity: {$cond: {
if: {$eq: [{$ifNull: ["$quantity","null"]}, "null"]},
then: 0,
else: {$sum: ["$quantity", resource.quantity]}
}},
title: resource.title,
seller_id: resource.seller_id,
initial_quantity: resource.quantity
}}],
{
upsert: true,
new: true
}
)
Playground

Mongo and mongoose $match _id in array

I have a frontend in React and a backend in express and node.
From FE i am calling an API on the server:
const { data: autotaskItems } = useApiCall({
url: `api/endpoint`,
method: 'post',
payload: {
filter: {
_id: {
$in: ["id1","id2"],
},
},
},
});
on the server:
router.post('/config-items/find', async (req, res) => {
const { filter } = req.body
// ConfigItem.find({ ...filter })
// .then(result => {
// res.status(200).json({ success: true, data: result });
// })
ConfigItem.aggregate([
{ $match: { ...filter }
}])
.then(result => {
res.status(200).json({ success: true, data: result });
})
But this doesn't work. I have found that aggregate doesn't "support" automatic conversion of ObjectId to string. If I have used find() and spread filter like above this will work just fine. However, I do need to use aggregate as I have a couple of lookups there too.
Anyone can help, please?
Also, if possible i would like to keep structure with spreading the filter object for match
Thank you
As per #Martinez's answer, this was resolved by the following:
Nice and simple :-)
ConfigItem.aggregate([{
"$addFields": {
"_id": {
"$toString": "$_id"
}
}
},
//rest of the query

Aggregate method for each document resulting from find method in mongodb

I want to execute this aggregate query:
db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: {
query: 'night',
path: {
wildcard: '*',
},
},
},
},
])
})
for each document resulting from my find method:
here is my find method:
app.get('/', (req, res) => {
db.collection('subs').find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
)
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
I know I probably have to use forEach and create a function, but I couldn't find out what to put in this function, I assume ## Heading ##it should be something like that:
.find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
).forEach(()=>{})
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
You can achieve this in several different ways, here is the simplest code sample I could produce:
app.get('/', async (req, res) => {
const result = await db.collection('subs').find(
{name: {$regex: 'dexter', $options: '$i'}},
{projection: {_id: 0, content: 0}}
).toArray();
const finalResults = await Promise.all(result.map(async (each) => {
each.textSearchResults = await db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: { // decide what your query is based on each document
query: each.name,
path: {
wildcard: '*',
},
},
},
},
])
return each
}))
res.json({
length: result.length,
body: {result},
});
});

Mongoose update only fields available in request body

I am trying to update one document using findOneAndUpdate and $set but I clearly missing something very crucial here because the new request is overwriting old values.
My Device schema looks like this:
{
deviceId: {
type: String,
immutable: true,
required: true,
},
version: {
type: String,
required: true,
},
deviceStatus: {
sensors: [
{
sensorId: {
type: String,
enum: ['value1', 'value2', 'value3'],
},
status: { type: Number, min: -1, max: 2 },
},
],
},
}
And I am trying to update the document using this piece of code:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{ $set: req.body },
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
And when I try to send a request from the postman with the body that contains one or multiple sensors, only the last request is saved in the database.
{
"deviceStatus": {
"sensors": [
{
"sensorId": "test",
"status": 1
}
]
}
}
I would like to be able to update values that are already in the database based on req.body or add new ones if needed. Any help will be appreciated.
The documentation said:
The $set operator replaces the value of a field with the specified
value.
You need the $push operator, it appends a specified value to an array.
Having this documents:
[
{
_id: 1,
"array": [
2,
4,
6
]
},
{
_id: 2,
"array": [
1,
3,
5
]
}
]
Using $set operator:
db.collection.update({
_id: 1
},
{
$set: {
array: 10
}
})
Result:
{
"_id": 1,
"array": 10
}
Using $push operator:
db.collection.update({
_id: 1
},
{
$push: {
array: 10
}
})
Result:
{
"_id": 1,
"array": [
2,
4,
6,
10
]
}
you want to using $push and $set in one findOneAndUpdate, that's impossible, I prefer use findById() and process and save() ,so just try
let result = await Device.findById(deviceId )
//implementation business logic on result
await result.save()
If you want to push new sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{
$push: {
"deviceStatus.sensors": { $each: req.body.sensors }
}
},
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
Update to the old answer:
If you want to update sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ "deviceId": deviceId },
{ "deviceStatus": req.body.sensors },
{ upsert: true },
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);

Mongoose's find method with $or condition does not work properly

Recently I start using MongoDB with Mongoose on Nodejs.
When I use Model.find method with $or condition and _id field, Mongoose does not work properly.
This does not work:
User.find({
$or: [
{ '_id': param },
{ 'name': param },
{ 'nickname': param }
]
}, function(err, docs) {
if(!err) res.send(docs);
});
By the way, if I remove the '_id' part, this DOES work!
User.find({
$or: [
{ 'name': param },
{ 'nickname': param }
]
}, function(err, docs) {
if(!err) res.send(docs);
});
And in MongoDB shell, both work properly.
I solved it through googling:
var ObjectId = require('mongoose').Types.ObjectId;
var objId = new ObjectId( (param.length < 12) ? "123456789012" : param );
// You should make string 'param' as ObjectId type. To avoid exception,
// the 'param' must consist of more than 12 characters.
User.find( { $or:[ {'_id':objId}, {'name':param}, {'nickname':param} ]},
function(err,docs){
if(!err) res.send(docs);
});
I implore everyone to use Mongoose's query builder language and promises instead of callbacks:
User.find().or([{ name: param }, { nickname: param }])
.then(users => { /*logic here*/ })
.catch(error => { /*error logic here*/ })
Read more about Mongoose Queries.
You can also add a mix of $or and and to give your functions more flexibility, options and robustness, like so:
var ObjectId = require("mongoose").Types.ObjectId;
var idParam = new ObjectId(param.length < 12 ? "123456789012" : param);
const {nameParam, nicknameParam, ageParam} = req.params || req.body || req.query
User.find({
$or: [{
_id: objId
},
{
name: nameParam
},
{
nickname: nicknameParam
}
],
$and: [{
age: ageParam
}]
},
function (err, docs) {
if (!err) res.send(docs);
}
);
So this means that your find() will look for all users where (_id = idParam OR name = nameParam OR nickname = nicknameParam) AND (age = ageParam)
I hope this helps someone out there.
Cheers!!!