Node js JSON: Access Object - node.js

I parse 'body' and save it to my var jsonNew getting 'results'.
let json = JSON.parse(body);
var jsonNew = json['results'];
jsonNew gives me:
[
{
address_components: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
],
formatted_address: 'Germany',
geometry: {
location: [Object],
location_type: 'ROOFTOP',
viewport: [Object]
},
place_id: '123',
plus_code: {
compound_code: '123',
global_code: '132+86'
},
types: [ 'street_address' ]
}
]
How can I access geometry.location to get 'lat' and 'lng'?
Which are the search terms I need to google to find the solution? What exactly do I have here?
"geometry" : {
"location" : {
"lat" : 1234,
"lng" : 12345
},
"location_type" : "ROOFTOP",
"viewport" : {
"northeast" : {
"lat" : 5555,
"lng" : 6666
},
"southwest" : {
"lat" : 555,
"lng" : 666
}
}
}

I was able to solve this getting [0] of the array - I am stupid!
var jsonNew = json['results'][0].geometry.location;

Related

Mongoose find() return a weird property

I just learnt mongoose in node.js, but when I use the collection.find(), it return a weird properties like below:
model {
'$__': InternalCache {
strictMode: true,
selected: {},
shardval: undefined,
saveError: undefined,
validationError: undefined,
adhocPaths: undefined,
removing: undefined,
inserting: undefined,
version: undefined,
getters: {},
_id: 61d95a65fb65d62fb20ecb88,
populate: undefined,
populated: undefined,
wasPopulated: false,
scope: undefined,
activePaths: [StateMachine],
pathsToScopes: {},
ownerDocument: undefined,
fullPath: undefined,
emitter: [EventEmitter],
'$options': true
},
isNew: false,
errors: undefined,
_doc: {
tags: [Array],
date: 2022-01-08T09:33:25.205Z,
_id: 61d95a65fb65d62fb20ecb88,
name: 'Angular Course',
author: 'meow',
isPublished: true,
__v: 0
},
'$init': true
}
If I run it in terminal using mongodb instead of mongoose, I got the correct answer like that:
> db.courses.find()
{ "_id" : ObjectId("61d9592205df8e2f7230932f"), "tags" : [ "node", "backend" ], "date" : ISODate("2022-01-08T09:28:02.467Z"), "name" : "Node.js Course", "author" : "meow", "isPublished" : true, "__v" : 0 }
{ "_id" : ObjectId("61d95a65fb65d62fb20ecb88"), "tags" : [ "Angular", "frontend" ], "date" : ISODate("2022-01-08T09:33:25.205Z"), "name" : "Angular Course", "author" : "meow", "isPublished" : true, "__v" : 0 }
Use lean()
Normally, in mongoose, find() returns mongoose document not plain javascript object. Use lean() to returns a plain javascript object.
async function getCourses() {
// Documents returned from queries with the lean option enabled are plain javascript objects, not Mongoose Documents.
// read more: https://mongoosejs.com/docs/api.html#query_Query-lean
const courses = await Course.find().lean();
console.log(courses);
};

How to pick out an array in a JSON response from spotify web API

I need to assign the genre field to a new array but I am not sure how to only get that field, or how to call that field
var SpotifyWebApi = require('spotify-web-api-node');
var spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken('-----');
spotifyApi.searchArtists('artist:queen')
.then(function(data) {
console.log('Search tracks by "queen" in the artist name', data.body.artists.items);
}, function(err) {
console.log('Something went wrong!', err);
});
This is the terminal when calling it.
I only want the first response.
PS C:\Users\g\Documents\js> node spotifyTest
Search tracks by "queen" in the artist name [ { external_urls:
{ spotify: 'https://open.spotify.com/artist/1dfeR4HaWDbWqFHLkxsg1d' },
followers: { href: null, total: 19579534 },
genres: [ 'glam rock', 'rock' ],
href: 'https://api.spotify.com/v1/artists/1dfeR4HaWDbWqFHLkxsg1d',
id: '1dfeR4HaWDbWqFHLkxsg1d',
images: [ [Object], [Object], [Object], [Object] ],
name: 'Queen',
popularity: 90,
type: 'artist',
uri: 'spotify:artist:1dfeR4HaWDbWqFHLkxsg1d' },
{ external_urls:
{ spotify: 'https://open.spotify.com/artist/3nViOFa3kZW8OMSNOzwr98' },
followers: { href: null, total: 1087117 },
genres: [ 'deep pop r&b', 'pop', 'r&b' ],
href: 'https://api.spotify.com/v1/artists/3nViOFa3kZW8OMSNOzwr98',
id: '3nViOFa3kZW8OMSNOzwr98',
images: [ [Object], [Object], [Object] ],
name: 'Queen Naija',
popularity: 68,
type: 'artist',
uri: 'spotify:artist:3nViOFa3kZW8OMSNOzwr98' } ]
You can access a field in a JSON object using the dot notation. Here's an example of replacing the genres of the first response with a new array.
let responseItems = [
{ external_urls: { spotify: 'https://open.spotify.com/artist/1dfeR4HaWDbWqFHLkxsg1d' },
followers: { href: null, total: 19579534 },
genres: [ 'glam rock', 'rock' ],
href: 'https://api.spotify.com/v1/artists/1dfeR4HaWDbWqFHLkxsg1d',
id: '1dfeR4HaWDbWqFHLkxsg1d',
images: [ [Object], [Object], [Object], [Object] ],
name: 'Queen',
popularity: 90,
type: 'artist',
uri: 'spotify:artist:1dfeR4HaWDbWqFHLkxsg1d'
},
{
external_urls: { spotify: 'https://open.spotify.com/artist/3nViOFa3kZW8OMSNOzwr98' },
followers: { href: null, total: 1087117 },
genres: [ 'deep pop r&b', 'pop', 'r&b' ],
href: 'https://api.spotify.com/v1/artists/3nViOFa3kZW8OMSNOzwr98',
id: '3nViOFa3kZW8OMSNOzwr98',
images: [ [Object], [Object], [Object] ],
name: 'Queen Naija',
popularity: 68,
type: 'artist',
uri: 'spotify:artist:3nViOFa3kZW8OMSNOzwr98'
}
];
let firstResponse = responseItems[0];
console.log(JSON.stringify(firstResponse.genres, null, 2));
let newGenres = [ 'rock', 'jazz' ];
firstResponse.genres = newGenres;
console.log(JSON.stringify(firstResponse.genres, null, 2));
This should show the following in the console:
[
"glam rock",
"rock"
]
[
"rock",
"jazz"
]

Aggregate function returns null GraphQL

I am testing a basic aggregation function using counts from Sequelize and here's my type Counts:
type Creserve {
id: ID!
rDateStart: Date!
rDateEnd: Date!
grade: Int!
section: String!
currentStatus: String!
user: User!
cartlab: Cartlab!
}
type Counts {
section: String!
count: Int
}
type Query {
getBooking(id: ID!): Creserve!
allBookings: [Creserve]
getBookingByUser(userId: ID): Creserve
upcomingBookings: [Creserve]
countBookings: [Counts]
}
I am using countBookings as my query for aggregate functions and here's my resolver for the query:
countBookings: async (parent, args, {models}) =>
{
const res = await models.Creserve.findAndCountAll({
group: 'section',
attributes: ['section', [Sequelize.fn('COUNT', 'section'), 'count']]
});
return res.rows;
},
The query that it outputs is this:
Executing (default): SELECT "section", COUNT('section') AS "count" FROM "Creserve" AS "Creserve" GROUP BY "section";
And tried this query in my psql shell and it's working fine:
section | count
---------+-------
A | 2
R | 2
However, when I tried querying countBookings in my GraphQL Playground, section is returned but not the count:
{
"data": {
"countBookings": [
{
"section": "A",
"count": null
},
{
"section": "R",
"count": null
}
]
}
}
Is there something I missed out? Or is this a bug? This is the answer I tried following to with this example: https://stackoverflow.com/a/45586121/9760036
Thank you very much!
edit: returning a console.log(res.rows) outputs something like this:
[ Creserve {
dataValues: { section: 'A', count: '2' },
_previousDataValues: { section: 'A', count: '2' },
_changed: {},
_modelOptions:
{ timestamps: true,
validate: {},
freezeTableName: true,
underscored: false,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: null,
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
indexes: [],
name: [Object],
omitNull: false,
hooks: [Object],
sequelize: [Sequelize],
uniqueKeys: {} },
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [Array] },
__eagerlyLoadedAssociations: [],
isNewRecord: false },
Creserve {
dataValues: { section: 'R', count: '2' },
_previousDataValues: { section: 'R', count: '2' },
_changed: {},
_modelOptions:
{ timestamps: true,
validate: {},
freezeTableName: true,
underscored: false,
underscoredAll: false,
paranoid: false,
rejectOnEmpty: false,
whereCollection: null,
schema: null,
schemaDelimiter: '',
defaultScope: {},
scopes: [],
indexes: [],
name: [Object],
omitNull: false,
hooks: [Object],
sequelize: [Sequelize],
uniqueKeys: {} },
_options:
{ isNewRecord: false,
_schema: null,
_schemaDelimiter: '',
raw: true,
attributes: [Array] },
__eagerlyLoadedAssociations: [],
isNewRecord: false } ]
Here's for res.count:
Executing (default): SELECT "section", COUNT('section') AS "count" FROM "Creserve" AS "Creserve" GROUP BY "section";
[ { count: '2' }, { count: '2' } ]
Problem
Actually you are doing everything right here... but what is happening here is the sequlize doesn't return plain object... It always returns the data in form of instance like that
[ Creserve {
dataValues: { section: 'A', count: '2' },
_previousDataValues: { section: 'A', count: '2' },
_changed: {},
_modelOptions:
{ timestamps: true,
Solution
I am not sure but there is no other way instead of looping and makes
response to json object...
const array = []
res.rows.map((data) => {
array.push(data.toJSON())
})
return array

using #each in handlebars not working

I have a var with products and if I console log it out it shows this.
Query {
_mongooseOptions: {},
mongooseCollection:
NativeCollection {
collection: null,
opts: { bufferCommands: true, capped: false },
name: 'products',
collectionName: 'products',
conn:
NativeConnection {
base: [Object],
collections: [Object],
models: [Object],
config: [Object],
replica: false,
hosts: null,
host: null,
port: null,
user: null,
pass: null,
name: null,
options: null,
otherDbs: [],
_readyState: 0,
_closeCalled: false,
_hasOpened: false,
_listening: false },
queue: [],
buffer: true,
emitter:
EventEmitter {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined } },
model:
{ [Function: model]
hooks: Kareem { _pres: {}, _posts: {} },
base:
Mongoose {
connections: [Object],
plugins: [],
models: [Object],
modelSchemas: [Object],
options: [Object] },
modelName: 'Product',
model: [Function: model],
db:
NativeConnection {
base: [Object],
collections: [Object],
models: [Object],
config: [Object],
replica: false,
hosts: null,
host: null,
port: null,
user: null,
pass: null,
name: null,
options: null,
otherDbs: [],
_readyState: 0,
_closeCalled: false,
_hasOpened: false,
_listening: false },
discriminators: undefined,
schema:
Schema {
obj: [Object],
paths: [Object],
subpaths: {},
virtuals: [Object],
singleNestedPaths: {},
nested: {},
inherits: {},
callQueue: [Object],
_indexes: [],
methods: {},
statics: {},
tree: [Object],
_requiredpaths: undefined,
discriminatorMapping: undefined,
_indexedpaths: undefined,
query: {},
childSchemas: [],
s: [Object],
options: [Object],
'$globalPluginsApplied': true },
collection:
NativeCollection {
collection: null,
opts: [Object],
name: 'products',
collectionName: 'products',
conn: [Object],
queue: [],
buffer: true,
emitter: [Object] },
Query: { [Function] base: [Object] },
'$__insertMany': [Function],
insertMany: [Function] },
schema:
Schema {
obj:
{ imagePath: [Object],
title: [Object],
description: [Object],
price: [Object] },
paths:
{ imagePath: [Object],
title: [Object],
description: [Object],
price: [Object],
_id: [Object],
__v: [Object] },
subpaths: {},
virtuals: { id: [Object] },
singleNestedPaths: {},
nested: {},
inherits: {},
callQueue: [ [Object], [Object], [Object], [Object] ],
_indexes: [],
methods: {},
statics: {},
tree:
{ imagePath: [Object],
title: [Object],
description: [Object],
price: [Object],
_id: [Object],
id: [Object],
__v: [Function: Number] },
_requiredpaths: undefined,
discriminatorMapping: undefined,
_indexedpaths: undefined,
query: {},
childSchemas: [],
s: { hooks: [Object], kareemHooks: [Object] },
options:
{ retainKeyOrder: false,
typeKey: 'type',
id: true,
noVirtualId: false,
_id: true,
noId: false,
validateBeforeSave: true,
read: null,
shardKey: null,
autoIndex: null,
minimize: true,
discriminatorKey: '__t',
versionKey: '__v',
capped: false,
bufferCommands: true,
strict: true,
pluralization: true },
'$globalPluginsApplied': true },
op: 'find',
options: { retainKeyOrder: false },
_conditions: {},
_fields: undefined,
_update: undefined,
_path: undefined,
_distinct: undefined,
_collection:
NodeCollection {
collection:
NativeCollection {
collection: null,
opts: [Object],
name: 'products',
collectionName: 'products',
conn: [Object],
queue: [],
buffer: true,
emitter: [Object] },
collectionName: 'products' },
_traceFunction: undefined,
_castError: null,
_count: [Function],
_execUpdate: [Function],
_find: [Function],
_findOne: [Function],
_findOneAndRemove: [Function],
_findOneAndUpdate: [Function] }
I am trying to use #each to get the products and show lets say the title first.
Here is my method in index js using node
var express = require('express');
var router = express.Router();
var Product = require('../models/product');
/* GET home page. */
router.get('/', function(req, res, next) {
Product.find({}, function (err, products) {
// check for and handle query errors
if (err) {
console.error('Product.find() error', err);
return next(err);
}
// continue to render the view with `products` available
console.log(products);
res.render('shop/index', { title: 'Shopping Cart', products: products });
});
});
module.exports = router;
And here is the foreach sentence at index.hbs
{{#each products}}
<p>{{products.price}}</p>
{{/each}}
I tried different ways like #each this and then getting it but non seem working.
Just incase if needed when I search in the db
> db.products.find()
{ "_id" : ObjectId("5855c55482d8722419e21a7d"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "weeee", "description" : " AWeeesomeee", "price" : "15", "__v" : 0 }
{ "_id" : ObjectId("5855c55482d8722419e21a7e"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "Gowwww", "description" : " this is great", "price" : "15", "__v" : 0 }
{ "_id" : ObjectId("5855c55482d8722419e21a7f"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "killaa crw", "description" : " i love it", "price" : "15", "__v" : 0 }
{ "_id" : ObjectId("5855c55482d8722419e21a80"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "joker", "description" : " kill em", "price" : "15", "__v" : 0 }
{ "_id" : ObjectId("5855c55482d8722419e21a81"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "harley", "description" : " naughtttyyy", "price" : "15", "__v" : 0 }
{ "_id" : ObjectId("5855c55482d8722419e21a82"), "imagePath" : "https://upload.wikimedia.org/wikipedia/en/thumb/5/5e/Gothiccover.png/250px-Gothiccover.png", "title" : "suicide", "description" : " squad", "price" : "15", "__v" : 0 }
> db.products.count()
6
App.js file
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var expressHbs = require('express-handlebars');
var mongoose = require('mongoose');
var routes = require('./routes/index');
var app = express();
app.connect('localhost:27017/shopping');
// view engine setup
app.engine('.hbs', expressHbs({defaultLayout : 'layout', extname: '.hbs' }));
app.set('view engine', '.hbs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Mongoose' model.find() doesn't return the results directly as Mongo's command-line does.
var products = Product.find();
It expects a callback function to be provided either to it directly, following the conditions, or provided to a method of the Query object it returns:
function proceedWithProducts(err, products) {
if (err) {
// handler query error
} else {
// use products
}
}
// a couple options...
Product.find({}, proceedWithProducts);
Product.find().exec(proceedWithProducts);
Within the route:
/* GET home page. */
router.get('/', function(req, res, next) {
Product.find({}, function (err, products) {
// check for and handle query errors
if (err) {
console.error('Product.find() error', err);
return next(err);
}
// continue to render the view with `products` available
console.log(products);
res.render('shop/index', { title: 'Shopping Cart', products: products });
});
});

Keystone.js / mongoose virtual fields lean record

I'm trying to produce a lean record for a REST API that include virtual fields.
The official documentation for how to implement virtual fields for Mongoose:
http://mongoosejs.com/docs/guide.html
My model:
var keystone = require('keystone')
, Types = keystone.Field.Types
, list = new keystone.List('Vendors');
list.add({
name : {
first: {type : Types.Text}
, last: {type : Types.Text}
}
});
list.schema.virtual('name.full').get(function() {
return this.name.first + ' ' + this.name.last;
});
list.register();
Now, let's query the model:
var keystone = require('keystone'),
vendors = keystone.list('Vendors');
vendors.model.find()
.exec(function(err, doc){
console.log(doc)
});
Virtual field name.full is not here:
[ { _id: 563acf280f2b2dfd4f59bcf3,
__v: 0,
name: { first: 'Walter', last: 'White' } }]
But if we do this:
vendors.model.find()
.exec(function(err, doc){
console.log(doc.name.full); // "Walter White"
});
Then the virtual shows.
I guess the reason is that when I do a console.log(doc) the Mongoose document.toString() method is invoked which does not include virtuals by default. Fair enough. That's understandable.
To include the virtuals in any of the conversion methods you have to go:
doc.toString({virtuals: true})
doc.toObject({virtuals: true})
doc.toJSON({virtuals: true})
However, this includes keys I don't want for my REST API to pump out to my users:
{ _id: 563acf280f2b2dfd4f59bcf3,
__v: 0,
name: { first: 'Walter', last: 'White', full: 'Walter White' },
_: { name: { last: [Object], first: [Object] } },
list:
List {
options:
{ schema: [Object],
noedit: false,
nocreate: false,
nodelete: false,
autocreate: false,
sortable: false,
hidden: false,
track: false,
inherits: false,
searchFields: '__name__',
defaultSort: '__default__',
defaultColumns: '__name__',
label: 'Vendors' },
key: 'Vendors',
path: 'vendors',
schema:
Schema {
paths: [Object],
subpaths: {},
virtuals: [Object],
nested: [Object],
inherits: {},
callQueue: [],
_indexes: [],
methods: [Object],
statics: {},
tree: [Object],
_requiredpaths: [],
discriminatorMapping: undefined,
_indexedpaths: undefined,
options: [Object] },
schemaFields: [ [Object] ],
uiElements: [ [Object], [Object] ],
underscoreMethods: { name: [Object] },
fields: { 'name.first': [Object], 'name.last': [Object] },
fieldTypes: { text: true },
relationships: {},
mappings:
{ name: null,
createdBy: null,
createdOn: null,
modifiedBy: null,
modifiedOn: null },
model:
{ [Function: model]
base: [Object],
modelName: 'Vendors',
model: [Function: model],
db: [Object],
discriminators: undefined,
schema: [Object],
options: undefined,
collection: [Object] } },
id: '563acf280f2b2dfd4f59bcf3' }
I can always of course just delete the unwanted keys, but this doesn't seem quite right:
vendors.model.findOne()
.exec(function(err, doc){
var c = doc.toObject({virtuals: true});
delete c.list;
delete c._;
console.log(c)
});
This produces what I need:
{ _id: 563acf280f2b2dfd4f59bcf3,
__v: 0,
name: { first: 'Walter', last: 'White', full: 'Walter White' },
id: '563acf280f2b2dfd4f59bcf3' }
Is there not a better way of getting a lean record?
I think you want the select method.. something like this:
vendors.model.findOne()
.select('_id __v name').
.exec(function(err, doc){
console.log(c)
});
Also personally I prefer setting virtuals: true on the schema rather than the document, but depends on use case I guess.
One solution would be to use a module like Lodash (or Underscore) which allows you pick a whitelist of property names:
vendors.model.findOne()
.exec(function(err, doc){
var c = _.pick(doc, ['id', 'name.first', 'name.last', 'name.full']);
console.log(c)
});
Given your use-case of serving this data via REST API, I think explicitly defining a whitelist of property names is safer. You could even define a virtual property on your schema which returns the predefined whitelist:
list.schema.virtual('whitelist').get(function() {
return ['id', 'name.first', 'name.last', 'name.full'];
});
and use it in multiple places, or have different versions of your whitelist, all managed at the model layer.

Resources