When i am trying to hit the api for this part of code on postman i get error of Can't use $elemMatch I search but can't find any solution for that:-
const accessChat = asyncHandler(async (req, res) => {
const { userId } = req.body;
if (!userId) {
return res.status(400).json({err:'UserId param not sent with request'});
}
var isChat = await Chat.find({
isGroupChat: false,
$and: [
{ users: { $elemMatch: { $eq: req.user._id } } },
{ users: { $elemMatch: { $eq: userId } } },
],
})
.populate("users", "-password")
.populate("latestMessage");
isChat = await User.populate(isChat, {
path: "latestMessage.sender",
select: "name pic email",
});
if (isChat.length > 0) {
res.send(isChat[0]);
} else {
var chatData = {
chatName: "sender",
isGroupChat: false,
users: [req.user._id, userId],
};
try {
const createdChat = await Chat.create(chatData);
const FullChat = await Chat.findOne({ _id: createdChat._id }).populate(
"users",
"-password"
);
res.status(200).json(FullChat);
} catch (error) {
res.status(400);
throw new Error(error.message);
}
}
});
Error image is attached
https://i.stack.imgur.com/HXvTR.png
Related
I'm using react with typescript and redux-toolkit when I want to show the user who is logged in this error occurs error
here's the use Selector in layout.tsx:
const {user} = useSelector((state:any) => state.user);
and here's the display in html (also layout.tsx):
<Link className="anchor" to='/profile'>{user?.name}</Link>
my Reducer ,userSlice.tsx:
export const userSlice = createSlice({
name: "user",
initialState: {
user: null,
},
reducers: {
setUser: (state,action) => {
state.user= action.payload;
},
},
});
export const { setUser } = userSlice.actions;
and this a part of the protectedRoute.tsx the part which I want to retrieve the username:
const getUser = async () => {
try {
dispatch(showLoading());
const response = await axios.post(
"/api/users/user",
{ token: localStorage.getItem("token") },
{
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
}
);
dispatch(hideLoading());
if (response.data.success) {
dispatch(setUser(response.data.data));
} else {
navigate("/login");
}
} catch (error) {
dispatch(hideLoading());
navigate("/login");
}
};
useEffect(() => {
if (!user) {
getUser();
}
}, [user]);
if (localStorage.getItem("token")) {
return props.children;
} else {
return <Navigate to="/login" />;
}
I am trying to retrieve data from my MongoDB database which stores chat conversations. This works fine and returns what I want. However, I only save userIDs in MongoDB, so I need to query profile picture, username etc from my MySQL database. I tried the following:
app.get('/api/retrieveAllChats', (req, res) => {
var Conversation = mongoose.model('Conversation', ConversationSchema);
var ChatMessage = mongoose.model('Message', ChatMessageSchema);
var userID = req.query.userID.toString()
var members = []
var conversationData = []
var retrieveAllChats = new Promise(function(resolve, reject) {
Conversation.aggregate([{ $match: { "members.uID": userID } }, { $lookup: { foreignField: "c_ID", from: "messages", localField: "_id", as: "messages" } }, { "$unwind": "$messages" }, { "$sort": { "messages.t": -1 } }, { "$group": { "_id": "$_id", "lastMessage": { "$first": "$messages" }, "allFields": { "$first": "$$ROOT" } } }, { "$replaceRoot": { "newRoot": { "$mergeObjects": [ "$allFields", { "lastMessage": "$lastMessage" } ] } } }, { "$project": { "messages": 0 } }], function (err, conversations) {
if (err) return handleError(err);
conversations.forEach((conversation, i) => {
return new Promise(function (resolveConversations, rejectConversations) {
var membersPromise = conversation.members.forEach((member, x) => {
return new Promise(function (resolveUserData, rejectUserData) {
getUserData(member["uID"], function(userData) {
members.push({userID: member["uID"], joinDate: member["j"], userName: userData["userName"], userDisplayName: userData["userDisplayName"], userVerified: userData["userVerified"], userProfilePicURL: userData["userProfilePicURL"]})
console.log("userData: ", userData)
conversations[i].members[x].userData = userData
conversationData = conversations
resolveUserData({userID: member["uID"], joinDate: member["j"], userName: userData["userName"], userDisplayName: userData["userDisplayName"], userVerified: userData["userVerified"], userProfilePicURL: userData["userProfilePicURL"]})
})
})
})
resolveConversations()
})
})
resolve()
})
}).catch(error => {
console.log(error)
res.json({ errorCode: 500 })
})
retrieveAllChats.then(function() {
res.header("Content-Type",'application/json');
res.send(JSON.stringify(conversationData, null, 4));
})
})
However, the conversationData array is always empty. So I need a way to resolve the retrieveAllChats promise and pass the data I added to the existing conversations object to return it with all information I need. Any ideas on how I can do this? (getUserData is a function to retrieve the MySQL data, this one works fine and returns what I want)
You are trying to do async operation inside forEach which wouldn't work. You need to either use for...of or Promise.all.
Also, you can make this code much cimpler by using .exec() at the end of running any query or aggregation as that is supported by mongoose. Something like this should work. Make sure you change your routte line to this to tell it is an async function
app.get("/api/retrieveAllChats", async (req, res) => {
core logic
const conversions = await Conversation.aggregate([{"$match": {"members.uID": userID}}, {"$lookup": {"foreignField": "c_ID", "from": "messages", "localField": "_id", "as": "messages"}}, {"$unwind": "$messages"}, {"$sort": {"messages.t": -1}}, {"$group": {"_id": "$_id", "lastMessage": {"$first": "$messages"}, "allFields": {"$first": "$$ROOT"}}}, {"$replaceRoot": {"newRoot": {"$mergeObjects": ["$allFields", {"lastMessage": "$lastMessage"}]}}}, {"$project": {"messages": 0}}]);
for(const conversation of conversations) {
for(const member of conversation.members) {
// add your promise call here and either await it or use then to get the promise value.
}
}
I want to set the depth of JSON parsing in Express middleware express.json().
For example, if I would set the option to parse the depth=1, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: "[object Object]" }
-- or --
When I set depth=2, then
'{ "email": { "$ne": "user#example.com" } }'
will be parsed to
{ email: { '$ne': 'user#example.com' } }
And so on,
In this case, there will be no issue of default depth, as the developer will be aware of how many nesting they will allow while development.
PS: It will prevent the application from being vulnerable to NoSQL Injection.
Just write you own middleware:
const get_depth = (obj) => {
let depth = 0
for(const key in obj) {
if( obj[key] instanceof Object ) {
depth = Math.max(get_depth(obj[key]), depth)
}
}
return depth+1
}
const depth_limit = 2
const limit_depth = function(req, res, next) {
if( get_depth(req.body) > depth_limit ) throw new Error("Possible NoSQL Injection")
next()
}
app.use(limit_depth)
Or, if you prefer "[object Object]":
let limit_depth = (obj, current_depth, limit) => {
for(const key in obj) {
if( obj[key] instanceof Object ) {
if( current_depth+1 === limit ) {
obj[key] = "[object Object]" // or something similar
}
else limit_depth(obj[key], current_depth+1, limit)
}
}
}
app.use(function(req, res, next) { limit_depth(req.body, 0, depth_limit); next() })
I write down the query, Maximum 6-8 depth goes. when use lookup inside the lookup.
const [result] = await Collection.aggregate([
{ $match:statusObj },
{
$project:{
_id:1,
name:1
}
},
{
$lookup:{
from:"articles",
let: { "cat_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$category_id", "$$cat_id"] },
{ $eq: ["$isDeleted", false] },
{ $eq: ["$type", type] }
]
}
}
},
{
$lookup:{
from:"view_articles",
let: { "article_id":"$_id"},
pipeline:[
{
$match:{
$expr:{
$and: [
{ $eq: ["$article_id", "$$article_id"] },
{ $eq: ["$isDeleted", false] }
]
}
}
}
],
as:"viewCount"
}
},
{
$addFields:{
noOfViewCount : { $size:"$viewCount"}
}
} ],
as:"articleCategoryData"
}
},
{
$addFields: {
postCount: {$size:"$articleCategoryData" },
tempsArray: { $map:
{
input: "$articleCategoryData",
as: "tempData",
in: { $add: "$$tempData.noOfViewCount" }
}
},
},
},
{
$addFields: {
viewCount:{ $sum:"$tempsArray" }
},
},
{
$project:{
_id: 1,
name: 1,
postCount: 1,
viewCount: 1
}
},
{
$facet: {
count: [
{
$count: "total"
}
],
result: [{ $match: {} }, { $skip: skipRecord }, { $limit: limit }]
}
}
]);
you can set depth to 10. If you feel JSON is coming wrong then increase it :)
In case anyone who doesn't want to change the value of req.body, can use this function from here
function serializer(payload: any, cdepth: number, options: Options): void {
const main: any = {}
const maxDepth = typeof options.maxNestingLevel == 'number' ? (options.maxNestingLevel == 0 ? 1 : options.maxNestingLevel) : 1
for (const key in payload) {
// check for object
if (payload[key] instanceof Object) {
// check if depth is limited, replace if needed
if (cdepth === maxDepth) {
main[key] = options.replaceWith
} else {
// serialize the nested
main[key] = serializer(payload[key], cdepth + 1, options)
}
} else {
// add to main object if not to be checked
main[key] = payload[key]
}
}
return main
}
I have JSON called by fetch request that looks like this:
[{
"nameSecond": "",
"Id": "",
"First": {
"nameFirst": "",
"Id": ""
}
},
{
"nameSecond": "",
"Id": "",
"First": {
"nameFirst": "",
"Id": ""
}
},
{
"nameSecond": "",
"Id": "",
"First": {
"nameFirst": "",
"Id": ""
}
},
{
"nameSecond": "",
"Id": "",
"First": {
"nameFirst": "",
"Id": ""
}
}
]
I want to replace an object of another JSON to every object of this JSON.
The second JSON which is going to be added to first JSON looks like this:
[{
"nameFirst": "",
"id": ""
},
{
"nameFirst": "",
"id": ""
},
{
"nameFirst": "",
"id": ""
},
{
"nameFirst": "",
"id": ""
}]
What I did is that when ChangeObjectFirst was run ,the object is clicked will be replace by the object Firstof firstJSON and new data will be shown.
<div onClick={((e) => this.ChangeObjectFirst(e, i))}>Change</div>
I used Object.assign({}, itemToReplace) to replace objects but
The main problem is that it will be done just for the first time. For the second time or more clicked object will not be replaced by object First and there will be this TypeError: el is undefined
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
dataNew: [],
library: null,
libraryNew: null,
}
}
componentDidMount() {
fetch('/json.bc', {
method: 'POST',
})
.then(response => response.text())
.then(text => {
const Maindata = JSON.parse(text.replace(/\'/g, '"'))
this.setState(state => ({
...state,
data: Maindata
}), () => {
this.reorganiseLibrary()
})
}).catch(error => console.error(error))
fetch('/json2.bc', {
method: 'POST',
})
.then(response => response.text())
.then(text => {
const Maindata = JSON.parse(text.replace(/\'/g, '"'))
this.setState(state => ({
...state,
dataNew: Maindata
}), () => {
this.reorganiseLibraryNew()
})
}).catch(error => console.error(error))
}
reorganiseLibrary = () => {
const { data } = this.state;
let library = data;
library = _.chunk(library);
this.setState({ library })
}
reorganiseLibraryNew = () => {
const { dataNew } = this.state;
let libraryNew = dataNew
libraryNew = _.chunk(libraryNew);
this.setState({libraryNew})
}
renderLibrary = () => {
const { library } = this.state;
if (!library || (library && library.length === 0)) {
return ''
}
return library.map((item, i) => (
<div>
{item.First.nameFirst}
{item.nameSecond}
</div>
))
}
renderLibraryNew = () => {
const { libraryNew } = this.state;
if (!libraryNew || (libraryNew && libraryNew.length === 0)) {
return ''
}
return libraryNew.map((item, i) => (
<div>
{item.nameFirst}
<div onClick={((e) => this.ChangeObjectFirst(e, i))}>Change</div>
</div>
))
}
render() {
const { library, libraryNew } = this.state;
return (
<div>
{this.renderLibrary()}
{this.renderLibraryNew()}
</div>
)
}
ChangeObjectFirst = (e, i) => {
const itemToReplace = this.state.libraryNew[i];
let { data } = this.state;
data = data.map(el => {
el['First'] = Object.assign({}, itemToReplace);
});
this.setState({ data: data });
}
}
ReactDOM.render(<App />, document.getElementById('Result'))
The issue is that while updating, you haven't returned the new data from map. Also since you are using library variables for rendering, you need to call reorganiseLibrary after updating data. A better implementation without mutation would be as below
ChangeObjectFirst = (e, i) => {
const itemToReplace = this.state.libraryNew[i];
let { data } = this.state;
data = data.map((el, idx) => {
return Object.assign({}, el, { First: itemToReplace});
});
this.setState({ data: data }, ()=> {this.reorganiseLibrary()});
}
I would like to display the number of tasks elements in JSON, but I do not know how to go about it.
I want to make something like this:
Tasks to do 2/12 (where 2 - tasks with flag 1, 12 - all tasks)
I tried using the lenght function, but I got the information function lenght is not defined, similarly with the slice function.
[
{
"id":1,
"clients_id":1,
"products_id":1,
"tasks_id":1,
"project_name":"Some project",
"created_at":null,
"updated_at":null,
"clients":{
"id":1,
"client_name":"Some client",
"contact_name":"Some client",
"client_phone":"123123123",
"client_mail":"clientmail#mailclient.com",
"client_nip":"1112223333",
"client_logo":"logo.jpg",
"updated_at":"2019-04-11 09:45:11",
"created_at":"-0001-11-30 00:00:00"
},
"products":{
"id":1,
"product_name":"Some product",
"product_description":"Really nice product bro",
"product_price":"999$",
"updated_at":"2019-04-08 14:35:13",
"created_at":null
},
"tasks":[
{
"id":1,
"project_id":1,
"task_name":"First task",
"task_description":"its very hard task",
"task_due":"2099-01-12 00:00:00",
"status":0,
"created_at":null,
"updated_at":"2019-04-11 14:09:08"
},
{
"id":2,
"project_id":1,
"task_name":"fix task 1",
"task_description":"or something else",
"task_due":"2201-01-12 00:00:00",
"status":1,
"created_at":null,
"updated_at":"2019-04-11 14:10:11"
}
]
}]
<script>
export default {
mounted() {
let app = this;
let id = app.$route.params.id;
app.id = id;
axios.get('/api/v1/projects/' + id)
.then(function (resp) {
app.project = resp.data;
})
.catch(function () {
alert("Could not load your projects")
});
},
data: function () {
return {
//client_id: null,
project: {
id: '',
clients_id: '',
products_id: '',
tasks_id: '',
project_name: '',
updated_at: '',
created_at: '',
clients: ''
},
task: {
status: ''
}
//client: []
}
},
methods: {
saveForm() {
var app = this;
var newproject = app.project;
axios.patch('/api/v1/projects/' + app.id, newproject)
.then(function (resp) {
app.$router.replace('/c/');
})
.catch(function (resp) {
console.log(resp);
alert("Could not create your company");
});
},
taskDone(taskid, projectid){
var app = this;
{{app}};
var newtask = app.task;
var flag = 1;
axios.patch('/api/v1/tasks/' + taskid + '?status='+flag)
.then(function (resp) {
app.$router.push('/pr/view/' + projectid);
location.reload();
})
.catch(function (resp) {
console.log(resp);
alert("Could not create your company");
});
},
taskUnDone(taskid, projectid){
var app = this;
{{app}};
var newtask = app.task;
var flag = 0;
axios.patch('/api/v1/tasks/' + taskid + '?status='+flag)
.then(function (resp) {
app.$router.push('/pr/view/' + projectid);
location.reload();
})
.catch(function (resp) {
console.log(resp);
alert("Could not create your company");
});
}
}
}
</script>
You could create a computed function that returns the length of tasks filtered by status of 1.
computed() {
status() {
const tasks = this.project.tasks;
const complete = tasks.filter(task => task.status === 1);
return `${complete.length}/${tasks.length}`;
}
}
Then use status as a "variable" in your markup.
<p>Tasks done: {{ status }}</p>