Mongoose query on json array - json

I am new to node.js and using mongoose along with node.js.
Schema:
var userSchema = new mongoose.Schema({
username: String,
password: String,
phone_no : String,
email: String,
name:String,
dob: {type: Date, "default": Date.now},
city:String,
locality:{lattitude:Number,longitude:Number},
sports: [String]
});
sample entry:
"userId": 10,
"username": "shu",
"name": "shubham goyal",
"password": "ahu",
"phone_no": "919357701457",
"email": "shubham2892#gmail.com",
"city": "delhi",
"_id": {
"$oid": "5331dbc243bb59f80a7ed60b"
},
"sports": [
"cricket,football"
],
"dob": {
"$date": "2014-03-25T19:40:50.886Z"
},
"__v": 0
}
The query i am trying to make:
models.user.find({'sports' : sports_selected},function(err,users) {
if(err) {
console.log("Cannot fetch sports with requested username" + req.user.username);
console.log(err);
}if(!users){
console.log("cannot find specified username" + req.user.username);
}else{
console.log(users.username);
//sport = player.sports;
//sport = ['cricket','football','basketball'];
}
});
I am getting undefined in users.username.I want to return all users who have selected a particular sports say 'cricket'.I googled it a lot but could not find anything.

It looks like your sample entry is incorrectly formatted. Sports entry shouldn't be like this:
"sports": [
"cricket,football"
]
You should keep the sports data like this instead:
"sports": [
"cricket","football"
]
If you provide cricket and football in the same string, mongo cannot differentiate these two as different sports.
Also users should be an array, so you need to loop through the array. Say you have one user with sport selected as cricket, then you can print users[0].username

Related

How to get data of a JSON file (typescript)

Hi I got a bit stuck at trying to understand how to fetch data of a JSON file.
environment.ts:
export const environment = {
production: false,
urlListBooks: "/assets/list-books.json",
urlGetBooks: "/assets/edit-book.json?:id",
urlGetTags: "/assets/edit-book.json?:tags",
urlPostBooks: "/assets/edit-book.json",
urlListTags: "/assets/list-tags.json",
urlPostTags: "/assets/edit-tag.json"
};
edit-book.json:
"book":{
"id": 1,
"title": "The Shining",
"authorId": 1,
"tags": [{"name":"new"}, {"name":"test"}]
},
"authors":[
{
"id": 1,
"prename": "Stephen",
"surname": "King"
},
{
"id": 3,
"prename": "Algernon",
"surname": "Blackwood"
},
{
"id": 4,
"prename": "Edgar Allan",
"surname": "Poe"
},
{
"id": 5,
"prename": "Howard Phillips",
"surname": "Lovecraft"
}
],
"tags":[
{
"name": "new"
},
{
"name": "Horror"
},
{
"name": "Romance"
}
]
}
service:
getBookTags(n: String) Observable<Tag[]>{
return this.http.get<Tag[]>(environment.urlGetTags.)
}
what I want getBookTags(n: String) to do is returning the tags array of the book with title n defined in the edit-book.json (e.g. "tags": [{"name":"new"}, {"name":"Horror"}] ) so that I can later use the function to check which tags a book has and select them.
Your help would be very appreciated :)
Ok I think I've solved this for you, I'm going to walk through my process with you so you understand what the goal is. You can see my solution here: https://codesandbox.io/s/thirsty-minsky-g6959f?file=/assets/edit-book.json:0-752
First thing is that your JSON you provided doesn't really make much sense, it shows multiple authors and just one "book". I think instead you want multiple books. Secondly, it's gotta be wrapped in a curly brace as shown:
{
"books": [
{
"id": 1,
"title": "The Shining",
"authorId": 1,
"tags": [{ "name": "new" }, { "name": "test" }]
},
{
"id": 2,
"title": "The Wendigo",
"authorId": 2,
"tags": [{ "name": "Horror" }]
}
],
"authors": [
{
"id": 1,
"prename": "Stephen",
"surname": "King"
},
{
"id": 3,
"prename": "Algernon",
"surname": "Blackwood"
},
{
"id": 4,
"prename": "Edgar Allan",
"surname": "Poe"
},
{
"id": 5,
"prename": "Howard Phillips",
"surname": "Lovecraft"
}
],
"tags": [
{
"name": "new"
},
{
"name": "Horror"
},
{
"name": "Romance"
}
]
}
Now, in your Typescript code we want to have typings for the json you're going to fetch. This will make your code more readable, it will give you intellisense, and help you catch some errors before you try to run your code. So we are going to go ahead and type the properties of the JSON as follows:
type Tag = {
name: string;
};
type Book = {
id: number;
title: string;
authorId: number;
tags: Tag[];
};
type Author = {
id: number;
prename: string;
surname: string;
};
type BookData = {
books: Book[];
authors: Author[];
tags: Tag[];
};
Basically what I said is we have bookdata which is made up of books, authors, and tags. Books have properties given under type Book, same thing with Author and Tag.
Now for the actual running code, we are going to use the fetch api to get the json data at the url.
async function getBookTags(n: string): Promise<Book[]> {
return fetch(url)
.then<BookData>((res) => res.json())
.then((data) => data.books)
.then((books) => books.filter((b) => doesBookHaveTag(b, n)));
}
First thing we do is fetch the data from the api, this returns a promise which when resolved (this is what .then does) we take the response and parse it for a json. Then when that promise resolves we get the books in the data. Then when that promise resolves we filter in books that have the matching tag.
doesBookHaveTag is just a little helper function I defined:
function doesBookHaveTag(book: Book, n: string): boolean {
// just return if book has at least one tag matching n
return book.tags.some((t) => t.name.toLowerCase() === n.toLowerCase());
}
If you don't understand promises you should watch some videos on it, but basically the browser sends out an http request and then when it resolves it queues a task to execute the function [see endnote] in .then when it has time. So when we want to call your async function and say log all books with the tag "horror" we do it as shown:
getBookTags("horror").then(console.log); // returns the one book.
I hope this makes sense and you can sort of see how to fetch the data, how to handle the promise it returns, and how to type your response. The only thing I'm not sure on is how Angular changes this for you (I'm a react guy), but this is really just non-library specific Javascript/Typescript.
[endnote] when I say function in .then, what I mean is that .then(data => data.books) is passing a function into the .then function. data => data.books is actually a function the same as:
function(data: BookData): Book[] {
return data.books
}

Convert Array to JSON with ID's as Index (MongoDB / Express)

I have an user schema in my mongodb database which saves users. In mongoose I write:
const userSchema = new Schema({
email: { type: String, unique: true, lowercase: true},
password: String,
fname: String,
lname: String,
articles: [{
type: Schema.Types.ObjectId,
ref: "article"
}]
})
I've saved a few users to that Collection. When I query my api for users, I get back an array of User objects. Like so:
[
{
"first_name": "Bob",
"_id": "5b36292760afa11b9a216945",
"email": "user#mail.de",
"__v": 0
},
{
"first_name": "Lisa",
"_id": "5bafkjahf123123123123125",
"email": "lisa#mail.de",
"__v": 0
}
]
Is this normal behaviour? And is this coming from the Mongo side of things or the express side? I heard that something like this is more common:
{
"5b36292760afa11b9a216945":{
"first_name": "Bob",
"email": "user#mail.de",
"__v": 0
},
"5bafkjahf123123123123125":
{
"first_name": "Lisa",
"email": "lisa#mail.de",
"__v": 0
}
}
? How could I convert my array into this kind of a JSON? Is there some functionality maybe within mongo that does that for me?
I don't think you can get an object like that back from mongo, but it's not hard to convert it with Array.reduce().
let returnedValue = [{"first_name": "Bob","_id": "5b36292760afa11b9a216945","email": "user#mail.de","__v": 0},{"first_name": "Lisa","_id": "5bafkjahf123123123123125","email": "lisa#mail.de","__v": 0}]
let result = returnedValue.reduce((a, c) => (a[c._id] = c, a), {})
console.log(result)
This leaves the _id in the original object, but if you needed to inside the reduce function.
You can try below aggregation as well
db.collection.aggregate([
{ "$group": {
"_id": null,
"data": {
"$push": {
"k": "$_id",
"v": {
"first_name": "$first_name",
"email": "$email"
}
}
}
}},
{ "$replaceRoot": {
"newRoot": { "$arrayToObject": "$data" }
}}
])
Output
[
{
"5b36292760afa11b9a216945": {
"email": "user#mail.de",
"first_name": "Bob"
},
"5bafkjahf123123123123125": {
"email": "lisa#mail.de",
"first_name": "Lisa"
}
}
]

How to serialize / represent database objects as JSON returned by API in node

I'm using express.js and sequelize.js to build an API. Once I retrieved an object from the DB using sequelize, I want to
filter out object attributes (e.g. retrieve the user, but don't render the User's password hash to the returned JSON)
add new object attributes
before I return it from the API as JSON.
Similar to what these Rails libraries do:
Roar
Grape Entity
What's the most common framework to do that in node? Or do sequelize.js / express.js contain functionality to do that?
UPDATE
Ok, there is a basic example, passport.js gets the authenticated user's object from the DB and attaches it to req.user;
router.get('/me/data',
passport.authenticate('bearer', { session: false }),
function(req, res) {
res.status(200).send(req.user);
}
);
That would return the following JSON response:
{
"id": 24,
"first_name": "John",
"last_name": "Doe",
"email": "mymail#example.com",
"password": "8d23cb9c4827bc06bb30ac47c06af0efbdbeb575001ab7de5387da4085f7184a381335c0f04b45f4a40e5a7042d47ae1e2d29d28fd5be1d534f09ba3db04e8ca",
"updatedAt": "2016-01-25T09:19:07.422Z",
"createdAt": "2016-01-25T09:19:07.422Z",
"data": null
}
But I want to return something like this:
{
"id": 24,
"full_name": "John Doe",
"email": "mymail#example.com",
"data": null
}
And not just for this one case, but in any case a user object is rendered.
The simplest solution would be to edit req.user before sending it:
function
render (user) {
callback({
"id": user.id,
"full_name": user.first_name + ' ' + user.last_name,
"email": user.email,
"data": null
});
}
router.get('/me/data',
passport.authenticate('bearer', { session: false }),
function(req, res) {
render(req.user, function(user) {
res.status(200).send(user);
});
}
);
This module will helper you with Grape Entity manner:
https://github.com/baijijs/entity
For example:
const Entity = require('baiji-entity');
const userEntity = new Entity({
id: true,
fullname: true,
email: true,
data: true
});
userEntity.parse(req.user);
// And the parsed result will be as below
{
"id": 24,
"full_name": "John Doe",
"email": "mymail#example.com",
"data": null
}
Hope this can help you.
Ok so you would like to chop of some of the fields? You could chop it of before the server is sending the response. For example with a module like this https://www.npmjs.com/package/json-chop or if you use mongo db you can hide the fields like described here https://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/
Without knowing more details about the setup, the following (untested) code has the potential to work. You could specify the attributes field in your query against the model in order to filter out results (based on certain conditions):
router.get('me/data',function(req, res) {
db.UserModel.find({
attributes:['id','first_name','last_name','email','data'],
where: { id: req.user.id}, //put in here whatever you are using to get the info from the database
})
.then (function(user) {
var formatUser = {
id: user.id,
full_name: user.first_name + ' ' + user.last_name,
email: user.email,
data: user.data,
};
res.json(formatUser);
}
});
See the following docs for more information:
http://docs.sequelizejs.com/en/latest/docs/models-usage/
http://docs.sequelizejs.com/en/latest/docs/querying/

Get one to may relationship data from tables as JSON

I have this two tables from the diagrams.For every question i can have multiple answer.
I want to get data by question id in json format like this:
var initialData = [
{question_id: "1", Description: "Danny", Status: "1", answers: [
{ answer_id: "1", description: "AAAAA" },
{ answer_id: "2", description: "Bbbbb" }]
}
];
I use this code
var question = db.Questions.FirstOrDefault((p) => p.questionID== id);
return this.Json(question , JsonRequestBehavior.AllowGet);
and i get this
var initialData = [
{question_id: "1", Description: "Danny", Status: "1", answers:null
}
];
Can you help my please with a solution.
Can you try this please.
var question = db.Questions.Include("Answer").FirstOrDefault((p) => p.questionID== id);
Have a look at the ado.Net blog

Mongoose - How can I make this data more usable?

I have the following data strucutre outputting form my Schema in a node/express app. I'd like to have the feeds array simply an array of name:key pairs. I don't like the sort of weird numbered object structure going on between "feeds" and the actual feeds data. But i can't figure out how to manually define that in mongoose. any help would be awesome. thanks!
outputted JSON
{
"title": "Testing",
"created_at": "2011-10-05T16:23:26.217Z",
"feeds": [{
"0": {
"name": "twitter",
"key": "person1"
},
"1": {
"name": "twitter",
"key": "person2"
},
"_id": "4e8c847e02edc10035000003"
}]
}
i want this:
{
"title": "Testing",
"created_at": "2011-10-05T16:23:26.217Z",
"feeds": [
{
"name": "twitter",
"key": "person1"
},
{
"name": "twitter",
"key": "person2"
}
],
"_id": "4e8c847e02edc10035000003"
}
this is my schema:
var Feed = new Schema({
name : { type: String }
, key : { type: String }
});
var Page = new Schema({
title : { type: String, required: true, index: { unique: true } }
, feeds : [Feed]
, created_at : { type: Date, required: true, default: Date.now }
});
Ok, a colleague was able to answer this for me. My bad for not posting the relevant code, I didn't realize where the problem actually was. But for those who may encounter this problem:
If you push your embedded docs into the model when saving, you may need to do a forEach loop rather than pushing the embedded docs (in this case Feeds) together. Using forEach, the database saved the feeds directly to the feeds array rather than creating those weird groupings.
This pushed the feeds in properly:
req.body.feed.forEach(function(feed){
page.feeds.push(feed);
});
Let me know if you have the same problem and need more explanation.