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

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'),
},
},
},
})),
}

Related

Sequelize search "Unknown column 'contact.name' in where clause

I have a service that is in charge of bringing the tickets with the last message of the users.
For this, the Contact, Queue, WhatsApp models were added to the include.
The problem is that when adding the Tags model, closely related to "Contact", the service stopped working and response with:
"Unknown column 'contact.name' in where clause
The only thing I added was the relationship with Tags, since it is new. Help me understand? It's like it no longer recognizes the column
interface Request {
searchParam?: string;
pageNumber?: string;
status?: string;
date?: string;
showAll?: string;
userId: string;
withUnreadMessages?: string;
queueIds: number[];
}
interface Response {
tickets: Ticket[];
count: number;
hasMore: boolean;
}
const ListTicketsService = async ({
searchParam = "",
pageNumber = "1",
queueIds,
status,
date,
showAll,
userId,
withUnreadMessages
}: Request): Promise<Response> => {
let whereCondition: Filterable["where"] = {
[Op.or]: [{ userId }, { status: "pending" }],
queueId: { [Op.or]: [queueIds, null] }
};
let includeCondition: Includeable[];
includeCondition = [
{
model: Contact,
as: "contact",
attributes: ["id", "name", "number", "profilePicUrl"],
include: [{
model: Tags,
as: "tags",
attributes: ["name"],
}]
},
{
model: Queue,
as: "queue",
attributes: ["id", "name", "color"]
},
{
model: Whatsapp,
as: "whatsapp",
attributes: ["name"]
},
];
if (showAll === "true") {
whereCondition = { queueId: { [Op.or]: [queueIds, null] } };
}
if (status) {
whereCondition = {
...whereCondition,
status
};
}
if (searchParam) {
const sanitizedSearchParam = searchParam.toLocaleLowerCase().trim();
includeCondition = [
...includeCondition,
{
model: Message,
as: "messages",
attributes: ["id", "body"],
where: {
body: where(
fn("LOWER", col("body")),
"LIKE",
`%${sanitizedSearchParam}%`
)
},
required: false,
duplicating: false
}
];
whereCondition = {
...whereCondition,
[Op.or]: [
{
"$contact.name$": where(
fn("LOWER", col("contact.name")),
"LIKE",
`%${sanitizedSearchParam}%`
)
},
{ "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } },
{
"$message.body$": where(
fn("LOWER", col("body")),
"LIKE",
`%${sanitizedSearchParam}%`
)
}
]
};
}
if (date) {
whereCondition = {
createdAt: {
[Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))]
}
};
}
if (withUnreadMessages === "true") {
const user = await ShowUserService(userId);
const userQueueIds = user.queues.map(queue => queue.id);
whereCondition = {
[Op.or]: [{ userId }, { status: "pending" }],
queueId: { [Op.or]: [userQueueIds, null] },
unreadMessages: { [Op.gt]: 0 }
};
}
const limit = 40;
const offset = limit * (+pageNumber - 1);
const { count, rows: tickets } = await Ticket.findAndCountAll({
where: whereCondition,
include: includeCondition,
distinct: true,
limit,
offset,
order: [["updatedAt", "DESC"]], logging: console.log
});
const hasMore = count > offset + tickets.length;
return {
tickets,
count,
hasMore
};
};
export default ListTicketsService;
Another thing is, i don't know how but this is giving me only unique record. The problem is, 1 contact may have n Tags. So, its possible do this query?
Regards

How do you check if value exist in object of object's array?

I want to know which logic i should use to check every object's array of parent object contained in grand parent object
Hi guys i want to check if this value for example : "127.0.0.1" exists in this object (MyObject has like 2k objects in it)
{
"name" : MyObject
"value": [
{
"name" : "Object1",
"properties":{
"address" : [
"13.65.25.19/32",
"13.66.60.119/32",
]
}
},
{
"name" : "Object2",
"properties":{
"address" : [
"13.65.25.19/32",
"127.0.0.1",
]
}
}
]
}
Btw does include() needs to match the whole string or for example if 127.0.0.1 is like this in my object 127.0.0.1/32, i can still retrieve it even if there is a ip range ?
Your data is structured quite specifically, so you can write a custom method which you can call over and over again. It will check for a
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '127.0.0.1';
const includesAddress = (address) => {
for (const val of obj.value) {
if (val.properties.address.some((a) => address === a)) return true;
}
return false;
};
console.log(includesAddress(address));
Array.flatMap implementation
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '127.0.0.1';
const output = obj.value.flatMap(item => item.properties.address).includes(address);
console.log(output);
If you want check if the partial ip addess is included in the list, you should make use of a regex implementation.
Sample Implementation
const obj = {
name: 'MyObject',
value: [
{
name: 'Object1',
properties: {
address: ['13.65.25.19/32', '13.66.60.119/32'],
},
},
{
name: 'Object2',
properties: {
address: ['13.65.25.19/32', '127.0.0.1'],
},
},
],
};
const address = '13.65.25.19';
const regex = new RegExp(address, 'i')
const output = obj.value.flatMap(item => item.properties.address).filter(x => regex.test(x)).length > 0;
console.log(output);

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

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.

Add new field to report - Script - NodeJS and MongoDB

I have to run a report from a script.
The script works fine
Need to add a new field that indicates if the user is admin or not.
I am not sure how to add that with a true or false condition.
Right now it is returning undefined or false.
The field in mongo database is this one:
mongo-database
var orderTotalsByUserId = {};
db.orders.aggregate([{ $match: { completed: { $exists: true }, completed: { $gt : new Date('2020-08-01') } } }, { $group: { _id: { userId : "$userId" }, total : { $sum: "$total"} } }]).forEach( d => orderTotalsByUserId[d._id.userId] = d.total )
var userIdsByOrders = db.orders.aggregate([{ $match: { completed: { $exists: true }, completed: { $gt : new Date('2020-08-01') } } }, { $group: { _id: { userId : "$userId" }, total : { $sum: "$total"} } }]).map( d => ObjectId(d._id.userId) )
var customers = {};
db.organizations.find().forEach(c => { customers[c._id.valueOf()] = c });
db.users.find({ _id: { $in: userIdsByOrders } }).forEach(d => print(`${d.firstName}, ${d.lastName}, ${d.profile.email}, ${customers[d.customerId].name},${d.sensitive.active.globalAdmin}, ${(orderTotalsByUserId[d._id.valueOf()] * .01).toFixed(2)}`) )
After some time, I've been able to solve it.
Hope it can be helpful for someone else.
First filtering the featurePermissions field in the pipeline.
Then, created an if statement, indicating that if the user has a Customer, it should print the following values.
The solution was:
var orderTotalsByUserId = {};
db.orders.aggregate([{ $match: { completed: { $exists: true }, completed: { $gt : new Date('2020-08-01') } } }, { $group: { _id: { userId : "$userId" }, total : { $sum: "$total"} } }]).forEach( d => orderTotalsByUserId[d._id.userId] = d.total );
var customers = {};
db.organizations.find().forEach(c => { customers[c._id.valueOf()] = c });
db.users.find({ featurePermissions: { $exists: true } }, { _id: 1, firstName: 1, lastName: 1, profile: 1, customerId: 1, featurePermissions: 1 }).forEach(d => { if (customers[d.customerId]) { print(`${d.firstName}, ${d.lastName}, ${d.profile.email}, ${customers[d.customerId].name}, ${orderTotalsByUserId[d._id.valueOf()] ? (orderTotalsByUserId[d._id.valueOf()] * .01).toFixed(2) : 0 }, ${d.featurePermissions.user_management ? 'true' : 'false' }`)}});

How to sort data with dynamic fields in PouchDB?

I'm having this query to index first_name and sort data according to it and it's working fine
try {
Users.createIndex({
index: { fields: ['first_name'] }
}).then(function (response) {
console.log(response);
}).catch(function (err) {
console.log(err);
});
const users = (await Users.find({
limit, skip: limit * (page - 1),
selector: {first_name: {$gt: null}},
sort: [ { 'first_name' : 'asc'} ]
})).docs;
But when I try to use variables it triggers an error
Error: Cannot sort on field(s) "orderBy" when using the default index
Code
orderBy = (query.params !== undefined && query.params.orderBy !== undefined) ? query.params.orderBy.sortField : 'first_name',
sortOrder = (query.params !== undefined && query.params.orderBy !== undefined) ? query.params.orderBy.sortOrder : 'asc'
console.log('orderBy: ' + orderBy) // first_name
console.log('sortOrder: ' + sortOrder) // asc
try {
Users.createIndex({
index: { fields: [orderBy] }
}).then(function (response) {
console.log(response);
}).catch(function (err) {
console.log(err);
});
const users = (await Users.find({
limit, skip: limit * (page - 1),
selector: {orderBy: {$gt: null}},
sort: [ { orderBy : sortOrder } ]
})).docs;
How can I edit this to make it work with dynamic variable just like the static variable?
The variable orderBy is not going to be substituted by value in the following code
selector: {orderBy: {$gt: null}},
sort: [ { orderBy : sortOrder } ]
The code evaluates orderBy literally. To assign a dynamic key to an object, use the object indexer:
myObject[myVar] = myVal;
Therfore in your code, something like this should do.
const query = {
selector: {},
sort: []
};
// setup selector
query.selector[prop] = {
$gt: null
};
// setup sort
let sortParam = {};
sortParam[prop] = sortDirection;
query.sort.push(sortParam);
I added a pouchDB snippet illustrating that concept.
let db;
// init example db instance
async function initDb() {
db = new PouchDB('test', {
adapter: 'memory'
});
await db.bulkDocs(getDocsToInstall());
}
initDb().then(async() => {
await db.createIndex({
index: {
fields: ['first_name']
}
});
await doQuery("first_name", "desc");
});
async function doQuery(prop, sortDirection) {
const query = {
selector: {},
sort: []
};
// setup selector
query.selector[prop] = {
$gt: null
};
// setup sort
let sortParam = {};
sortParam[prop] = sortDirection;
query.sort.push(sortParam);
// log the query
console.log(JSON.stringify(query, undefined, 3));
// exec the query
const users = (await db.find(query)).docs;
users.forEach(d => console.log(d[prop]));
}
// canned test documents
function getDocsToInstall() {
return [{
first_name: "Jerry"
},
{
first_name: "Bobby"
},
{
first_name: "Phil"
},
{
first_name: "Donna"
},
{
first_name: "Ron"
},
{
first_name: "Mickey"
},
{
first_name: "Bill"
},
{
first_name: "Tom"
},
{
first_name: "Keith"
},
{
first_name: "Brent"
},
{
first_name: "Vince"
},
]
}
<script src="https://cdn.jsdelivr.net/npm/pouchdb#7.1.1/dist/pouchdb.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.memory.min.js"></script>
<script src="https://github.com/pouchdb/pouchdb/releases/download/7.1.1/pouchdb.find.min.js"></script>

Categories

Resources