Using same column with different valus in mongodb - node.js

Hi I tried to use the same column name more than once in mongoose but it showing different output.Instead of that I need the expected output is there any way to achieve it.Can anyone help me
const mongoose = require('mongoose')
mongoose.connect('----------------------------------------------',
{useNewUrlParser:true,useCreateIndex:true,useUnifiedTopology:true})
const wavicle = mongoose.model('Datasetlist',{
Dataset:[{
id:Number,
dataset:String,
parameter: [{
Name:String,
Name:String,
Name:String,
Name:String,
Name:String,
Name:String
}]
}]
})
const me = new wavicle({
Dataset:[{
id:1,
dataset:'Daily Food Report',
parameter: [{
Name:'StoreName',
Name:'Dates',
Name:'TransDesc',
Name:'Amount',
Name:'credit'
}]
}]
})
me.save().then(()=>{
console.log(me)
}).catch(()=>{
console.log(error)
})
OUTPUT:
_id:5f044c0165a79639745ce2a1
Dataset:Array
0:Object
_id:5f044c0165a79639745ce2a2
id:1
dataset:"Daily Food Report"
parameter:Array
0:Object
_id:5f044c0165a79639745ce2a3
Name:"credit"
__v:0
But I need the output as
EXPECTED OUTPUT:
dataset:[
{id:1,dataset:'Daily Food Report',parameter: [
{name:'Store Name'},
{name:'Dates'},
{name:'TransDesc'},
{name:'Amount'},
{name:'credit'},
{name:'Total'}
]

There are 3 issues here:
The schema definition: Although it actually works the way you define multiple keys with the same name, but at the end the keys will overwrite each others and you end up with an object of only one key. The way to do is as follows:
{
Dataset:[{
id:Number,
dataset:String,
parameter: [{
Name: String // be carful with the case, your expect the output to have the key "name", but you defined as "Name", the keys are case sensitive
}]
}]
}
Document creation: as mentioned in the first issue. If you define multiple keys with the same name, they will overwrite each others, then you probably end up with a single object in an array like this: parameters: [{ Name:'credit' }]. The correct way is to write separate objects
const me = new wavicle({
Dataset:[{
id:1,
dataset:'Daily Food Report',
parameter: [{
Name:'StoreName'
},{
Name:'Dates'
},{
Name:'TransDesc'
},{
Name:'Amount'
},{
Name:'credit'
}]
}]
})
The logging: when you do console.log(me). It's possible that it won't print all nested structures, So all you see is Array while there are nested objects inside. You should do the following instead
console.dir(me, { depth: null })

Related

How to get categories and sub-categories in single API response

I have two collections Categories and Subcategories inside a Categories collection. I have an array that is storing the ids of subcategories that are stored in Subcategories collection. Below is my document structure:
Categories collection
{
id:65,
title:"Automotive",
active:true,
subcategories:[35,28,30]
}
subcategories collection
{
id:35,
title:"Automotive technology",
category_id:65,
active:true
},
{
id:28,
title:"Automotive coatings",
category_id:65,
active:true
},
{
id:30,
title:"Machinery and equipments",
category_id:65,
active:true
}
As seen in above collection 3 documents from subcategories collection have been associated with the category document. I want to fetch data in below format on single API hit.
API response should be in below format:
{
data:{
category:{
id:65,
title:"Automotive",
subcategories:[{
id:35,
name:"Automotive technology",
},
{
id:28,
name:"Automotive coatings",
},
{
id:30,
name:"Machinery and equipments",
}]
},
category:{
id:66,
title:"Food",
subcategories:[{
id:23,
name:"Drinks",
},
{
id:2,
name:"Additives",
}]
},
},
messsage:"Success",
code:200
}
As of now I am able to get data in 2 api hits that is like first getting all the categories
const category = await db.categories.find({});
Then on click of some particular category fetching all the sub categories based on the category id.
const subCategories = await db.SubCategories.find({category_id:id});
How can I get the above data in desired format in single API hit?
You need something like this, also, if you use mongoose, you can use .populate()
To format data you can use $project stage in aggregation pipeline or projection in .find()
If you want to use Mongoose with populate:
CategorySchema:
const CategorySchema= new mongoose.Schema({
...
subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: 'SubCategory' }],
...
});
need _id column on reference table
ref content is must be equal to model name like
module.exports = mongoose.model('SubCategory', SubCategorySchema);
Controller:
const categories = await Category.find({})
.populate({
path: 'subCategories'
})
path content is must be equal to column name
If you want to write with Mongo query:
db.getCollection("categories").aggregate([
{
$lookup:
{
from: 'subCategories',
localField: 'subCategories',
foreignField: 'id',
as: 'subCategories'
}
}
])
You get empty object because you are not using "_id" field for join schemas. If you want to use "id" field u need to use virtuals on Mongoose:
https://mongoosejs.com/docs/tutorials/virtuals.html#populate

Why is an array in my mongoose model preventing me from querying

I have the following mongoose model
CartSchema = new Schema({
company: String,
items: [{
_id: Schema.ObjectId,
price: Number,
gears: [String]
}],
});
I access it via this simpe query
const response = await Cart.findOne( { "items": { _id: "5e4d7a5bcff77131f46d8aa9" } });
And this is my data in the mongo database
So from this information we can see that the only information that I have in my database that corresponds to the model is the items[0]._id which should be found with the query above. The odd thing is it returns null as long as this line gears: [String], is in my model. It is not required (I also tried setting it manually to required : false but I can't seem to get my data if this line is in my model. If I remove the line from my model, I can get the data just fine.
Am I missing something obvious here that would prevent me from getting my data because of the gears: [String] line in my model?
by this way, { "items": { _id: "5e4d7a5bcff77131f46d8aa9" } }, you're searching for an exact match, the items should be an object contains only the specified _id
instead, you should use the dot notation to filter by the _id in the items array
const response = await Cart.findOne( { "items._id": "5e4d7a5bcff77131f46d8aa9" });
hope it helps

populate with condition in mongoose

I have a movie booking data like below
movie order schema
{
"movie_id": "5d64fb7975214a183bf10f5b",
"variant_id": "5d64fda8fc7f911a77afd55c",
}
and movie schema data like below
{
"_id":"5d64fb7975214a183bf10f5b",
"description":"Sahoo movie ",
"options":[
],
"variants":[
{
"enabled":true,
"_id":"5d64fda8fc7f911a77afd55c",
"variant_name":"",
"regular_price":345,
"sale_price":125,
"stock_quantity":45,
},
{
"enabled":true,
"_id":"5d661c8181a4572a27f048dd",
"variant_name":"",
"regular_price":120,
"sale_price":50,
"stock_quantity":10,
}
],
"on_sale":false,
"variable":true
}
now I'm trying to querying the movie order
let data = await MovieOrder.find().populate(movie_id)
but it is giving movie details with all the variant.but what I'm looking for here is
In the movie order what is the variant_id is present based on that I need to populate the movie with variant based on that variant id on the movie order
Note: the result should be, what are the variant_id in the movie order schema is equal to variant id in the movie schema
Hope you guys understand my problem, please give me the solution
With the way your schema is designed it is hard for populate to filter the movies variants array with the variant_id in the movies order
as this is not how populate works.
In order to use populate properly, you would have to change the movies schema making the variants array as ref to
Variants model. For example, your schema definitions would need to look like
Schema definitions
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const variantSchema = new Schema({
enabled: Boolean,
variant_name: String,
regular_price: Number,
sale_price: Number,
stock_quantity: Number
})
const Variant = mongoose.model('Variant', variantSchema)
const movieSchema = new Schema({
description: String,
options: [String],
variants: [{ type: 'ObjectId', ref: 'Variant' }],
on_sale: Boolean,
variable: Boolean
})
const Movie = mongoose.model('Movie', movieSchema)
await MovieOrder.find().populate('movie_id variant_id')
This way your query just returns the movie and the variant it needs.
However, if the current schema design remains as it is, you can use $lookup in an aggregate pipeline to do the populate and then filter the resulting array using $filter on the variant that matches the variant_id field in your MovieOrder model:
await MovieOrder.aggregate([
{ '$lookup': {
'from': 'movies',
'localField': "movie_id", // field in the movieorders collection
'foreignField': "_id", // field in the movies collection
'as': "movie"
} },
{ '$addFields': {
'movie': { '$arrayElemeAt': ['$movie', 0 ] }
} },
{ '$addFields': {
'movie.variants': {
'$filter': {
'input': '$movie.variants',
'cond': { '$eq': ['$variant_id', '$$this._id'] }
}
}
} },
]).exec()

Mongoose did not sent the data as expected

I had a mongoose object at server side:
...
item = {
name: "Test",
id: 1
}
// item's an mongo schema
// id and name defined in model as String and Number
Then I add into item new field mentions:
item.mention = [{ id: 1, ... }]
But I can't get mention at client side.
My response code:
res,json({ status: 1, message: 'success', data: item })
The response was data: { name: "Test", id: 1 }
I don't want to add mention into my mongo schema.
So, what's my problem?
How can I fix that?
Thanks!
The problem is that mongoose will not allow you to modify item document if a field you are trying to set value for does not exists in schema of the model, which is your case is "mention" field.
If you want to add "mention" field you have to access _doc field of the item document. Example :-
item._doc.mention = [{ id: 1, ... }]
Response should be:-
{ name: "Test", id: 1, mention: [{ id: 1, ... }] }
It is not usually recommended, but if you want to change schema like this.
you should pass strict false parameter to mongoose model while setting schema like this.
var ModelSchema = new Schema({
number: String,
}, { strict: false });
Now, it will change schema if you give any other parameter which is not in schema, it will add that one.

Which type to store an Array of MongoDB ObjectIds?

I want to save an Array of ObjectIds which I get via an REST interface via mongoose to a mongodb. I constantly run into the problem that I get a cast error when I save the objectsIds from the REST interface to the DB. The serverside code is written in typescript.
The schema is:
var WaSchema = new mongoose.Schema({
ownerId: { type: 'String', required: true },
options: { type: 'String', required: false },
launch: [{ type : 'ObjectId', required: true }],
});
From the REST interface I get for "lanch" an Array of strings: launch: Array<string>
Here is how I currently do the save:
WaModel.findOneAndUpdate(query, {
ownerId: userId,
options: wa.options,
launch: wa.launch
},
{ upsert: true },
(err, doc) => {
if (err) throw err
else return 'successfully saved/updated';
})
How must the ObjectId in the REST interface needs to look like to be correctly casted? Is it just a sting like '575e52790c0fc76a11e381d0' or does it need a prefix like ObjectId("575e52790c0fc76a11e381d0")?
How would the Array look like at the end? This depends a liitle on the answer of #1
I saw the populate function, can this be of help here?
1) If you are using mongoose then no need to add prefix ObjectId. You can save as an array of reference Ids.
var insertData = {
ownerId: userId,
options: wa.options,
launch: [ '56cea954d82cd11004ee67b5','56ceaa00d82cd11004ee67bc' ]
}
2) At the end your array will look like this.
"launch" : [
ObjectId("56cea954d82cd11004ee67b5"),
ObjectId("56ceaa00d82cd11004ee67bc")
],
3) And Yes, populate function will be helpful here. It will populate whole array. After populate it will look like
"launch" : [
{ _id: '56cea954d82cd11004ee67b5',
.... other fields
},
{ _id: '56ceaa00d82cd11004ee67bc',
.... other fields
},
]
I would say store the object ids in array with datatype ObjectId. So to do that you should convert the id in string format that is '575e52790c0fc76a11e381d0' into type ObjectId.
Use the following to convert it:
Consider the id is of a user (userId)
var stringUserId = '575e52790c0fc76a11e381d0';
var ObjectId = mongoose.Types.ObjectId;
var userId = ObjectId(stringUserId); //This should assign a value of datatype ObjectId to userId

Resources