How to search nested property in mongoose - javascript

hi I have mongodb document like below:
{
_id: ObjectId("63df40c651f1358f2b60f24a"),
origin: {
country: 'Usa',
state: 'Washington',
city: 'Washington',
type: 'Point',
coordinates: [ 12.555, 18.645 ]
},
destination: {
country: 'Usa',
state: 'Montana',
city: 'Yellowstone',
type: 'Point',
coordinates: [ 12.555, 20.645 ]
},
date: 'mon 2023-12-16',
seats: 4,
status: 'pending',
driver: {
firstName: 'sam',
lastName: 'johnson',
phoneNumber: '92823831736',
socialNumber: '1381222327',
driverId: '63d8f94202653dc3592a0aa4'
},
I use below url and code for finding location base on destination:
URL: "localhost:3000/api/v1/travel/?state=Montana&city=Yellowstone"
and my code is:
const Travel = require("../models/travel-model");
const getAllTravelsByDestination = async (req, res) => {
const { country, state, city } = req.query;
const queryObject = {};
if (country) {
queryObject.destination.country = country;
}
if (state) {
queryObject.destination.state= state;
}
if (city) {
queryObject.destination.city = city;
}
const travelList = await Travel.find({ destination:queryObject });
res.status(StatusCodes.OK).json({ status: "success", travelList });
};
I expect to get my document back but instead i get empty list as response:
{
"status": "success",
"travelList": []
}
please help me for this problam

You created a filter properly. But, instead of find({ destination: queryObject }), you should only do find(queryObject), since the queryObject already contains the destination property.
const travelList = await Travel.find(queryObject);

Check this line of your code:
const travelList = await Travel.find({ destination:queryObject });
You defined destination already in the queryObject. So you should just do
await Travel.find(queryObject);
Please note that queries are by default case-sensitive. So upper & lowercase matters.

Related

How can I $push an item in two different fields, depending on the condition?

I'm trying to receive the user location and store it in the database. Also, the user can choose if he wants to save all his previous locations or not.
So I have created a boolean variable historicEnable: true/false.
So when the historicEnable is true, I want to push to historicLocation[] array in the UserSchema and if it is false, I want just to update currentLocation[] array in the UserSchema.
conntrollers/auth.js
exports.addLocation = asyncHandler(async (req, res, next) => {
const {phone, location, status, historicEnable} = req.body;
let theLocation;
if (historicEnable== true){
theLocation = await User.findOneAndUpdate(
{ phone },
{ $push:{ locationHistoric: location, statusHistoric: status }},
{ new: true }
)
} else if(historicEnable== false){
theLocation = await User.findOneAndUpdate(
{ phone },
{ location, status },
{ new: true }
)
}
res.status(200).json({
success: true,
msg: "A location as been created",
data: theLocation,
locationHistory: locationHistory
})
})
models/User.js
...
currentLocation: [
{
location: {
latitude: {type:Number},
longitude: {type:Number},
},
status: {
type: String
},
createdAt: {
type: Date,
default: Date.now,
}
}
],
historicLocation: [
{
locationHistoric: {
latitude: {type:Number},
longitude: {type:Number},
},
statusHistoric: {
type: String
},
createdAt: {
type: Date,
default: Date.now,
}
}
]
Also, not sure how to make the request body so the function works.
req.body
{
"phone": "+1234",
"historicEnable": true,
"loications": [
{
"location": {
"latitude": 25,
"longitude": 35
},
"status": "safe"
}
]
}
To sum up, if historicEnable is true, the data will be pushed in historicLocation, and if it false, to update the currentLocation.
How can I solve this?
You can use an update with an aggregation pipeline. If the historicEnable is known only on db level:
db.collection.update(
{phone: "+1234"},
[
{$addFields: {
location: [{location: {latitude: 25, longitude: 35}, status: "safe"}]
}
},
{
$set: {
historicLocation: {
$cond: [
{$eq: ["$historicEnable", true]},
{$concatArrays: ["$historicLocation", "$location"]},
"$historicLocation"
]
},
currentLocation: {
$cond: [
{$eq: ["$currentLocation", false]},
{$concatArrays: ["$currentLocation", "$location"]},
"$currentLocation"
]
}
}
},
{
$unset: "location"
}
])
See how it works on the playground example
If historicEnable is known from the input, you can do something like:
exports.addLocation = asyncHandler(async (req, res, next) => {
const phone = req.body.phone
const historicEnable= req.body.historicEnable
const locObj = req.body.location.locationHistoric[0];
locObj.createdAt = req.body.createdAt
const updateQuery = historicEnable ? { $push:{ locationHistoric: locObj}} : { $push:{ currentLocation: locObj}};
const theLocation = await User.findOneAndUpdate(
{ phone },
updateQuery,
{ new: true }
)
res.status(200).json({
success: true,
msg: "A location as been created",
data: theLocation,
locationHistory: locationHistory
})
})

Sequelize magic method returning null

Just a bit confused as to why this magic method is returning null. It's probably very simple, but I'm using methods I wouldn't normally (bulkingCreating) and can't currently see it.
Association: Country.hasOne(Capital, { foreignKey: 'countryId' });
Populating dummy data:
const countries = await Country.bulkCreate([
{ name: 'England' },
{ name: 'Spain' },
{ name: 'France' },
{ name: 'Canada' }
]);
const capitals = await Capital.bulkCreate([
{ name: 'London' },
{ name: 'Madrid'},
{ name: 'Paris' },
{ name: 'Ottawa' }
]);
countries.forEach((country, index) => {
country.setCapital(capitals[index]);
});
const country = await Country.findOne({where: {name: 'Spain'}});
console.log(country.name, Object.keys(country.__proto__)); // Spain / magic methods
const capital = await country.getCapital();
console.log(capital); // null
The table:
Am I wrong in thinking country.getCapital() should return the relevant entry?
As you might guess setCapital should be an async function because it makes changes in DB so you need to use for instead of forEach method that does not support async callbacks:
let index = 0;
for (const country of countries) {
await country.setCapital(capitals[index]);
index += 1;
}
It would be better to create countries one by one and create capitals for them not relying on the same indexes of both collections (DB might return created records in a different order).
If you are using Sequelize 5.14+, you can do this in 1 bulkCreate using include option.
const countries = await Country.bulkCreate([
{
name: 'England',
Capital: { // This keyname should be matching with model name.
name: 'London'
}
},
{
name: 'Spain',
Capital: {
name: 'Madrid'
}
},
...
],
{
include: Capital,
returning: true // If Postgres, add this if you want the created object to be returned.
}
);

Unable to fulfilling Stripe Connect representative requirement

im trying to create a company account via Stripe Connect. The problem is that whatever i do i always receive that there is missing representative. I'm attaching my JS code below.
Thanks in advance for the help people !
currently_due: [
"representative.address.city",
"representative.address.line1",
"representative.address.postal_code",
"representative.dob.day",
"representative.dob.month",
"representative.dob.year",
"representative.email",
"representative.first_name",
"representative.last_name",
"representative.phone",
"representative.relationship.executive",
"representative.relationship.title"
],
const account = await stripe.accounts.create({
country: payload.country,
type: 'custom',
capabilities: {
card_payments: {
requested: payload.capabilities.card_payments
},
transfers: {
requested: payload.capabilities.transfers
},
},
business_profile: payload.business_profile,
business_type: payload.business_type,
tos_acceptance: {
date: Math.round(+(new Date()) / 1000),
ip: ip
},
company: {
...payload.company,
directors_provided: true,
executives_provided: true,
owners_provided: true,
},
external_account: {
object: 'bank_account',
country: 'BG',
currency: 'BGN',
account_number: 'BG80BNBG96611020345678'
}
});
const person = await stripe.accounts.createPerson(
account.id,
{
...payload.representative
}
);
let updatedAccount = await stripe.accounts.retrieve(
account.id,
);
The person representative payload looks like this. All fields are filled according to documentation and the creation passes
representative: {
first_name: document.getElementById('representative.first_name').value,
last_name: document.getElementById('representative.last_name').value,
email: document.getElementById('representative.email').value,
phone: document.getElementById('representative.phone').value,
ssn_last_4: document.getElementById('representative.ssn_last_4').value,
dob: {
month: document.getElementById('representative.dob.month').value,
day: document.getElementById('representative.dob.day').value,
year: document.getElementById('representative.dob.year').value,
},
address: {
line1: document.getElementById('representative.address.line1').value,
postal_code: document.getElementById('representative.address.postal_code').value,
city: document.getElementById('representative.address.city').value,
state: document.getElementById('representative.address.state').value,
},
relationship: {
title: document.getElementById('representative.relationship.title').value,
executive: document.getElementById('representative.relationship.executive').value === 'true',
}
}

GraphQL query return NULL in GraphiQL, but data appears in console log

I have a GraphQL API which is supposed to return data from MySQL and PostGres database. In the resolvers, I have console.log the results and can view the data in the terminal.
address: {
type: AddressType,
description: "An Address",
args: {
id: { type: GraphQLInt },
},
resolve: (parent, args) => {
// Make a connection to MySQL
let result;
connection.query(
`SELECT * FROM addresses WHERE id = ${args.id}`,
(err, res, fields) => {
if (err) console.log(err);
console.log("========");
console.log(res);
console.log("+++++++++");
console.log(res[0]);
// console.log(result);
}
);
return result;
},
},
In the terminal, I can see the results when I run the query on GraphiQL:
[nodemon] starting `node schema.js`
Server is running
Connected to PSQL database.
Connected to mySQL database.
========
[
RowDataPacket {
id: 1,
address_type: 'House',
status: 'Inactive',
entity: 'Building',
number_and_street: 'PO BOX 276',
suite_and_apartment: 'PO',
city: 'Ennis',
postal_code: '59729-0276',
country: 'USA',
notes: 'Dolorem quia repellendus et et nobis.',
created_at: 2020-12-18T05:00:00.000Z,
updated_at: 2021-05-21T04:00:00.000Z,
latitude: null,
longitude: null
}
]
+++++++++
RowDataPacket {
id: 1,
address_type: 'House',
status: 'Inactive',
entity: 'Building',
number_and_street: 'PO BOX 276',
suite_and_apartment: 'PO',
city: 'Ennis',
postal_code: '59729-0276',
country: 'USA',
notes: 'Dolorem quia repellendus et et nobis.',
created_at: 2020-12-18T05:00:00.000Z,
updated_at: 2021-05-21T04:00:00.000Z,
latitude: null,
longitude: null
}
However on GraphiQL, I get null for data.
Input:
{
address(id: 1) {
address_type
}
}
output:
{
"data": {
"address": null
}
}
I am very new to GraphQL. What could I be missing here? I am attempting to get this information from the terminal to show upon query in GraphiQL. Just trying to learn more.
The classic problem with inattention:
You use the res variable for the console. And nowhere are you assigning a value to result.
And the return result is executed before the query is executed. (out of context where you have data)
See the documentation for how to use the async / await syntax. You are currently using callbacks - which is not a recommended syntax.
Not sure, but should be something like, you should use async/await, and await the return of query data. Also be sure to assign the value to the variable you have:
address: {
type: AddressType,
description: "An Address",
args: {
id: { type: GraphQLInt },
},
resolve: async (parent, args) => {
const result = await connection.query('SELECT * FROM addresses WHERE id = $1', [args.id]);
return result;
},
},
What ended up working for me was the following:
address: {
type: AddressType,
description: "An Address",
args: {
id: { type: GraphQLInt },
},
resolve: async (parent, args) => {
const [rows, fields] = await promisePool.query(
`SELECT * FROM addresses WHERE id = ${args.id}`
);
console.log(rows[0]);
return rows[0];
},
},

How to use map function on a json response in javascript?

I am using zomato API to find a random restaurant within an area. This is the API call.
app.post('/locations/:query', async (req, res) =>
{
try{
const query = req.params.query;
const data = await zomato.cities({ q: query,count: 1 })
const cityId= await (data[0].id);
const result = [];
const nrOfRequests = 60;
let currCount = 0;
const nrOfEntries = 20;
for(let i=0; i < nrOfRequests ; i++) {
const response = await zomato.search({ entity_id: cityId, entity_type: 'city', start:currCount, count:nrOfEntries, sort:'rating', order:'desc' });
result.push(...response.restaurants);
currCount += nrOfEntries;
}
const no = Math.floor(Math.random() * 60);
const restaur = result[no].restaurants.map(r => {
return {
name: r.name,
url: r.url,
location: r.location,
price: r.price_range,
thumbnail: r.thumb,
rating: r.user_rating.aggregate_rating,
}
})
res.send({restaur});
} catch (err) {
console.error(err)
res.status(500).send('error')
}
});
What this API is doing is
1)Finds cityId of query.(query is a city name)
2)Using for loop finds upto 60 restaurants in that city according to rating and pushes it in results[].(zomato api has restriction to display only 20 restaurants per call)
3)Chooses a random number between 0 to 60 and outputs that particular restaurant from result.
4)Then what I want to do is find features like name price etc and return to display that in the frontend.
Using this code I get the following error
TypeError: Cannot read property 'map' of undefined
The result[no] response looks like this.
{
R: {
has_menu_status: { delivery: -1, takeaway: -1 },
res_id: 302578,
is_grocery_store: false
},
apikey: '*************',
id: '302578',
name: 'Barbeque Nation',
url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1',
location: {
address: 'M/A 04, DLF Avenue, District Centre, Saket, New Delhi',
locality: 'DLF Avenue, Saket',
city: 'New Delhi',
city_id: 1,
latitude: '28.5273432339',
longitude: '77.2173847631',
zipcode: '',
country_id: 1,
locality_verbose: 'DLF Avenue, Saket, New Delhi'
},
switch_to_order_menu: 0,
cuisines: 'North Indian, BBQ, Beverages',
timings: '12noon – 3:30pm, 6pm – 11pm (Mon-Sun)',
average_cost_for_two: 1600,
price_range: 3,
currency: 'Rs.',
highlights: [
'Lunch',
'Serves Alcohol',
'Mall Parking',
'Cash',
'Credit Card',
'Debit Card',
'Dinner',
'Takeaway Available',
'Fullbar',
'Indoor Seating',
'Air Conditioned',
'Reopened',
'Buffet'
],
offers: [],
opentable_support: 0,
is_zomato_book_res: 0,
mezzo_provider: 'OTHER',
is_book_form_web_view: 0,
book_form_web_view_url: '',
book_again_url: '',
thumb: 'https://b.zmtcdn.com/data/pictures/chains/2/1212/b6429ddad24625e65344caabb921bd57.jpg?fit=around%7C200%3A200&crop=200%3A200%3B%2A%2C%2A',
user_rating: {
aggregate_rating: '4.7',
rating_text: 'Excellent',
rating_color: '3F7E00',
rating_obj: { title: [Object], bg_color: [Object] },
votes: 2248
},
all_reviews_count: 1159,
photos_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/photos?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1#tabtop',
photo_count: 1991,
menu_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/menu?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1&openSwipeBox=menu&showMinimal=1#tabtop',
featured_image: 'https://b.zmtcdn.com/data/pictures/chains/2/1212/b6429ddad24625e65344caabb921bd57.jpg',
medio_provider: 1,
has_online_delivery: 1,
is_delivering_now: 0,
store_type: '',
include_bogo_offers: true,
deeplink: 'zomato://restaurant/302578',
is_table_reservation_supported: 1,
has_table_booking: 0,
events_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/events#tabtop?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1',
phone_numbers: '080 61756005',
all_reviews: { reviews: [ [Object], [Object], [Object], [Object], [Object] ] },
establishment: [ 'Casual Dining' ],
establishment_types: []
}
The results[no] that you're showing indeed has no property called restaurants. You have an array called results, and you're selecting a single element from that array. When you have a single element, there's nothing to "map".
Instead, just return the fields you want from that element:
const no = Math.floor(Math.random() * 60);
const restaur = {
name: result[no].name,
url: result[no].url,
location: result[no].location,
price: result[no].price_range,
thumbnail: result[no].thumb,
rating: result[no].user_rating.aggregate_rating,
};
res.send(restaur);

Categories

Resources