mongodb: Find elements in array field - mongodb

My post schema looks like this.
const Post = new Schema({
author: String,
view: Number,
point: Number,
title: String,
images: [Schema.Types.Mixed],
content: String,
tags: [String],
comments: [{ displayName: String, body: String, createdAt: Date }],
createdAt: {
type: Date,
default: Date.now
},
editedAt: Date
})
And my query is:
Post.statics.findByTags = function(tags) {
return this.find({
tags: { $in: tags }
}).
limit(10).
sort({ createdAt: -1 }).
exec()
}
What I'm trying to do is match posts which have one of the tags provided by parameter "tags".
ex) if tags equals ["tag1", "tag2"] and
Post1 = { ...some values, tags: ["tag1"]},
Post2 = { ...some values, tags: ["tag1", "tag2"]}
I want to match both posts.
I'm using Postman to test those queries and now I'm getting nothing except an empty array.

I found mistakes that I made.
The query wasn't wrong and what I did wrong was saving "tags" field with JSON like String like "['xxx', 'xxxx']". it seems perfect array but yeah, it is just a string and I couldn't get the result I expected.
so, I added JSON.parse in "createPost" method and it now produces the right result.
// tags should be array type, so parse them
let { tags } = body
tags = JSON.parse(tags)
const { author, title, images, content } = body

Related

mongoose: getting unknown objectId when stored string instead of objectId

According to me when i pushed 'ABC' instead of objectId, It should show some error like
Cast to ObjectId failed for value "ABC" at path "likes".
but when i print that updated data i saw some unknown objectId in likes array
My question is, what is that unknown objectId(6a61736f6e20626f75726e65) and why it is generated
automatically
User Model
new Schema({
name: { type: String,required:true},
}, { usePushEach: true ,timestamp:true});
Feed Model
new Schema({
user: { type: Types.ObjectId, ref: 'user', required: true },
name: { type: String,default:null, trim: true },
likes: [{ type : Types.ObjectId, ref: 'user',}],
}, { usePushEach: true ,timestamp:true});
In Feed Schema likes have reference to User Schema
var data = await feed.findByIdAndUpdate(
postId,
{
$push: { likes: 'ABC' }
}
);
if(data){
var data = await feed.findById(postId);
console.log(data.likes);//["6a61736f6e20626f75726e65"]
}
OutPut:
["6a61736f6e20626f75726e65"]
Since you are pushing a String in likes which is not an ObjectId, you are getting this error.
Your schema clearly states that likes is an array of ObjectId:
likes: [{ type : Types.ObjectId, ref: 'user'}],
so you have to pass an ObjectId instead of user's name.
You have to do this:
// Whenever you are pushing value in `likes` `array`,
// you need to get the `ObjectId` of the user who liked the feed.
let userId = <userId_of_Ishwar>;
var data = await feed.findByIdAndUpdate(
postId,
{
$push: { likes: userId }
});

Mongoose Model containing arrays

First of all, I'm pretty new to MongoDB, Mongoose and Express. I'm trying to create a Mongoose model that has two arrays that I want to populate with multiple objects called itemSchema but I'm not sure how I'm supposed to update the array short of using findOneAndUpdate but since my array is initially empty there is no initial ID until a document is created. With the method that I have defined below - any already existing data in the food array is replaced by a new array. Below is my model -
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
id: String,
drinks: [
{
id: String,
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
food: [
{
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
]
});
module.exports = Item = mongoose.model("item", itemSchema);
I don't know if I'm defining the schema correctly. I know that it isn't very DRY ( since both arrays contain the same types ) but since I believe this is such a simple use case I don't want to define two separate schema for Drink and Food when I could just create one Schema.
router.post("/food", async (req, res) => {
try {
// Create an object from the request that includes the name, price and description
const newItem = {
name: req.body.name,
price: req.body.price,
description: req.body.description
};
// pass the object to the Items model
let item = new Items(newItem);
// add to the comments array
console.log("the new comment ", newItem);
item.food.unshift(newItem);
item.save();
// return the new item array to confirm adding the new item is working.
res.json(item);
} catch (error) {
// Display an error if there is one.
res.send(404).json(error);
}
});
The issue with the approach above comes from how I'm supposed to update the array. I defined the function below to update the food array for example but a new array gets created every single time. I believe that is has to do with not having Id param that I can use to provide the model with the findOneAndUpdate method. Any help would be greatly appreciated. Thank you in advance.
As per my opinion you can make your schema more simple as in your food and drinks array all the fields are same so you can simply take one more field as itemType and then you do not need to take two separate sub docs for food and drinks.
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
id: String,
itemType: { type: String }, // FOOD or DRINK
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
});
If you wants to know more about updating in array with findOneAndUpdate() then i will explain two simple task to perform with this function.
CASE:1 If array of your sub doc is empty then you can push new document in your sub doc as below:
var updatedData = await Model.findOneAndUpdate(
{
_id: doc._id
},
{
$push: {
drinks: {
name: drink.name,
price: drink.price,
description: drink.description,
}
},
},{ new: true }
).lean().exec();
CASE:2 If you want to update existing sub doc by sub doc id then you can update as below:
var updatedData = await Model.findOneAndUpdate(
{
'drink._id': drinkId
},
{
$set: {
'drink.$.name': drink.name,
'drink.$.price': drink.price,
'drink.$.description': drink.description,
},
},{ new: true }
).lean().exec();

Get documents with specific value in an array inside the document in mongodb

my document is as below
const mailSchema = new Schema ({
from: String,
to: [{
emailId: String,
status: Number
}],
cc: [{
emailId: String,
status: Number
}],
bcc: [{
emailId: String,
status: Number
}],
subject: String,
content: String,
status: Number,
createdBy: Schema.Types.ObjectId
}
How to retrieve document where 'to.emailid' contains a given value?
I have used below code but it is not working.
const emailtext = 'test#gmail.com'
MailSchema.find({'to.emailId': emailtext });
I think your schema is incorrect. what you want to do is to have a document that contain an object file. But what you did is a document with an array of file objects. so i think you should remove all the square bracket in the schema, then it should look like this.
//Mail Schema without array of objects.
const mailSchema = new Schema ({
from: String,
to: {
emailId: String,
status: Number
},
cc: {
emailId: String,
status: Number
},
bcc: {
emailId: String,
status: Number
},
subject: String,
content: String,
status: Number,
createdBy: Schema.Types.ObjectId
}
However if what you really want is an array of objects, then your query should also include the index of the array you want to get the emailId from.
Example.
MailSchema.find({'to[0].emailId': emailtext });

Is an ObjectId automatically generated for a nested object?

My schema is as follows:
const MessageType = {
// ...
oAuth: { provider: String, id: String },
attachments: [ {name: String, contentType: String} ],
// ...
}
MessageSchema = new mongoose.Schema(MessageType, { timestamps: true});
Messages = mongoose.model("Message", MessageSchema);
When I insert a new Message document using Messages.create, an ObjectId (_id) is also generated for attachments, in addition to my name and contentType fields, ie:
[ { name: "xxx", contentType: "yyy", _id: zzzzzz }]
Why is this happening, for attachments but not oAuth?
For avoiding that the _id was generated you must set the option _id: false, Also if you don't want to save the empty attachments object, you need to set default: undefined.
const MessageTypeSchema = new mongoose.Schema({
oAuth: {
type: String
},
attachments: {
type: [
{
type: String
}
],
_id: false,
default: undefined
}
});
Here the code that I used to test:
console.log('-------- Document with attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx',
attachments: ['teste.png','teste2.jpg']
}).save().then(result => {
console.log(result);
});
console.log('-------- Document without attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx'
}).save().then(result => {
console.log(result);
});
And here the result of execution:
Mongoose creates _id for single nested subdocuments or arrays, and your object field oAuth is not one of this cases:
Subdocuments are documents embedded in other documents. In Mongoose,
this means you can nest schemas in other schemas. Mongoose has two
distinct notions of subdocuments: arrays of subdocuments and single
nested subdocuments.
Each subdocument has an _id by default. Mongoose
document arrays have a special id method for searching a document
array to find a document with a given _id.
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
Link of Mongoose documentation: Mongoose SubDocs
You can define _id : false in attachments array.
const MessageType = {
// ...
attachments: [ {name: String, contentType: String, _id: false} ],
// ...
}

What's a good way to look up list of enumerated values?

I have a collection of case with a field named status (integer) whose valid values are 0, 1, 2, 4 and 8, representing "New", "WIP", "Solved", "Canceled" and "Closed" respectively.
So, in mongoose syntax, it might be like:
const caseSchema = new Schema({
createdOn: Date,
subittedBy: String,
status: Number,
...
});
const statusSchema = new Schema({
value: Number,
description: String
});
Is this a good way to organize the data? How do I make a query to retrieve cases with the status field properly filled with the description?
It is one way to do it sure. You could do the query by using $lookup. It would look something like this:
db.getCollection('<YourCasesColName>').aggregate([
{ $match : { 'status' : 1 } }, // or { $in: [1,2,3] },
{
$lookup: {
from: '<YourStatusColName>',
localField: 'status',
foreignField: 'value',
as: 'statusDoc',
}
}
])
Another way is to add a reference to the actual status via ObjectId so that instead of numbers in the cases you would be storing references to the actual Status objects and in this way have a better referential integrity. However you would still need to do similar query to get both in one shot. So here is what I am talking about:
const caseSchema = new Schema({
createdOn: Date,
subittedBy: String,
status: { type: mongoose.Schema.Types.ObjectId, ref: 'Status' },
// ^ now your status hows reference to exactly the type of status it has
});
const statusSchema = new Schema({
value: Number,
description: String
});
So the actual data would look like this:
// Statuses
[{
_id: <StatusMongoObjectID_1>,
value: 1,
description: 'New'
},{
_id: <StatusMongoObjectID_2>,
value: 2,
description: 'New'
}]
// Cases
[{
_id: <MongoObjectID>,
createdOn: '<SomeISODate>',
subittedBy: '<SomeString>',
status: <StatusMongoObjectID_1>
},
{
_id: <MongoObjectID>,
createdOn: '<SomeISODate>',
subittedBy: '<SomeString>',
status: <StatusMongoObjectID_2>
}]