using db.mycollection.distinct('field1', 'field2') with mongodb / mongoskin - node.js

I have the following line in an exports declaration in a user.js file:
db.collection('myList').distinct('field1', 'field2'), function(err, items) {
res.json(items);
}
The db lookup works fine from the mongo command line. However, when I run the webpage, I get:
TypeError: string is not a function
I'm not certain how to proceed at this point. Should I be casting the return to a string somehow before returning it?
Thanks.

Your question does not seem to be well defined, consider the logic:
> db.info.insert({ field1: "this", field2: "that" })
> db.info.insert({ field1: "this", field2: "another" })
> db.info.insert({ field1: "this", field2: "that" })
> db.info.insert({ field1: "this", field2: "another" })
> db.info.distinct('field1', 'field2')
[ "this" ]
But what you actually want is:
db.info.aggregate([
{$group: {_id: { field1: "$field1", field2: "$field2" } } }
])
Which gives:
{
"result" : [
{
"_id" : {
"field1" : "this",
"field2" : "another"
}
},
{
"_id" : {
"field1" : "this",
"field2" : "that"
}
}
],
"ok" : 1
}
The distinct results of the combined fields.
I hope we are learning now.

For some odd reason MongoSkin abstraction doesn't provide access to the MongoDB 'distinct' method. To get distinct to work, I had to use the native MongoDB collection and not the abstracted MongoSkin collection. Here is how I did it
var store = mongo.db(connectionString, ackOptions);
store.open(function(err,resp){
resp.collection(this.options.collection, {strict: true}, function(err, myCollection) {
myCollection.distinct('tag',function(err,res){
console.log(err)
console.log(res)
})
});
})

Related

Prevent mongoose "Model.updateOne" from updating ObjectId(_id) of the model when using "$set"

I'm updating the age and name of a character with a specific _id from an array of characters that is inside a document of model Drama.
The document I'm working with:-
{
"_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
"characters" : [
{
"_id" : ObjectId("619fdac5a03c8b10d0b8b13c"),
"age" : "23",
"name" : "Vinay",
},
{
"_id" : ObjectId("619fe1d53810a130207a409d"),
"age" : "25",
"name" : "Raghu",
},
{
"_id" : ObjectId("619fe1d53810a130207a502v"),
"age" : "27",
"name" : "Teju",
}
],
}
So to update the character Raghu I did this:-
const characterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": characterObj,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
This updated the character but it also assigned a new ObjectId to the _id field of the character. So, I'm looking for ways on how to prevent the _id update.
Also, I know I can set the individual fields of character instead of assigning a whole new object to prevent that but it will be very tedious if my character's object has a lot of fields.
//Not looking to do it this way
$set: {
"characters.$.age": characterObj.age,
"characters.$.name": characterObj.name,
},
Thanks.
I found something here, just pre define a schema (a blueprint in a way) that affects the id
var subSchema = mongoose.Schema({
//your subschema content
},{ _id : false });
Stop Mongoose from creating _id property for sub-document array items
Or I would say, when you create a character assign it a custom id from the start, that way it will retain that id throughout.
I'm leaving this question open as I would still like to see a simpler approach. But for now, I did find one easy alternative solution for this issue which I'm will be using for some time now until I find a more direct approach.
In short - Deep merge the new object in the old object using lodash and then use the new merged object to set field value.
For example, let's update the character Raghu from my question document:-
First install lodash(Required for deep merging objects) using npm:
$ npm i -g npm
$ npm i --save lodash
Import lodash:
const _ = require("lodash");
Now update the character Raghu like this:-
const newCharacterObj = {
age: "26",
name: "Dr. Raghu",
};
Drama.findById(
{ _id: req.drama._id, "characters._id": characterId },
"characters.$",
function(err, dramaDocWithSpecificCharacter) {
console.log(dramaDocWithSpecificCharacter);
// ↓↓↓ console would log ↓↓↓
// {
// "_id" : ObjectId("619d44d2ec2ca20ca0404b5a"),
// "characters" : [
// {
// "_id" : ObjectId("619fe1d53810a130207a409d"),
// "age" : "25",
// "name" : "Raghu",
// }
// ],
// }
const oldCharacterObj = dramaDocWithSpecificCharacter.characters[0];
const mergedCharacterObjs = _.merge(oldCharacterObj, newCharacterObj);
// _.merge() returns a deep merged object
console.log(mergedCharacterObjs);
// ↓↓↓ console would log ↓↓↓
// {
// _id: 619fe1d53810a130207a409d,
// age: "26",
// name: "Dr. Raghu",
// };
Drama.updateOne(
{ _id: req.drama._id, "characters._id": characterId },
{
$set: {
"characters.$": mergedCharacterObjs,
},
},
function(err, foundlist) {
if (err) {
console.log(err);
} else {
console.log("Update completed");
}
}
);
}
);
// req.drama._id is ObjectId("619d44d2ec2ca20ca0404b5a")
// characterId is ObjectId("619fe1d53810a130207a409d")
Note: We can also use the native Object.assign() or … (spread operator) to merge objects but the downside of it is that it doesn’t merge nested objects which could cause issues if you later decide to add nested objects without making changes for deep merge.
You can pass your payload or request body like this if we provide _id it will prevent update to nested document
"characters" : [
{
"_id" : "619fdac5a03c8b10d0b8b13c",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a409d",
"age" : "updated value",
"name" : "updated value",
}, {
"_id" : "619fe1d53810a130207a502v",
"age" : "updated value",
"name" : "updated value",
}
],
It works for me for bulk update in array object

MongoDB update multiple items in an array of objects with corresponding data

I have an array in my MongoDB document as shown below:
{
...otherDocFields,
groupID: "group-id",
users: [
{id: "uid1", name: "User1"},
{id: "uid2", name: "User2"},
{id: "uid3", name: "User3"},
{id: "uid4", name: "User4"}
]
}
I'm trying to write a function that will update users' names based on that ID.
I tried something like:
async function updateNames(groupID: string, data: Array<{id: string, name: string}>) {
try {
// MongoDB Aggregation
await mongoDB.collection("users").aggregate([
{$match: {groupID}},
{$unwind: {
path: '$users',
includeArrayIndex: 'users.id',
preserveNullAndEmptyArrays: true
}
}
//....
])
} catch (e) {
console.log(e)
}
}
I'm stuck at the part to update the relevant names from the data param in the function.
A sample data would be:
[
{id: "uid1", name: "newName1"},
{id: "uid3", name: "newName3"}
]
I can for sure read, manually process it and update the document but I'm looking for a way to do it in single go using aggregation.
You can do this with an update statement using array filters (https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#---identifier--)
First, if we just declare some tests data which would be passed into your method:
const data = [
{id: "uid2", name: "ChangeName1"},
{id: "uid4", name: "ChangedName2"}
];
We can then create an update statement which updates all the users within that list within a map and a reduce:
const sets = data.map((element, index) => ({ [`users.$[f${index}].name`]: element.name })).reduce((accumulator, currentValue) => (Object.assign(currentValue, accumulator)), { });
const update = { $set: sets };
This will give us the following update statement:
{
"$set" : {
"users.$[f1].name" : "ChangedName2",
"users.$[f0].name" : "ChangeName1"
}
}
We can create a bunch of array filters with that data:
const arrayFilters = data.map((element, index) => ({ [`f${index}.id`]: element.id }));
This will give us the following which we can pass into the options of an update.
[ { "f0.id" : "uid2" }, { "f1.id" : "uid4" } ]
Last of all we can execute the following update command:
db.users.updateOne(
filter,
update,
{ arrayFilters }
);
Now if we check the output we'll get the following results
> db.users.find().pretty()
{
"_id" : ObjectId("60e20841351156603932c526"),
"groupID" : "123",
"users" : [
{
"id" : "uid1",
"name" : "User1"
},
{
"id" : "uid2",
"name" : "ChangeName1"
},
{
"id" : "uid3",
"name" : "User3"
},
{
"id" : "uid4",
"name" : "ChangedName2"
}
]
}

MongoDB add field to an object inside an array

I have an object user that looks like that
{
"_id" : ObjectId("5edbdf57ac52325464b054ec"),
...
"purchaseHistory" : [
{
"_id" : ObjectId("5ee7a8f6b438a1254cec3f74"),
...
},
{
"_id" : ObjectId("5ee7a8f6b438a1254cec3f88"),
...
}
]
}
What I wanna do is to add a new field to a specific object inside "purchaseHistory" by ID, for example I wanna add to "5ee7a8f6b438a1254cec3f88" a field "status": 0
What I tried is
users.findOneAndUpdate(
{
_id: ObjectId(userId),
'purchaseHistory._id': ObjectId(saleId)
},
{
$set: { 'purchaseHistory.$.status': status}
}
)
But it gives me an error, how can I do it properly?
According to the website provided by D. SM, I was able to do it this way
users.findOneAndUpdate(
{
'_id': ObjectId(userId),
'purchaseHistory._id': ObjectId(saleId)
},
{
$set: { 'purchaseHistory.$.status': status }
}
)
MongoDB provides the positional update operator for cases like this.

$pulling an object from an array based on _id in Mongoose [duplicate]

Doc:
{
_id: 5150a1199fac0e6910000002,
name: 'some name',
items: [{
id: 23,
name: 'item name 23'
},{
id: 24,
name: 'item name 24'
}]
}
Is there a way to pull a specific object from an array? I.E. how do I pull the entire item object with id 23 from the items array.
I have tried:
db.mycollection.update({'_id': ObjectId("5150a1199fac0e6910000002")}, {$pull: {id: 23}});
However I am pretty sure that I am not using 'pull' correctly. From what I understand pull will pull a field from an array but not an object.
Any ideas how to pull the entire object out of the array.
As a bonus I am trying to do this in mongoose/nodejs, as well not sure if this type of thing is in the mongoose API but I could not find it.
try..
db.mycollection.update(
{ '_id': ObjectId("5150a1199fac0e6910000002") },
{ $pull: { items: { id: 23 } } },
false, // Upsert
true, // Multi
);
I have a document like
I have to delete address from address array
After searching lots on internet I found the solution
Customer.findOneAndUpdate(query, { $pull: {address: addressId} }, (err, data) => {
if (err) {
return res.status(500).json({ error: 'error in deleting address' });
}
res.json(data);
});
my database:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 },
{ "id" : 3 }
]
}
my query:
db.getCollection('play_table').update({},{$pull:{"data":{"id":3}}},{multi:true}
output:
{
"_id" : ObjectId("5806056dce046557874d3ab18"),
"data" : [
{ "id" : 1 },
{ "id" : 2 }
]
}
You can try it also:
db.getCollection('docs').update({ },{'$pull':{ 'items':{'id': 3 }}},{multi:true})
For a single record in array:
db.getCollection('documents').update(
{ },
{'$pull':{ 'items':{'mobile': 1234567890 }}},
{new:true}
);
For a multiple records with same mobile number in array:
db.getCollection('documents').update(
{ },
{
$pull: {
items: { mobile: 1234567890 }
}
},
{ new:true, multi:true }
)
Use $pull to remove the data
return this.mobiledashboardModel
.update({"_id": args.dashboardId}, { $pull: {"viewData": { "_id": widgetId}}})
.exec()
.then(dashboardDoc => {
return {
result: dashboardDoc
}
});
Kishore Diyyana:
If you want to remove all elements including the key of the element attributes list.
Here is the example of mongoDB unset operator:
db.UM_PREAUTH_CASE.update(
{ 'Id' : 123}, { $unset: { dataElements: ""} } )
JSON Look like this:
{ "Id":123,"dataElements" : [ { "createdBy" : "Kishore Babu Diyyana", "createdByUserId" : 2020 }, { "createdBy" : "Diyyana Kishore", "createdByUserId" : 2021 } ] }

nested document read in MongoDB array in node

I have a nested document of following format:
{
_id: "1234567890",
course: {content: [{ id: 1,
children:[{
id: 1111
},
{
id: 2222
}
{
id: 3333
}]
},
{ id: 2,
children:[{
id: 4444
},
{
id: 5555
}
{
id: 6666
}]
}
]}
}
I'm trying to query the children based on id (course.content.children.id) using the following code from Node.js:
var query = {
'course.content.children.id': quizId,
};
var option = {
'course.content.children.$': 1,
};
db.getEntityWithOption(tablename,query,option,function(data,err){
res.send(data.course.content[0].children);
});
db.getEntityWithOption is wrapper method on top of db.find() method. when I call the method with quiz id 1, the above code return me the following complete array: `
{ id: 1,
children:[{
id: 1111
},
{
id: 2222
}
{
id: 3333
}]
}
But I need only the specific element of the array with id 1111 not the compete array. Can any life savior suggest me what I'm doing wrong here. TIA :)
Well you can find the id what you want from the children object then send the result like:
res.send(data.course.content[0].children[0]);
Or you can find it with a for loop if its not in the first array element then sent the result.
Or you can try using $elemMatch in your query but then you will have to use aggregation.
Btw in my opinion you should not use so deeply nested documents cuz it will be hard to run complex queries on.
Please try it through aggregation, with $unwind and $match
> db.course.aggregate([
// unwind the `content` array
{$unwind: '$course.content'},
// unwind the `children` array of `content`
{$unwind: '$course.content.children'},
// match the id with `1111`
{$match: {'course.content.children.id': 1111}}]);
Result:
{ "_id" : ObjectId("56d058d22750efa1f9dc3772"), "course" : { "content" : { "id" : 1, "children" : { "id" : 1111 } } } }
To meet you requirement, only the the 1111 array is returned, we can do that through $project
> db.course.aggregate([{$unwind: '$course.content'},
{$unwind: '$course.content.children'},
{$match: {'course.content.children.id': 111}},
{$project: {_id: 0, arr: '$course.content.children'}}]);
Result:
{ "arr" : { "id" : 111 } }
You want to query by a property but you do not know which child of the content and children arrays possess the requested id. MongoDB provides the positional operator
The query should be defined this way:
var query = {
'course.content.$.children.$.id': quizId,
};

Resources