Using MongooseJS to Changing Mongodb _id to BSON UUID for ref - node.js

I currently use MongooseJS to change "_id" for each of my collections to a BSON UUID. On top of this I use a virtual to "id" to convert "_id" to its string equivalent. It works pretty good and gives me the benefit of using a UUID for the "_id" and not store it as a string which wastes disk resources.
Here is a snippet of code to show how this is done
const uuid = require("uuid-mongodb");
require("mongoose-uuid2")(mongoose);
let schema_options = {
"id": false,
"toObject": {
"getters": true,
"virtuals": true
},
"toJSON": {
"getters": true,
"virtuals": true,
"transform"(doc, ret) {
delete ret._id;
}
} };
let schema = new Schema(
{
"_id": {
"type": UUID,
"default": uuid.v4,
"required": true
},
"code": {
"type": String,
"required": true,
"unique": true
},
"name": {
"type": String,
"required": true
}
},
schema_options);
schema.virtual("id").get(function() {
return uuid.from(this._id).toString();
});
schema.virtual("id").set(function(uuid_string) {
this._id = uuid.from(uuid_string);
});
However, if I add a "ref" to another collection as with
schema.add({
"test_references": {
"type": [
{
"type": mongoose.Types.UUID,
"ref": "test_references"
}
],
"required": true
}
});
I get a hash representation of the BSON UUID. Is there a way to make MongooseJS during a get operation to show these refs as UUID string representations
i.e. - I expect this "104e0f2e-3b54-405b-ba81-e87c5eb9f263" but get this "EE4PLjtUQFu6geh8XrnyYw=="
Note:: If this is the incorrect forum for this post, please let me know and I will move this to the correct forum immediately

After some more research, I was able to apply a transform to the returned value.
This looks like the following:
this.schema.options.toJSON.transform = function(doc, ret, option) {
let items = [];
delete ret._id;
ret.items.forEach((item) => {
items.push(mongoose.uuid.from(item).toString());
});
ret.items = items;
return ret;
};
It is not ideal to look through all the items in the array but it is the best I could find in my research

Related

Mongoose - How to query for one of the fields in a list of objects, but update all of the fields?

I have a schema UserSettings that looks as follows:
{
"id": "5d8b987f9f8b9f9c8c8b9f9",
"settings": [
{
"setting_id": "112378482033233",
"is_active": true,
},
{
"setting_id": "115677743203553",
"is_active": true,
}
]
}
I want to be able to use mongoose to findOneAndUpdate one of the setting objects with a specific id to replace with a new one.
I currently have:
let setting_object = {
setting_id: settingId,
is_active: isActive,
}
return await models.UserSettings.findOneAndUpdate({
id: mongoose.Types.ObjectId(user.id)
},
{
$set: {
settings: setting_object
}
},
{
new: true,
upsert: true
});
The issue with this is that if is_active is the opposite boolean as the one in the document DB, it will add a new object in the settings array instead of replace with the one with the same id.
So if i had
let setting_object = {
setting_id: 112378482033233,
is_active: false,
}
and did the findOneAndUpdate above, the new DB would read:
{
"id": "5d8b987f9f8b9f9c8c8b9f9",
"settings": [
{
"setting_id": "112378482033233",
"is_active": true,
},
{
"setting_id": "112378482033233",
"is_active": false,
},
{
"setting_id": "115677743203553",
"is_active": true,
}
]
}
I read some things with using mongoose's aggregate but I haven't totally understood how this connects with my example. Thanks in advance!

Mongodb update all the documents with unique id

I have collection with name products with almost 100k documents. I want to introduce a new key called secondaryKey with unique value uuid in all the documents.
I do this using nodejs.
Problem I am facing:-
When I try the below query,
db.collection('products').updateMany({},{"$set":{secondaryKey: uuid()}});
Here it updates all the documents with same uuid value,
I try with loop to update document one by one,but here issues is I don't have filter value in updateOne because I want to update all the documents.
Can anyone please help me here.
Thanks :)
If you are using MongoDB version >= 4.4 You can try this:
db.products.updateMany(
{},
[
{
$set: {
secondaryKey: {
$function: {
body: function() {
return UUID().toString().split('"')[1];
},
args: [],
lang: "js"
}
}
}
}
]
);
Output
[
{
"_id": ObjectId("..."),
"secondaryKey": "f41b15b7-a0c5-43ed-9d15-69dbafc0ed29"
},
{
"_id": ObjectId("..."),
"secondaryKey": "50ae7248-a92e-4b10-be7d-126b8083ff64"
},
{
"_id": ObjectId("..."),
"secondaryKey": "fa778a1a-371b-422a-b73f-8bcff865ad8e"
}
]
Since it's not the same value you want to put in each document you have to use the loop.
In your loop, you have to update the current document of the iteration. So you have to filter with the _id in the updateOne
The above reply didn't work for me. Plus, it compromises security when you enable javascript on your database (see here $function and javascript enabling on database). The best way is to not overload your server, do your work on local as below:
const { nanoid, customAlphabet } = require('nanoid')
async function asdf() {
const movies = await client.db("localhost").collection("productpost");
var result2 = []
let result = await movies.find({}).toArray()
result.forEach(element => {
const nanoid = customAlphabet('1234567890', 10)
console.log(element.price)
element.price = 4
element.id = nanoid()
result2.push(element)
});
console.log("out reult2", result2)
await movies.deleteMany({})
await movies.insertMany(result2)
})
It will delete any objects on your collections and update with the new ones. Using nanoid as uniqueids.
This is the database object array after adding unique id:
{ "_id": { "$oid": "334a98519a20b05c20574dd1" }, "attach": "[\"http://localhost:8000/be/images/2022/4/bitfinicon.png\"]", "title": "jkn jnjn", "description": "jnjn", "price": 4, "color": "After viewing I am 48.73025772956596% more satisfied with life.", "trademark": "", "category": "[]", "productstate": "Published", "createdat": { "$date": "2022-04-03T17:40:54.743Z" }, "language": "en"}
P.S: Please backup your collection before doing this or filter the array on your needs for not going through all collection.

How to update a value inside mongodb with nodeJS?

I'm trying to update a value inside mogoodb array but is the problem
database?
"TempChannels": [{
"user": "299481590445113345",
"channel": "794869948878159884",
"settings": []
}, {
"user": "583363766750806043",
"channel": "795004103998308352",
"settings": []
}],
The part of the code that should update the user:
Data.TempChannels[0].user = Target.id
Data.save().catch(er => console.log(er))
Also, no error appears when I run the code. and when i console the data it returns a user which has updated but it is not actually saved in mongodb!
code
Data.save().then(() => console.log(Data.TempChannels[0].user))
this is the whole data
{
"_id": {
"$oid": "5ff0cd1ee3d9fd2d40d82d23"
},
"TempChannels": [{
"user": "299481590445113345",
"channel": "795014692522295326",
"settings": []
}, {
"user": "583363766750806043",
"channel": "795015273060892753",
"settings": []
}],
"Guild": "704158258201624657",
"Stats": "true",
"ChannelID": "795014681664290826",
"ParentID": "795014680556994610",
"LogChannelID": "795014683601010709",
"TempControlChannelID": "795014682518749274",
"DefaultSettings": {
"limit": null,
"name": null,
"bitrate": null,
"copyperms": null
},
"__v": 2
}
I'm filtering the data by Guild
if you are using mongoose to connect MongoDB, the
Use markModified("updated field name")
Data.TempChannels[0].user = Target.id
Data.markModified('TempChannels');
Data.save().catch(er => console.log(er))
if you are using mongoose, there is a method that will allow to update the content of existing data
const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
res.n; // Number of documents matched
res.nModified; // Number of documents modified
If you are using Mongoose, you can update the record by doing something similar to this:
SchemaName.update({Guild: 'Guild Value Here'}, {$set: { "TempChannels[0].user" : "%newvalue%"}})
.then((data) => {
console.log(data)
})
.catch(err => {
console.log.error(err);
});
You should replace schemaName with the name of your schema if you are using mongoose, and in case you are not using it, I think it would be better for you to start using it.

how to push value in to object's property dynamically in mongoose?

I want to push the value into array based on the property value recieved from frontend using mongoose.
Schema:
authencity: {
fake: [],
notSure: [],
authentic: [],
}
Now I want to push the value into array based on property value received from frontend
const result = await News.findOneAndUpdate({ name: newsName }, { $push: { `authencity[${authencityType}]`: email } });
authencityType could be anything like fake, notSure, authentic based on this value I want to insert the value into respective array.
Sample Data:
Sample Document :{
"_id": {
"$oid": "5f0af1e09a724b06c86bfaec"
},
"authencity": {
"fake": [],
"notSure": [],
"authentic": []
},
"name": "Rajasthan deputy CM Sachin Pilot\u2019s supporters come to Gurugram resort, call police summon a joke",
"description": "",
"url": "https://news.google.com/__i/rss/rd/articles/CBMiqwFodHRwczovL3d3dy5oaW5kdXN0YW50aW1lcy5jb20vaW5kaWEtbmV3cy9yYWphc3RoYW4tZGVwdXR5LWNtLXNhY2hpbi1waWxvdC1zLXN1cHBvcnRlcnMtY29tZS10by1ndXJ1Z3JhbS1yZXNvcnQtY2FsbC1wb2xpY2Utc3VtbW9uLWEtam9rZS9zdG9yeS1ZQ1BQVDFlandtb1RoQXJaRXVabmNOLmh0bWzSAa0BaHR0cHM6Ly9tLmhpbmR1c3RhbnRpbWVzLmNvbS9pbmRpYS1uZXdzL3JhamFzdGhhbi1kZXB1dHktY20tc2FjaGluLXBpbG90LXMtc3VwcG9ydGVycy1jb21lLXRvLWd1cnVncmFtLXJlc29ydC1jYWxsLXBvbGljZS1zdW1tb24tYS1qb2tlL3N0b3J5LVlDUFBUMWVqd21vVGhBclpFdVpuY05fYW1wLmh0bWw?oc=5",
"category": "general",
"language": "en",
"country": "in",
"__v": 0
}
Received Data : email : abcd#gmail.com authencityType could be fake or notSure or authentic
I want to push value in specific array based on authencityType recieved
You need to define your path using the dot notation instead of square brackets:
`authencity.${authencityType}`
For instance authenticity.fake
try:
const result = await News.findOneAndUpdate({ name: newsName }, { $push: { `authencity.${authencityType}`: email } }, { new: true });
Also note that in mongoose's findOneAndUpdate version you need to pass new: true parameter in order to return modified document
const newColumn = `authencity.${authencityType}`;
const result = await News.findOneAndUpdate({ name: newsName }, { $push: { [newColumn]: email } }, { new: true });
Here, I have created a dynamic string to construct a column name using template literal and then enclosed it with [], so mongo will consider this a column name

create a new object Id in mongoDB using node js

I am using the below code to insert data to mongodb
router.post('/NewStory', function (req, res) {
var currentObject = { user: userId , story : story , _id:new ObjectID().toHexString() };
req.db.get('clnTemple').findAndModify({
query: { _id: req.body.postId },
update: { $addToSet: { Stories: currentObject } },
upsert: true
});
});
This code is working fine if i remove the _id:new ObjectID().toHexString()
What i want to achieve here is that for every new story i want a unique _id object to be attached to it
What am i doing wrong?
{
"_id": {
"$oid": "55ae24016fb73f6ac7c2d640"
},
"Name": "some name",
...... some other details
"Stories": [
{
"userId": "105304831528398207103",
"story": "some story"
},
{
"userId": "105304831528398207103",
"story": "some story"
}
]
}
This is the document model, the _id that i am trying to create is for the stories
You should not be calling .toHexString() on this as you would be getting a "string" and not an ObjectID. A string takes more space than the bytes of an ObjectId.
var async = require('async'),
mongo = require('mongodb'),
db = require('monk')('localhost/test'),
ObjectID = mongo.ObjectID;
var coll = db.get('junk');
var obj = { "_id": new ObjectID(), "name": "Bill" };
coll.findAndModify(
{ "_id": new ObjectID() },
{ "$addToSet": { "stories": obj } },
{
"upsert": true,
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So that works perfectly for me. Noting the "new" option there as well so the modified document is returned, rather than the original form of the document which is the default.
{ _id: 55c04b5b52d0ec940694f819,
stories: [ { _id: 55c04b5b52d0ec940694f818, name: 'Bill' } ] }
There is however a catch here, and that is that if you are using $addToSet and generating a new ObjectId for every item, then that new ObjectId makes everything "unique". So you would keep adding things into the "set". This may as well be $push if that is what you want to do.
So if userId and story in combination already make this "unique", then do this way instead:
coll.findAndModify(
{
"_id": docId,
"stories": {
"$not": { "$elemMatch": { "userId": userId, "story": story } }
}
},
{ "$push": {
"stories": {
"userId": userId, "story": story, "_id": new ObjectID()
}
}},
{
"new": true
},
function(err,doc) {
if (err) throw err;
console.log(doc);
}
)
So test for the presence of the unique elements in the array, and where they do not exist then append them to the array. Also noting there that you cannot do an "inequality match" on the array element while mixing with "upserts". Your test to "upsert" the document should be on the primary "_id" value only. Managing array entries and document "upserts" need to be in separate update operations. Do not try an mix the two, otherwise you will end up creating new documents when you did not intend to.
By the way, you can generate an ObjectID just using monk.
var db = monk(credentials.database);
var ObjectID = db.helper.id.ObjectID
console.log(ObjectID()) // generates an ObjectID

Resources