Map the nested data from other table using promise and async-await - javascript

I need the expert advice for this code. I need to know Is there any better way to solve this.
I am using the mongoose for db. I have a dataset like this:
Below is matchTable:
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
}
],
status: true
}
And I have a Car table in which car name is there on behalf of id
like this
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febce'), name: 'ford' },
{ _id: ObjectId('5c1b4ffd18e2d84b7d6febcg'), name: 'mitsubishi' },
So I want to make join the data from car table, so that response get name on behalf of aid.
Desired result will be like
{
_id: 617bc0113176d717f4ddd6ce,
car: [],
status: true
},
{
_id: 617bc0113176d717f4ddd6cg,
car: [
{
aid: '5c1b4ffd18e2d84b7d6febcg',
name: 'mitsubishi'
}
],
status: true
}
For that I have to merge the car table on matchTable. I have done this but I want to give some suggestion that is there any better way to do or is it fine. I need expert advice.
const getData = await matchTable.find(
{ status: true }
).lean().exec();
let dataHolder = [];
await Promise.all (
getData.map(async x => {
await Promise.all(
x.car.map(async y => {
let data = await Car.findOne(
{ _id: ObjectId(y.aid) },
{ name: 1 }
).lean().exec();
y.name = '';
if (data) {
y.name = data.name;
}
})
)
// If I return { ...x }, then on response it will return {}, {} on car column
dataHolder.push(x) //So I have chosen this approach
})
);
Please guide me if any better and efficient solution is there. Thanks in advance

You can make use of aggregation here.
const pipeline = [
{
$match : { status : true }
},
{
$unwind: '$matchtable',
},
{
$lookup: {
from: "cars",
localField: "car.aid",
foreignField: "_id",
as: "matchcars"
}
},
{
$addFields: {
"car.carName": { $arrayElemAt: ["$matchcars.name", 0] }
}
},
{
$group: {
_id: "$_id",
cars: { $push: "$matchcars" }
}
}
]
const result = await matchTable.aggregate(pipeline).exec();
Please make sure, aid field inside car array (in matchTable collection) is an ObjectId because its being matched to _id (which is an ObjectId) inside cars collection.

Related

Search query String Array Object in mongoose and use elemMatch is not working as expected

I am facing with an error which I am unable to resolve it.
I want to use query search to search if the data of 1 job matches with user data but I am stucked in some cases.
First case it is that my query search of the job looks like this I mean the data.
The problem it is that in the searchFilterSkills.searchSkillsOffer I have an array of objects and I want to match the name of each of them if one of them exist but I am unable to iterate through them because I get an array of Strings with .map() which cannot iterate on it.
After answer from Fabian if all elements matches it returns the elements and it matches but I want if 1 of the elements matches it will return the object.
I tried $in but did not work. Is there any query to use instead of $all
These are my data which I try to search.
"skillsOffer":[
{
"name":"Max",
"rate":0
},
{
"name":"Test",
"rate":0
},
{
"name":"Javascript",
"rate":0
}
],
"country":"DEU",
"postalCode":12345
And these are the user Data which he/she has.
"searchFilter" : {
"remote" : 0,
"data" : [
{
"region" : [
"1"
],
"country" : "DEU",
"searchActive" : false,
"postalCode" : "123",
"available" : {
"$date" : 1664955924380
}
}
]
},
"searchFilterSkills" : {
"searchSkillsOffer" : [
{
"name" : "Javascript",
"rate" : 100
},
{
"name" : "Test",
"rate" : 60
},
{
"name" : "Client",
"rate" : 0
}
],
}
At skillsOffer I want to search if only the name matches not the rate.
Then if remote is 1 then search the above query and without postalCode with remote or the above one and remote.
async searchUsers(req, res, next) {
const jobID = req.query.jobID;
let job = await Job.findById(jobID);
let postalCode = job.postalCode;
postalCode = postalCode.toString().slice(0, 1);
let postalCode2 = job.postalCode;
postalCode2 = postalCode2.toString().slice(0, 2);
let postalCode3 = job.postalCode;
postalCode3 = postalCode3.toString().slice(0, 3);
let postalCode4 = job.postalCode;
postalCode4 = postalCode4.toString().slice(0, 4);
let postalCode5 = job.postalCode;
postalCode5 = postalCode5.toString().slice(0, 0);
let userIds = job.skillsOffer.map(user => user.name).join(",");
let users = await User.find({
"searchFilter.data": {
$elemMatch: {
"$or": [
{
postalCode: postalCode,
},
{
postalCode: postalCode2,
},
{
postalCode: postalCode3,
},
{
postalCode: postalCode4,
},
{
postalCode: postalCode,
},
{
postalCode: postalCode5,
},
]
}
},
"searchFilter.data": {
$elemMatch: {
country: job.country
}
},
'searchFilterSkills.searchSkillsOffer': {
$elemMatch: {
name: {
$regex: new RegExp(`^${job.skillsOffer.map(jt => jt.name)}`, 'i'), but it does not return a thing here
},
},
},
});
if (job.remote.toString() === "1") {
users = await User.find({
"searchFilter.data": {
$elemMatch: {
"$or": [
{
postalCode: postalCode,
},
{
postalCode: postalCode2,
},
{
postalCode: postalCode3,
},
{
postalCode: postalCode4,
},
{
postalCode: postalCode,
},
{
postalCode: postalCode5,
},
]
}
},
"searchFilter.data": {
$elemMatch: {
country: job.country
}
},
"searchFilter.remote": job.remote,
});
}
if (!users) {
res.status(204).json({ error: "No Data" });
return;
}
return res.status(200).send({
user: users.map(t =>
t._id
)
});
},
I assume you want to match each name from skillsOffer array. This way you have to define an $elemMatch object for each name (basically mapping each name).
You can use the following (partial) query in your code in order to check whether all names are contained in your searchFilterSkills.searchSkillsOffer array.
{
'searchFilterSkills.searchSkillsOffer': {
$all: job.skillsOffer
.map((user) => user.name)
.map((name) => ({
$elemMatch: {
name: {
$regex: new RegExp(`^${name}$`, 'i'),
},
},
})),
},
}
If you would like to match any name, you should or the following code:
{
$or: job.skillsOffer
.map((user) => user.name)
.map((name) => ({
'searchFilterSkills.searchSkillsOffer': {
$elemMatch: {
name: {
$regex: new RegExp(`^${name}$`, 'i'),
},
},
},
})),
}

Problem with countDocuments Mongoose: its not actually waiting for the callback function to execute

I've a problem with a Mongoose backend. i guess its an asynchronous problem. Can someone try to explain me how countDocuments works?
albumFields.features = [];
for await (const feature of features) {
await Artist.countDocuments({ _id: feature._id }, (err, count) => {
if (count > 0) {
albumFields.features.push({ _id: feature._id });
}
console.log('artist.count', albumFields.features);
});
console.log('features.forEach', albumFields.features);
}
console.log('!!!', albumFields.features);
the output im getting is:
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
!!! [ { _id: '5ec09f9c99339f3ad85973c0' } ]
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
The output i would expect is:
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' } ]
artist.count [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
features.forEach [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
!!! [ { _id: '5ec09f9c99339f3ad85973c0' },
{ _id: '5ec09fb199339f3ad85973c4' } ]
So the problem is countDocuments() will return a promise only if there is no call back function. In our case we created the callback which gets the number of documents matching the filter.
I am not sure why the code executed the way it did so maybe someone can explain but I did manage to solve the problem.
let arraytest = [];
for (const feature of features) {
await Artist.countDocuments({ _id: feature._id }).then((count) => {
if (count > 0) {
albumFields.features = [
...albumFields.features,
{ _id: feature._id },
];
}
});
}
For anyone else who runs into the same problem, there was a link on the mongoose documentation which is easy to miss.
https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments

How to insert array items in mongoose?

i'm making a web site with MEAN Stack
I made a schema.
below is the schema.
const StoreSchema = mongoose.Schema({
storename: {
type: String
},
storenumber: {
type: Number
},
product: [{
pname: String,
pcode: Number,
price: String,
detail: String
}]
});
product is array.
when i make this schema, mongoose make it like this.
_id:5c3afaa96649e84364aab24b
storenumber:1
product:Array
__v:0
now there is a problem .
i want to insert values to pname, pcode, price, detail in product array.
but i can't push the values to the array.
below is the inserting value code.
module.exports.addProd = function(newProd, callback) {
Store.updateOne({
storenumber: number
}, {
$push: {
pname: newProd.pname
},
$push: {
pcode: newProd.pcode
},
$push: {
price: newProd.price
},
$push: {
detail: newProd.detail
}
}, callback);
}
but it doesn't work.
even i wrote product.
the values in newProd are all correct
what i want is like this
product[0] = {
pname:blah,
pcode:1,
price:5,
detail:blah blah
}
how can i insert values to array in mongoose?
please help me
You have to push the whole object together rather than each field like this -
Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } }, callback);
and also rather than using callback you should use async await -
module.exports.addProd = async newProd => {
let res = await Store.updateOne({
storenumber: number
}, { $push: { "product": newProduct } });
return res;
}

How to return an array of objects in GraphQL, possibly using the same endpoint as the one that returns a single object?

I am making a GraphQL API where I would be able to retrieve a car object by its id or retrieve all the cars when no parameter is provided.
Using the code below, I am successfully able to retrieve a single car object by supplying id as a parameter.
However, in the case where I would expect an array of objects i.e. when I supply no parameter at all, I get no result on GraphiQL.
schema.js
let cars = [
{ name: "Honda", id: "1" },
{ name: "Toyota", id: "2" },
{ name: "BMW", id: "3" }
];
const CarType = new GraphQLObjectType({
name: "Car",
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString }
})
});
const RootQuery = new GraphQLObjectType({
name: "RootQueryType",
fields: {
cars: {
type: CarType,
args: {
id: { type: GraphQLString }
},
resolve(parent, args) {
if (args.id) {
console.log(cars.find(car => car.id == args.id));
return cars.find(car => car.id == args.id);
}
console.log(cars);
//***Problem Here***
return cars;
}
}
}
});
Test queries and their respective results:
Query 1
{
cars(id:"1"){
name
}
}
Query 1 Response (Success)
{
"data": {
"cars": {
"name": "Honda"
}
}
}
Query 2
{
cars{
name
}
}
Query 2 Response (Fail)
{
"data": {
"cars": {
"name": null
}
}
}
Any help would be much appreciated.
A Car and a List of Cars are effectively two separate types. A field cannot resolve to a single Car object one time, and an array of Car object another.
Your query is returning null for the name because you told it the cars field would resolve to a single object, but it resolved to an array instead. As a result, it's looking for a property called name on the array object and since one doesn't exist, it's returning null.
You can handle this in a couple of different ways. To keep things to one query, you can use filter instead of find and change the type of your query to a List.
cars: {
type: new GraphQLList(CarType), // note the change here
args: {
id: {
type: GraphQLString
},
},
resolve: (parent, args) => {
if (args.id) {
return cars.filter(car => car.id === args.id);
}
return cars;
}
}
Alternatively, you could split this into two separate queries:
cars: {
type: new GraphQLList(CarType),
resolve: (parent, args) => cars,
},
car: {
type: CarType,
args: {
id: {
// example of using GraphQLNonNull to make the id required
type: new GraphQLNonNull(GraphQLString)
},
},
resolve: (parent, args) => cars.find(car => car.id === args.id),
}
Check the docs for more examples and options.

Mongoose $slice and get orginal size array

I'm currently trying to get the total amount of items in my News object, and return a slice of the items as objects.
I found out how to use the $slice operator in my query, but I don't know how to get the original size of the array of items.
The code I'm currently using in NodeJS:
if (req.query.limit) {
limit = 5;
}
News.findOne({ connected: club._id }, {items: {$slice: limit}}).exec(function (err, news) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else if (!news || news.items.length === 0) {
res.jsonp([]);
} else {
const returnObj = { items: [], totalNumber: 0 };
const items = news.items.sort(function (a, b) {
return b.date - a.date
});
res.jsonp({
items: items,
totalNumber: news.items.length
});
}
});
The Mongo model:
var mongoose = require('mongoose'),
validator = require('validator'),
Schema = mongoose.Schema;
var NewsSchema = new Schema({
connected: {
type: Schema.Types.ObjectId,
required: 'Gelieve een club toe te wijzen.',
ref: 'Club'
},
items: [{
userFirstName: String,
action: String,
date: Date,
targetName: String
}],
created: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('News', NewsSchema);
How would I do this efficiently?
Thanks!
EDIT: final code which works:
News.aggregate([{
$match: {
connected: club._id
}
}, {
$project: {
totalNumber: {
$size: '$items'
},
items: {
$slice: ['$items', limit]
}
}
}
]).exec(function (err, news) {
console.log(news);
if (!news || news[0].items.length === 0) {
res.jsonp([]);
} else {
res.jsonp(news[0]);
}
});
You cannot have both information at once using find and $slice.
The soluce you have :
Use aggregate to return the count and only the sliced values.
Like :
[{
$project: {
count: {
$size: "$params",
},
params: {
$slice: ["$params", 5],
},
},
}]
To help you out making aggregate, you can use the awesome mongodb-compass software and its aggregate utility tool.
Use a find without $slice, get the number of item there, and then slice in javascript the array before returning it.
EDIT :
[{
$sort: {
'items.date': -1,
},
}, {
$project: {
count: {
$size: "$items",
},
params: {
$slice: ["$items", 5],
},
},
}]

Categories

Resources