Convert DB response from object to array - node.js

I have the following schema:
const mySchema = new mongoose.Schema({
name: String,
subscribers: [ { name: String } ]
})
const myModel = mongoose.model('User', mySchema, 'users')
and I have this code in one of my controllers:
const allOfHisSubscribers = await myModel.findById(req.params.id).select('subscribers')
I want the database response of the await myModel.findByid call to be:
[
{ name: 'x' },
{ name: 'y' },
{ name: 'z' },
]
However, the code above is returning:
{
subscribers: [
{ name: 'x' },
{ name: 'y' },
{ name: 'z' },
]
}
I don't want the JavaScript way of doing it, I know you can use something like Array.prototype.map to get the result, but I am using a middleware, so I can only use the mongoose or MongoDB way of doing it (if possible).
Got it :D, if not, let me know in the comments 🌹

Related

MongoDB: add object to subarray if not exists

I searched many questions here and other articles on the web, but they all seem to describe somehow different cases from what I have at hand.
I have User schema:
{
username: { type: String },
lessons: [
{
lesson: { type: String },
result: { type: String }
}
]
}
I want to add new element into lessons or skip, if there is already one with same values, therefore I use addToSet:
const dbUser = await User.findOne({ username })
dbUser.lessons.addToSet({ lesson, result: JSON.stringify(result) })
await dbUser.save()
However it makes what seems to be duplicates:
// first run
[
{
_id: 60c80418f2bcfe5fb8f501c1,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
}
]
// second run
[
{
_id: 60c80418f2bcfe5fb8f501c1,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
},
{
_id: 60c80470f2bcfe5fb8f501c2,
lesson: '60c79d81cf1f57221c05fdac',
result: '{"correct":2,"total":2}'
}
]
At this point I see that it adds _id and thus treats them as different entries (while they are identical).
What is my mistake and what should I do in order to fix it? I can change lessons structure or change query - whatever is easier to implement.
You can create sub-documents avoid _id. Just add _id: false to your subdocument declaration.
const userSchema = new Schema({
username: { type: String },
lessons: [
{
_id: false,
lesson: { type: String },
result: { type: String }
}
]
});
This will prevent the creation of an _id field in your subdoc, and you can add a new element to the lesson or skip it with the addToSet operator as you did.

how to put mongodb items in variables for discord bot?

This is the model:
Schema = mongoose.Schema;
module.exports = mongoose.model(
"Leveling",
new Schema({
guildID: {
type: String
},
guildName: {
type: String
},
roletoad: {
type: String,
default: "null"
},
roletoremove: {
type: String,
default: "null"
},
rolelevel: {
type: Number,
default: 0
},
})
);
This is the command to get all leveling roles in a specific guild:
if(args[0]==="list"){
const del = await Leveling.find({
guildID: message.guild.id,
},{
_id: 0,
roletoad: 1,
roletoremove: 1,
rolelevel:1
})
return await message.channel.send(del)
}
This is the output:
{
roletoad: '735106092308103278',
roletoremove: '731561814407774248',
rolelevel: 5
}
{
roletoad: '735598034385371167',
roletoremove: '744562691817078905',
rolelevel: 7
}
I want to know how to get each item(roletoad,roletoremove,rolelevel) in a specific variable.
It seems you're getting an array of objects form your db in the del variable, and each object in that array has the properties roletoad, roletoremove and rolelevel, which you want in separate variables.
For each object of your array, you can store these properties in variables by object destructuring. One approach is as follows:
//the data you'll get from the db
const del = [{
roletoad: '735106092308103278',
roletoremove: '731561814407774248',
rolelevel: 5
},
{
roletoad: '735598034385371167',
roletoremove: '744562691817078905',
rolelevel: 7
}]
for(const {
roletoad: yourRoleToAddVar,
roletoremove: yourRoleToRemoveVar,
rolelevel: yourRoleToLevelVar
} of del){
console.log(`Role to add: ${yourRoleToAddVar}`)
console.log(`Role to remove: ${yourRoleToRemoveVar}`)
console.log(`Role Level: ${yourRoleToLevelVar}`)
console.log(`---------------------------`)
//do what you want with these variables here
}
NOTE: This should go without saying but the scope of these variables will only be valid within this loop.

MongoDB query on multiple array of objects

Say I have a mongoSchema of user with the following field
googleId: String,
name: String,
Language : [{}],
User can initially specify an array of language-set [up to 5] on their profile page.
For example, user can have 2 language set as below:
Language :
[main: {name: English, code: ko}, secondary: [{name:Japanese, code: jp}, {name:Chinese, code: en}]],
[main: {name: Korean, code: ko}, secondary: [{name:Japanese, code: jp}, {name:English, code: en}]
From this information, I want to render out only the post that matches the following preference.
For example post with English to Japanese, English to Chinese (from first language set)
as well as post with Korean to Japanese, Korean to English (from second language set)
My post schema has
postName: String
original: {},
target: {},
For example,
postName: 'Korean to Japanese Post'
original: {name: Korean, code: ko}
target: {name: Japanese, code jp}
What should I put inside the curly brackets to fetch the posts with specified language set
const prefLang = req.user.Language
const post = await Post.find({ some query using prefLang to check over matching original and target })
res.send(translations)
The query object should look like this for your example
{
$or: [
{
original: { name: "English", code: "en" },
target: {
$in: [{ name: "Japanese", code: "jp" }, { name: "Chinese", code: "cn" }]
}
},
{
original: { name: "Korean", code: "ko" },
target: {
$in: [{ name: "Japanese", code: "jp" }, { name: "English", code: "en" }]
}
}
]
}
So you will have to construct the condition from prefLang like this
const query = {
$or: perfLang.map(lang => ({
original: lang.main,
target: { $in: lang.secondary }
}))
}
const post = await Post.find(query)
Tip:
If the language objects have a strict schema, you can define them like this
const LanguageSchema = new mongoose.Schema({
name: String,
code: String
}, {
_id: false
})
const UserSchema = new mongoose.Schema({
//...
googleId: String,
name: String,
Language: [{ main: LanguageSchema, secondary: [LanguageSchema] }]
})
Sounds like the usage of basic boolean logic would suffice:
const prefLang = req.user.Language;
let conditions = [];
prefLang.forEach((langObj) => {
let tragetLangs = langObj.secondary.map(lang => lang.code);
conditions.push({
$and: [
{
"original.code": langObj.main.code,
},
{
"target.code": {$in: tragetLangs}
}
]
})
})
// you should check conditions !== empty array.
const post = await Post.find({$or: conditions})

Updating an array in a discriminated model in mongoose

I have a model called Person which has a discriminator of "Worker" which gives it an additional locations field of an array.
I am trying to push an element onto the locations array without going through the fetch/modify/save method (so I can use updateMany later on to update several documents at the same time).
Why is that not happening in the code below? I have tried this with findByIdAndUpdate and findOneAndUpdate as well.
index.js:
const { connect } = require("mongoose");
const Person = require("./Person");
connect("mongodb://127.0.0.1:27017/test?gssapiServiceName=mongodb", {
useNewUrlParser: true,
}, async () => {
console.log("Database connected")
const person = await Person.create(
{
__t: "Worker",
name: "John",
locations: ["America"],
},
)
console.log(person);
// Outputs:
// {
// locations: [ 'America' ],
// _id: 5eba279663ecdbc25d4d73d4,
// __t: 'Worker',
// name: 'John',
// __v: 0
// }
await Person.updateOne(
{ _id: person._id }
{
$push: { locations: "UK" },
},
)
const updated = await Person.findById(person._id);
console.log(updated);
// (Updating "locations" was unsuccessful)
// Outputs:
// {
// locations: [ 'America' ],
// __t: 'Worker',
// _id: 5eba279663ecdbc25d4d73d4,
// name: 'John',
// __v: 0
// }
});
Person.js:
const { Schema, model } = require("mongoose");
const personSchema = Schema({
name: String,
});
const Person = model("Person", personSchema);
Person.discriminator(
"Worker",
Schema({
locations: [String],
})
);
module.exports = Person;
So it turns out you have to pass in the key (__t) when updating from the root Parent model and not the Worker model as the database does not know what fields a Worker would have.
Therefore, you can do the following:
await Person.updateOne(
{ _id : person._id, __t: "Worker" },
{ $push: { locations: "UK" } }
)
See more in this Github issue

Looping through objects and adding them to Sequelize db

I have an array of objects that I like to put into SQL via Sequelize and I'm running into issues.
[
{ owner: false,
id: '2342365',
name: 'awrAPI' },
{ owner: false,
id: '5675689699',
name: 'TgatAPI' },
{ owner: true,
id: '57456767',
name: 'ytasyAPI' }
[
Currently the way i have it set up is through a simple for in.
for( var key in guilds ) {
Guild
.findOrCreate({where: {primid: guilds[key].id}, defaults: {
primid: guilds[key].id,
name: guilds[key].name,
owner: guilds[key].icon
}})
.spread(function(guild, created) {
console.log(guild.get({
plain: true
}))
console.log(created)
})
}
I assume this is wrong and was wondering if there is a better way to loop through my object and chain the findorcreates. Currently it goes through the first object, but then does not add any more. I've been looking into using Bluebird, but I'm not too familiar with using promises. Thoughts?
var myData = [
{
owner: false,
id: '2342365',
name: 'awrAPI'
},
{
owner: false,
id: '5675689699',
name: 'TgatAPI'
},
{
owner: true,
id: '57456767',
name: 'ytasyAPI'
}
];
Promise.all(myData.map(function(value) {
// Do your thing
return Guild.findOrCreate...
})).then(function() {
// All is resolved do your next thing
})
An alternative would be to utilize squelize's bulkCreate model method for multiple records insertion.
link to sequelize's bulkCreate docs
quick snippet here:
const dataForInsertion = [
{
username:"daniel",
currentORM:"Sequelize"
},
{
username:"lekan",
currentORM:"Mongoose"
},
]
Guild.bulkCreate(dataForInsertion)
.then(()=>{})
.catch(()=>{})

Resources