How to use Query using regex in Node js (mongoose)? - node.js

I cannot seem to wrap my head around how regex works for Mongoose in Node js. I have looked through some of the regex examples on different websites and on Stackoverflow. Is regex with find() not usable for an object with multiple properties? If so, must I use aggregate instead and how should I go about using it?
I am trying to query for documents that return date === "June" where "June" is a substring.
Model.find({ 'meta': { 'date': { $regex: '.*June.', $options: 'i'} } })
.then((data) => {
console.log(`[*csv metadata 2*] Data: ${data}`);
res.json(data);
})
.catch((error) => {
console.log(`error: ${error}`);
});
I have also tried Model.find({ 'meta': { 'date': { $regex: /.*June./, $options: 'i'} } }), none of which worked.
Mongodb structure
"meta": {
"products": {
"bolder": {
"ver": "v1.0",
"csv": "filePath"
},
"rock": {
"ver": "v1.0",
"csv": "filePath"
},
"stone": {
"ver": "v1.0",
"csv": "filePath"
},
"mountain": {
"ver": "v1.0",
"csv": "filePath"
}
},
"date": "20 June 2022",
"env": "Indoor",
"desc": "This is a comment in the textarea."
}

This is completely theoretical...
first just grab the collection...
Secondly create a new list with aggregated data...
The data should have the field you're searching for including a ref to the main obj
i.e const data ={ "Id" : "doc_id", "Date" : "June 22", "Other" : "relevant fields" }
Search the new list,
to optimize...you can also add some fields to this new object for data you don't want to refetch by querying the entire the OG collection objects

Related

mongoose find in document object

Lets say I have a mongoose schema, something like:
mongoose.Schema({
website_id:mongoose.SchemaTypes.ObjectId,
data:Object
})
where data field contains JSON Object. Something like:
{
"actions":[
{
"action":"pageChange",
"url":"http://localhost:3000/login",
"dom":"",
"timestamp":1653341614846
},
{
"action":"pageChange",
"url":"http://localhost:3000/signup",
"dom":"",
"timestamp":1653341626442
},
{
"action":"pageChange",
"url":"http://localhost:3000/view",
"dom":"",
"timestamp":1653341626442
},
{
"action":"pageChange",
"url":"http://localhost:3000/login",
"dom":"",
"timestamp":1653341626442
}
]
}
Is there any way I can get all documents, where data field object contains http://localhost:3000/login as url, without getting all the documents first and looping them through.
Object is going to be dynamic generated, and items will repeat themselves
Of course, there are several ways and in this case, one of the best ways is to use "aggregate"
db.collection.aggregate([
{$unwind: "$actions"},
{ $match: {"actions.url": "http://localhost:3000/login"}},
{$group: {
_id: "$_id",
actions: {$push: "$actions"}
}
}
])
return Response :
{
"actions": [
{
"action": "pageChange",
"dom": "",
"timestamp": 1.653341614846e+12,
"url": "http://localhost:3000/login"
},
{
"action": "pageChange",
"dom": "",
"timestamp": 1.653341626442e+12,
"url": "http://localhost:3000/login"
}
]
}
If i find other or better methods, I'll definitely share..
I hope this solution helps you.
Sure you can do that. You can specify the object nesting in form of string in the query.
await MyModel.find({ 'data.objectKey.items.item': 'text I want to find' }).exec();

updating multiple different array object elements in mongodb with nodejs

mongodb collection:
"_id": ObjectId("5e2ac528e9d99f3074f31de7"),
"publications": [
{
"_id": ObjectId("5e2ac528e9d99f3074f31de8"),
"name": "Times of India",
"productCode": "TCE1",
"tradeCopies": 40
},
{
"_id": ObjectId("5e2ac528e9d99f3074f31de9"),
"publicationName": "Economic Times",
"productCode": "ECE1",
"tradeCopies": 100
}
],
"orderCreatedBy": ObjectId("5e2977e1cc1208c65c00648b"),
"submittedTo": ObjectId("5e2555363405363bc4bf86c2"),
Nodejs Code
i would get multiple "productCode" like "TCE1","ECE1" etc,and i need to update tradeCopies of all the object array elements in one go according to their productCodes
Here is what i tried
exports.editOrder = async (req, res, next) => {
const { orderId, dealerId, productCode, tradeCopies } = req.body;
try{
const orders: await Order.updateOne(
{ _id: orderId,
submittedTo: dealerId,
"publications.productCode": productCode},
{$set:{"publications.$.tradeCopies":50}}
)
res.status(200).json({
orders,
message: "order submitted"
});
} catch (error) {
res.send(error);
}
};
CONCERNS
1-this query is updating only 1 array object element according to the matched productCode i want all the tradeCopies of all the array objects according to their productCodes to be updated in onego
2- the above query is working only in mongo Shell not in nodejs driver and whenever i remove double quotes in nodejs query vscode shows there might an error
You want to use arrayFilters.
const orders: await Order.updateOne(
{ _id: orderId,
submittedTo: dealerId,
"publications.productCode": productCode
},
{ $set: { "publications.$[element].tradeCopies":50 } },
{ arrayFilters: [ { "element.productCode": productCode } ] }
)
I'm not sure what you mean by removing the double quotes, but this snippet is nodejs driver compatible.

Right outer join in aggregation pipeline

I have two collections, let's call them Cats and Parties, with the following schemas:
Cat
{ name: String }
Party
{ date: Date, attendants: [{ cat: { ref: 'Cat' }, role: String }] }
where role symbolizes some other attribute, say, whether the attending cat is a VIP member.
Now I want to get a list of all cats that exist (even those poor kitties who never attended any party) and for each cat, I want a list of all the roles it ever had for at least one party. Furthermore, I want this entire list to be sorted by the (per cat) last attended party's date with cats who never attended any party being last.
This raises the following problems for me:
Aggregrating over Parties excludes party-pooper kitties who never joined a party.
Aggregating over Cats sort of goes »the wrong way« because I cannot $lookup parties the cat attended because that information is in a subdocument array.
The pipeline I currently have gives me all cats who attended at least one party with a list of their roles, but doesn't sort by the last attended party. In fact, I could live with excluding cats who never attended a party, but the sorting is crucial for me:
Party.aggregate([
{ $unwind: '$attendants' },
{ $project: { role: '$attendants.role', cat: '$attendants.cat' } },
{
$group: {
_id: '$cat',
roles: { $addToSet: '$role' }
}
},
{
$lookup: {
from: 'cats',
localField: '_id',
foreignField: '_id',
as: 'cat'
}
},
{ $unwind: '$cat' },
// (*)
{ $addFields: { 'cat.roles': '$roles' } },
{ $replaceRoot: { newRoot: '$cat' } }
])
My current idea would basically be a right outer join at (*) to add a list of parties the cat has attended, $project that to the party's date and then $group using $max to get the latest date. Then I can $unwind that now one-element array and $sort over it in the end.
The problem is that right outer joins don't exist in mongo, AFAIK, and I don't know how to get that list of parties per cat within the pipeline.
To clarify, the expected output should be something like
[
{
"_id": "59982d3c7ca25936f8c327c8",
"name": "Mr. Kitty",
"roles": ["vip", "birthday cat"],
"dateOfLastParty": "2017-06-02"
},
{
"_id": "59982d3c7ca25936f8c327c9",
"name": "Snuffles",
"roles": ["best looking cat"],
"dateOfLastParty": "2017-06-01"
},
...
{
"_id": "59982d3c7ca25936f8c327c4",
"name": "Sad Face McLazytown",
"roles": [],
"dateOfLastParty": null
},
]
As stated, you want the "cats" so use the Cat model and do the "left outer join" that is actually inherent to $lookup, rather than asking for a "right outer join" from the opposing collection, since a "right outer join" is not possible with MongoDB at this time.
It's also far more practical as a "left join", because you want "cats" as your primary source of output. The only thing to consider when linking to "Party" is that each "Cat" is listed in an array, and therefore you get the whole document back. So all that needs to be done is in "post processing" after the $lookup, you simply "filter" the array content for the matching entry of the current cat.
Fortunately we get good features with $arrayElemAt and $indexOfArray, that allow us to do that exact extraction:
let kitties = await Cat.aggregate([
{ '$lookup': {
'from': Party.collection.name,
'localField': '_id',
'foreignField': 'attendants.cat',
'as': 'parties'
}},
{ '$replaceRoot': {
'newRoot': {
'$let': {
'vars': {
'parties': {
'$map': {
'input': '$parties',
'as': 'p',
'in': {
'date': '$$p.date',
'role': {
'$arrayElemAt': [
'$$p.attendants.role',
{ '$indexOfArray': [ '$$p.attendants.cat', '$_id' ] }
]
}
}
}
}
},
'in': {
'_id': '$_id',
'name': '$name',
'roles': '$$parties.role',
'dateOfLastParty': { '$max': '$$parties.date' }
}
}
}
}}
]);
So my concept of "optimal" processing here actually uses $replaceRoot here because you can define the whole document under a $let statement. The reason I'm doing that is so we can take the "parties" array output from the previous $lookup and reshape each entry extracting the matching "role" data for the current "kitty" at that given party. This we can actually make a variable itself.
The reason for the "array variable" is because we can then use $max to extract the "largest/last" date property as "singular" and still extract the "role" values as an "array" from that reshaped content. This makes it easy to define the fields you wanted.
And since it was a "left join" started from Cat in the first place, then those poor kitties that missed out on all parties are still there, and still have the desired output.
Two aggregation pipeline stages. What could be more simple!
As a full listing:
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const uri = 'mongodb://localhost/catparty',
options = { useMongoClient: true };
const catSchema = new Schema({
name: String
});
const partySchema = new Schema({
date: Date,
attendants: [{
cat: { type: Schema.Types.ObjectId, ref: 'Cat' },
role: String
}]
});
const Cat = mongoose.model('Cat', catSchema);
const Party = mongoose.model('Party', partySchema);
function log(data) {
console.log(JSON.stringify(data,undefined,2))
}
(async function() {
try {
const conn = await mongoose.connect(uri,options);
// Clean collections
await Promise.all(
Object.keys(conn.models).map( m => conn.models[m].remove({}) )
);
var cats = await Cat.insertMany(
['Fluffy', 'Snuggles', 'Whiskers', 'Socks'].map( name => ({ name }) )
);
cats.shift();
cats = cats.map( (cat,idx) =>
({ cat: cat._id, role: (idx === 0) ? 'Host' : 'Guest' })
);
log(cats);
let party = await Party.create({
date: new Date(),
attendants: cats
});
log(party);
let kitties = await Cat.aggregate([
{ '$lookup': {
'from': Party.collection.name,
'localField': '_id',
'foreignField': 'attendants.cat',
'as': 'parties'
}},
{ '$replaceRoot': {
'newRoot': {
'$let': {
'vars': {
'parties': {
'$map': {
'input': '$parties',
'as': 'p',
'in': {
'date': '$$p.date',
'role': {
'$arrayElemAt': [
'$$p.attendants.role',
{ '$indexOfArray': [ '$$p.attendants.cat', '$_id' ] }
]
}
}
}
}
},
'in': {
'_id': '$_id',
'name': '$name',
'roles': '$$parties.role',
'dateOfLastParty': { '$max': '$$parties.date' }
}
}
}
}}
]);
log(kitties);
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
And example output:
[
{
"_id": "59a00d9528683e0f59e53460",
"name": "Fluffy",
"roles": [],
"dateOfLastParty": null
},
{
"_id": "59a00d9528683e0f59e53461",
"name": "Snuggles",
"roles": [
"Host"
],
"dateOfLastParty": "2017-08-25T11:44:21.903Z"
},
{
"_id": "59a00d9528683e0f59e53462",
"name": "Whiskers",
"roles": [
"Guest"
],
"dateOfLastParty": "2017-08-25T11:44:21.903Z"
},
{
"_id": "59a00d9528683e0f59e53463",
"name": "Socks",
"roles": [
"Guest"
],
"dateOfLastParty": "2017-08-25T11:44:21.903Z"
}
]
And you should be able to see how those "roles" values actually become an array with more data. And if you need that to be a "unique list", then simply wrap with $setDifference as in:
'roles': { '$setDifference': [ '$$parties.role', [] ] },
And that is also covered

Mongoose: Update does not work in nested array object

I have a document with the array of objects and one object contains multiple objects I want to update inner object with $set but didn't get any luck.
can anybody give me any hint so that I can resolve it?.
This is my object:
{
"_id": ObjectId("56fbfafdf86fa6161911d104"),
"site": "xyz",
"adsPerCategory": NumberInt(2),
"sampledAt": ISODate("2016-03-30T16:12:45.138+0000"),
"items": [
{
"id": "4563873",
"content": {
"title": "WATER DISTILLERS",
"body": "Perfect to save money.",
}
},
{
"id": "4563s23232873",
"content": {
"title": "Cola water",
"body": "Perfect for body.",
}
}
]
}
I want to update body.
for now, I have given single object but it can be multiple.
Here what I tried
models.Sample.update(
{
_id: samples._id
},
'$set': {
'items.0.content.body': body.description
},
function(err, numAffected) {
console.log(err);
console.log('Affected....', numAffected);
}
);
It's working fine if I put 0 but I want to make it dynamic.
Like 'items.index.content.body': body.description
Thank You.
I think you can do something like this.
models.Sample.find({ _id: ObjectId(samples._id) })
.forEach(function (doc) {
doc.items.forEach(function (element, index, array) {
items[index].content.body = body.description;
});
models.Sample.save(doc);
});

Node + Mongodb. $pull not working?

I am using Node.js and MongoDB and I'm trying to setup a DELETE route. In the function responsible for handling the delete I am using Mongo's "$pull" operator. I've looked at a couple of examples now and I don't know what I am doing wrong.
Here's a sample of how the database documents are setup
{
"_id": {
"$oid": "123abc"
},
"sleepData": [
{
"date": "03/28/2016",
"hour": "11",
"minute": "11",
"meridiem": "PM",
"feeling": "7"
},
{
"date": "03/29/2016",
"hour": "3",
"minute": "41",
"meridiem": "PM",
"feeling": "1"
},
{
"date": "03/30/2016",
"hour": "1",
"minute": "29",
"meridiem": "AM",
"feeling": "5"
},
{
"date": "03/30/2016",
"hour": "1",
"minute": "38",
"meridiem": "AM",
"feeling": "4"
},
]
}
*Note the near-duplicate data, thus the reason why my $pull query is so specific.
Here is my function for the route
module.exports.DELETE = function(req, res) {
var sleepDataToDelete = {
date: req.query.date,
hour: req.query.hour,
minute: req.query.minute,
meridiem: req.query.meridiem,
feeling: req.query.feeling
};
// next code block is what this console prints out
console.log("Deleting req.query:\n", sleepDataToDelete);
var sleepObjectId = req.query.sleepObjectId;
var sleepDataCollection = db.get().collection('sleepData');
sleepDataCollection.update(
{
_id: sleepObjectId
},
{
$pull: {
sleepData: {
date: sleepDataToDelete.date,
hour: sleepDataToDelete.hour,
minute: sleepDataToDelete.minute,
meridiem: sleepDataToDelete.meridiem,
feeling: sleepDataToDelete.feeling
}
}
},
function(err, result) {
if(err) {
console.log("err", err);
return res.status(400).end();
} else {
console.log("Count: ", result.result.n);
console.log("Deleted! :) ");
return res.status(200).end();
}
}
);
};
This is what the console.log("Deleting req.query:\n", sleepDataToDelete); prints out, which also matches the third index in the sleepData array.
Deleting req.query:
{
date: '03/30/2016',
hour: '1',
minute: '29',
meridiem: 'AM',
feeling: '5'
}
I have even tried putting the json field names in double/single quotes, but that didn't work either. The number of objects modified is 0. I have also tried reducing the "$pull {...}" query to just "date" instead of having "date", "hour", "minute", "meridiem", and "feeling." This still results in 0 modified items from the print statement.
As #BlakesSeven pointed out, I was not passing in an ObjectId in my query. So, credit goes to him. Needless to say, this solved my issue.

Resources