How to get sub document only in mongoose? - json

I'm trying to extract only sub document from an array has the following schema :
const UserSchema = Schema({
name: {
type: String
},library:[{
story:{type: Schema.Types.ObjectId,ref: 'Story'}
}],
});
i tried to use :
module.exports.getUserStories = function(userId, callback){
User.findOne({_id: userId },callback)
.select('library.story')
};
and it gives this result :
{
"_id": "5949615072e15d2b34fa8f9d",
"library": [
{
"story": "592ae46cf2a0ba2b208cb092"
},
{
"story": "592ae608df26d80790092fe9"
},
{
"story": "592ae46cf2a0ba2b208cb092"
}
]
}
but what i'm expecting to get is only this :
[
{
"story": "592ae46cf2a0ba2b208cb092"
},
{
"story": "592ae608df26d80790092fe9"
},
{
"story": "592ae46cf2a0ba2b208cb092"
}
]
I already tried to use double selection like :
module.exports.getUserStories = function(userId, callback){
User.findOne({_id: userId },callback)
.select('library.story')
.select('story')
};
But is gives the same result

Try this one :
module.exports.getUserStories = function(userId, callback){
User.find({_id: userId },{'library.story'}).then(function(user){
if(user){
callback(user.library);
}});
};
Docs here

This output is expected to return by "select" but simply you can prepare the returned data to be as you need as following:
User.findOne({_id: userId }).select('library').then(function(result){
if(result){
//If there is returned item
var stories = result.library;
//Continue ...
}
},function(error){
//Error handling
})

Related

Retrieving data from MongoDB ánd MySQL simultaneously

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.
}
}

how to change json format in asp.net mvc?

I'm trying to make autocomplete textbox using this link
https://github.com/devbridge/jQuery-Autocomplete
but I got this error
Uncaught TypeError: Cannot read property 'length' of undefined
this is my action method
public JsonResult GetNews(string prefix)
{
var newsList = NewsDataRoot.AutoCompleteTitle(prefix).Select(n => new
{
value = n.Title,
data = n.Id
}).ToList();
var myjson = Json(newsList, JsonRequestBehavior.AllowGet);
return myjson;
}
and it return this result when I test it in browser
[{"value":"this is a test","data":2006}]
I found the format must be
{
suggestions: [{
"value": "United Arab Emirates",
"data": "AE"
}, {
"value": "United Kingdom",
"data": "UK"
}, {
"value": "United States",
"data": "US"
}, {
"value": "United Funes",
"data": "DAN"
}]
}
how can do this?
thanks a lot!
also as you can see I tried transformResult but it doesnt worked
<script>
$('#autocomplete').autocomplete({
serviceUrl: '/TestAutoComplete/GetNews',
paramName: 'prefix',
transformResult: function(response) {
return {
suggestions: $.map(response.myData, function(dataItem) {
return { value: dataItem.valueField, data: dataItem.dataField };
})
};
},
onSelect: function (suggestion) {
alert('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
</script>
Try this, creates an anonymous object which just has the suggestions property
var newsList = NewsDataRoot.AutoCompleteTitle(prefix)
.Select(n => new {
value = n.Title,
data = n.Id
}).ToList();
var myjson = Json(new { suggestions = newsList }, JsonRequestBehavior.AllowGet);
if you want to set number to be numeric string you can try convert its value to string
public JsonResult GetNews(string prefix)
{
var newsList = NewsDataRoot.AutoCompleteTitle(prefix).Select(n => new
{
value = n.Title,
data = n.Id.ToString()
}).ToList();
var myjson = Json(new {suggestions = newsList}, JsonRequestBehavior.AllowGet);
return myjson;
}

saving in the database variable as ObjectID() MongoDB, NodeJS

I created function which is adding people_id inside array of given card. But there is problem that inserted id is always not as an objectId() where I just need it to be saved as objectId.
When id is added to array i I'm sending whole variable board JSON to nodejs API where is executed function findOneAndUpdate. And here is problem because after saved this arrays is not objectID in Author. Can someone tell me how to make it?
JSON board
{
"_id" : ObjectId("59e096a7622a3825ac24f343"),
"name" : "1",
"users" : [
ObjectId("59cd114cea98d9326ca1c421")
],
"lists" : [
{
"list" : "1",
"cards" : [
{
"name" : "2",
"Author" : [
"59df60fb6fad6224f4f9f22d",
"59df60fb6fad6224f4f9f22d",
"59df60fb6fad6224f4f9f22e"
]
},
{
"name" : "3",
"Author" : []
}
]
},
{
"list" : "fea",
"cards" : [
{
"name" : "card",
"Author" : []
}
]
}
],
"__v" : 0 }
Router:
router.post('/add/member', function (req, res, next) {
console.log(req.body)
Board.findOneAndUpdate({ _id: req.body._id },
{
$set: {
lists : req.body.lists
}
},
{
upsert: true
},
((cards) => {
res.send(cards)
})
)
});
model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BoardSchema = new Schema({
name: { type: String, maxlength: 20 },
lists : { type: Array },
users : [{ type : Schema.Types.ObjectId, ref: 'User' }],
});
module.exports = mongoose.model('Board', BoardSchema);
And here is function with adding poeple
$scope.addMemberToCard = (indexList, indexCard, member) => {
$scope.board.lists[indexList].cards[indexCard].Author.push(member);
console.log( $scope.board.lists[indexList].cards[indexCard].Author)
return ApiService.staff.addMemberToCard($scope.board).then(function () {
})
}
You could use the mongoose Types ObjectID method and then transform the string sent into an ObjectID.
const id = new new mongoose.Types.ObjectId(Author);

Getting json object data with react

I am attempting to pull data out of json like this, which is imported as "values"
{
"content": {
"person": [
{
"name": "Test"
"age" : "24:
}
]
}
}
I am using .map like below but getting the error .default.map is not a function I believe it is because i have objects not arrays, i've tried a bunch of stuff including object.keys but i'm getting errors all over the place, any direction would be appreciated.
import values from './sample.json'
const vals = values.map((myval, index) => {
const items = person.items.map((item, i) => {
return (
<div>{item.name}</div>
)
})
return (
<div>{items}</div>
)
})
I think your data and code have some errors. But after fixing those and also changing the name from 'person' to 'people' if that's what you are after, here's the code that does what you are trying to do:
var data = {
content: {
people: [
{
name: "Test",
age: 24,
},
{
name: "Foo",
age: 25,
},
],
},
};
var App = React.createClass({
render: function () {
var people = data.content.people.map(function (person) {
return <div>{person.name}</div>;
});
return <div>{people}</div>;
},
});
ReactDOM.render(<App />, document.getElementById("app"));
And here's the JSBin for that: https://jsbin.com/coyalec/2/edit?html,js,output
Update: I'm updating the answer with more detailed example. It now deals with data more generically, like it doesn't assume what are the entries of 'contents' and such, but it knows that each type like 'people' or 'pets' are an array.
var data = {
content: {
people: [
{
name: "Test",
age: 24,
},
{
name: "Foo",
age: 25,
},
],
pets: [
{
name: "Sweety",
age: 3,
},
{
name: "Kitty",
age: 5,
},
],
},
};
var App = React.createClass({
render: function () {
// Get the keys in data.content. This will return ['people', 'pets']
var contentKeys = Object.keys(data.content);
// Now start iterating through these keys and use those keys to
// retrieve the underlying arrays and then extract the name field
var allNames = contentKeys.map((t) =>
data.content[t].map((e) => <div>{e.name}</div>)
);
return <div>{allNames}</div>;
},
});
ReactDOM.render(<App />, document.getElementById("app"));
And here's the latest JSBin: https://jsbin.com/coyalec/4/edit?html,js,output

Nested arrays in object json to csv

Im using 'json-csv' library to create a csv from a users arrays with nested objects and arrays.
var users = [
{
subscriptions: [
{
package : {
name: 'Grammar'
},
state: 'EXPIRED',
timerange: {
period : 5550
},
transaction:{
amount: 10000
}
},
{
package : {
name: 'GK'
},
state: 'ACTIVE',
timerange: {
period : 30
},
transaction:{
amount: 10340
}
},
],
account:{
balance: 200
},
name: "Johhy Moe",
email: null,
user_id: "123456789",
username: null,
user_type: "facebook",
id: 3,
createdAt: "2016-07-11T08:02:40.000Z",
updatedAt: "2016-07-11T08:02:40.000Z",
},
{
subscriptions: [
{
package : {
name: 'GK'
},
state: 'EXPIRED',
timerange: {
period : 42
},
transaction:{
amount: 5252
}
},
{
package : {
name: 'MATH'
},
state: 'ACTIVE',
timerange: {
period : 25
},
transaction:{
amount: 200
}
}
],
account:{
balance: 1500
},
name: "John Doe",
email: null,
user_id: "123456789",
username: null,
user_type: "facebook",
id: 7,
createdAt: "2016-07-29T06:44:18.000Z",
updatedAt: "2016-07-29T06:44:18.000Z"
},
]
Now i want the generated csv to be like this
USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD
3,Johhy Moe,123456789,200,Grammar,10000,EXPIRED,5550
3,Johhy Moe,123456789,200,GK,10340,ACTIVE,30
7,John Doe,123456789,1500,GK,5252,EXPIRED,30
7,John Doe,123456789,1500,MATH,200,ACTIVE,25
As you see if there are two objects inside subscription array for each user, i want to repeat that user again but with different subscription data.
I've thought of using the library because my users array can go up to thousands of users with hundreds of subscription.
And i'm at a loss to what i should do.
my Code:
var options= {
fields : [
{
name : 'id',
label : 'USERID'
},
{
name : 'name',
label : 'Name'
},
{
name : 'user_id',
label : 'FBID'
},
{
name : 'account.balance',
label : 'ACCOUNT'
},
{
name: '',
label: 'Subscription'
}
]
}
var source = es.readArray(users)
source
.pipe(jsoncsv.csv(options))
.pipe(res)
I dont want to use a library also. So if someone could provide me with a resource to make my own csv file with strings and also using streams , that would be great. Thanks!!
This will solve your problem. Now you just have to change console.log to fs and write to your file.
var json2csv = function (json, listKeys) {
var str = "";
var prefix = "";
for (var i = 0; i < listKeys.length; i++) {
str += prefix + json[listKeys[i]];
prefix = ",";
}
return str;
};
var async = require('async');
var csvData = ['USERID,NAME,FBID,ACCOUNT,SUBSCRIPTION,PRICE,STATE,TIMEPERIOD'];
async.each(users, function (user, callback) {
var csvRow1 = {
USERID: user.id,
NAME: user.name,
FBID: user.user_id,
ACCOUNT: user.account.balance
};
async.each(user.subscriptions, function (subscription, callback) {
var csvRow2 = JSON.parse(JSON.stringify(csvRow1));
csvRow2.SUBSCRIPTION = subscription.package.name;
csvRow2.PRICE = subscription.transaction.amount;
csvRow2.STATE = subscription.state;
csvRow2.TIMEPERIOD = subscription.timerange.period;
csvData.push(json2csv(csvRow2, ['USERID', 'NAME', 'FBID', 'ACCOUNT', 'SUBSCRIPTION', 'PRICE', 'STATE', 'TIMEPERIOD']));
callback(null);
}, function (err) {
callback(err);
});
}, function (err) {
if (err) {
// return err;
} else {
// return csvData;
}
});