Node + Mongodb. $pull not working? - node.js

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.

Related

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

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

how to write dynamic query in mongoose

i do not know if this has answer or not but i couldn't fix my issue .
Shift.updateOne({
$and: [{phone: number}, {statusSH: "open"}, {
'order.startTime': start,
'order.finishTime': finish
},{'order.orders.5.stat':"false"}]
},{'$set': {'order.orders.5.stat': "true"}},function (error,update) {
if (update){
}
});
this 'order.orders.5.stat' works fine . but i don't know how i can use dynamic index . like if i have a "var index" where should i add that to query . it seems if i 'order.orders.'+index+'.stat' i get error .
All helps will be appreciated. Thank You.
Edit :
doc should be like this
{
"_id": "5f52745e77abf55e80228379",
"phone": "09898989666",
"final": true,
"statusSH": "open",
"created_time": "1599239262",
"__v": 0
"order": {
"startTime": "139912292135",
"finishTime": "139912292240",
"orders": [
{
"_id": "5f52745e77abf55e8022837a",
"stat": "false",
},
{
"_id": "5f52745e77abf55e8022837b",
"stat": "false",
}
]
}
}
You need to use [] when you concat key of object,
var index = "5";
var number = "09898989666";
var start = "139912292135";
var finish = "139912292240";
Shift.updateOne({
$and: [
{ phone: number },
{ statusSH: "open" },
{
'order.startTime': start,
'order.finishTime': finish
},
{ ['order.orders.' + index + '.stat']: "false" } // change here
]
},
{
'$set': { ['order.orders.' + index + '.stat']: "true" } // change here
},
function(error, update) {
if (update) {
console.log(update)
}
});
x='order.orders.{}.stat'.format(index)
.... x:"false"

unknown operator "0" when doing "$elemMatch" in .find()

Issue
I have a pouchdb-express server I am using for some tests for a CouchDB database.
I have the following database with an item like so:
{
_id: "12345",
email: 'u0#email.com',
companies: [{ id: 'company', uid: 'u0' }]
}
I then run the following command that works on my API hitting the CouchDB database but does not work when I try it on the PouchDB Express server.
.find({selector: { "companies": { "$elemMatch": {id: "company", uid: "u0"} } }})
I get the following error:
{ error: 'bad_request',
  reason:  'unknown operator "0" - should be one of $eq, $lte, $lt, $gt, $gte, $exists, $ne, $in, $nin, $size, $mod, $regex, $elemMatch, $type, $allMatch or $all',
  name: 'bad_request',
  status: 400,
  message:  'unknown operator "0" - should be one of $eq, $lte, $lt, $gt, $gte, $exists, $ne, $in, $nin, $size, $mod, $regex, $elemMatch, $type, $allMatch or $all' }
I also get the same exact error during the following query:
.find({
limit:9999999,
selector:{
$or: [
{$and: [{type:"something"},{uid:"u0"}] },
{$and: [{type:"document"}]}
]
}
})
I've also tried doing $eq almost exactly like the test suite does with still no dice.
Does anyone know why this would happen?
Info
Environment: Node.JS
Server: PouchDB Server
Here is my package.json
"pouchdb-find": "^6.4.3", // tried 7.0.0 and still have the issue
"pouchdb-node": "^6.4.3",
"pouchdb-server": "^4.1.0",
I have tinkered with this and discovered that for records like:
{
"borough": "Brooklyn",
"cuisine": "American ",
"marks": [50, 60, 45, 43],
"grades": [
{
"grade": "A",
"score": 5
},
{
"grade": "A",
"score": 7
},
{
"grade": "A",
"score": 12
},
{
"grade": "A",
"score": 12
}
],
"name": "Riviera Caterer"
},
This simple selector will return the correct results :
{ selector: { '$elemMatch': { '$gte': 0, '$lt': 30 } } }
and, while this "composite (?)" selector, ignores mismatches, and returns all rows!! ...
{ selector: 'grades': { '$elemMatch': { 'score': 14 } } }
... this one bombs with the error you indicate :
{ selector: 'grades': { '$elemMatch': { "grade": "B" } } }
I suspect the "pouchdb-find" version of $elemMatch can only handle simple arrays, not arrays of objects.
Seems like a PR is required :-(

MongoDB query and projection on subdocument array returns also array of another document

I am trying to query an embedded subdocument and then only return an array in that subdocument via projection. After a query you can select fields that you want returned via projection. I want to use the native functionality because it is possible and the most clean way. The problem is it returns arrays in two documents.
I tried different query and projection options, but no result.
User model
// Define station schema
const stationSchema = new mongoose.Schema({
mac: String,
stationName: String,
syncReadings: Boolean,
temperature: Array,
humidity: Array,
measures: [{
date: Date,
temperature: Number,
humidity: Number
}],
lastUpdated: Date
});
// Define user schema
var userSchema = mongoose.Schema({
apiKey: String,
stations : [stationSchema]
}, {
usePushEach: true
}
);
api call
app.get('/api/stations/:stationName/measures',function(req, res, next) {
var user = {
apiKey: req.user.apiKey
}
const query = {
apiKey: user.apiKey,
'stations.stationName': req.params.stationName
}
const options = {
'stations.$.measures': 1,
}
User.findOne(query, options)
.exec()
.then(stations => {
res.status(200).send(stations)
})
.catch(err => {
console.log(err);
res.status(400).send(err);
})
});
Expected result
{
"_id": "5c39c99356bbf002fb092ce9",
"stations": [
{
"stationName": "livingroom",
"measures": [
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:45.468Z",
"_id": "5c3a6f09fd357611f8d078a0"
},
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:46.500Z",
"_id": "5c3a6f0afd357611f8d078a1"
},
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:47.041Z",
"_id": "5c3a6f0bfd357611f8d078a2"
}
]
}
]
}
Actual result
{
"_id": "5c39c99356bbf002fb092ce9",
"stations": [
{
"stationName": "livingroom",
"measures": [
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:45.468Z",
"_id": "5c3a6f09fd357611f8d078a0"
},
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:46.500Z",
"_id": "5c3a6f0afd357611f8d078a1"
},
{
"humidity": 60,
"temperature": 20,
"date": "2019-01-12T22:49:47.041Z",
"_id": "5c3a6f0bfd357611f8d078a2"
}
]
},
******************************************************
// this whole object should not be returned
{
"stationName": "office",
"measures": []
}
******************************************************
]
}
edit
The answer below with aggregation works, but I still find it odd that I would need so much code. If after my normal query I get the same result with ".stations[0].measures", instead of the whole aggregation pipeline:
.then(stations => {
res.status(200).send(stations.stations[0].measures)
})
The way I read the code, the above does exactly the same as:
const options = {'stations.$.measures': 1}
Where the dollar sign puts in the index 0 as that was the index of the station that matches the query part: stationName: "livingroom"
Can someone explain?
This is not described in terms of mongoose but this will find a particular station name in an array of stations in 1 or more docs and return only the measures array:
db.foo.aggregate([
// First, find the docs we are looking for:
{$match: {"stations.stationName": "livingroom"}}
// Got the doc; now need to fish out ONLY the desired station. The filter will
// will return an array so use arrayElemAt 0 to extract the object at offset 0.
// Call this intermediate qqq:
,{$project: { qqq:
{$arrayElemAt: [
{ $filter: {
input: "$stations",
as: "z",
cond: { $eq: [ "$$z.stationName", "livingroom" ] }
}}, 0]
}
}}
// Lastly, just project measures and not _id from this object:
,{$project: { _id:0, measures: "$qqq.measures" }}
]);
$elemMatch operator limits the contents of an array field from the query results to contain only the first element matching the $elemMatch condition.
Try $elemMatch in Select Query as below :
const query = {
apiKey: user.apiKey,
'stations.stationName': req.params.stationName
}
const options = {
'stations' : {$elemMatch: { 'stationName' : req.params.stationName }}
}

geoNear not returning any results

I'm trying to use geoNear to return documents in my collection that have latitude and longitude coordinates, but not getting any results even though there are documents in the collection that have latitude and longitude coordinates. I'm using mlab to store my data, for example, here is a document in my collection.
var trucker = db.collection('trucker');
{
"_id": {
"$oid": "581e82d00f192a694bb56679"
},
"latitude": 77.0551642746301,
"longitude": 10.825842664395019,
"loc": [
77.0551642746301,
10.825842664395019
],
}
I have the coordinates in two different ways, because I was trying to see if geoNear would pick up the location, and return the result, but still can't find it. This is how I'm using geoNear currently, to find results
exports.geoNear = function (lat, lon ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
trucker.geoNear([job.lon, job.lat], function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
};
app.get('/geoFind', function(req, res) {
AM.geoNear('48.99759239999999', '-123.0683089', function(o){
res.send(200,o);
});
});
I am getting this result when visiting localhost:8000/geoFind:
{
"waitedMS": 0,
"results": [],
"stats": {
"nscanned": 0,
"objectsLoaded": 0,
"maxDistance": 0,
"time": 0
},
"ok": 1
}
Now, I have tried many different ways such as the following but get, Response errorMongoError: 'near' field must be point with the following:
trucker.createIndex( { loc : "2dsphere" } )
trucker.geoNear({loc: {type: "Point", coordinates: [job.lon,job.lat]}}, {spherical: true}, function(err, success) {
if(success){
res(success);
} else {
console.log('Response error '+err + job.lon +':' + job.lat);
}
});
Now, I'm wondering how do I use geoNear to return documents with latitude and longitude coordinates? Does latitude and longitude need to be stored in the database a specific way? I tried db.command() as well with geoNear and I always get db.command is not a function, same with trucker.command or trucker.db.db.command or trucker.db.command (anything with command to get data from the database which I think is because I'm using mongoose not mongoclient, because my database it set up with mlab). Nothing I've tried has worked which is why I'm now asking this question as I've tried just about every way imaginable to get results from my database using geoNear.
I got back at it this morning and did some more testing because it didn't make sense to me why I was not getting any results, so I created a brand new collection called location, and inserted 1 document to test and it worked, then 2 documents that look like the following and that worked too:
{
"_id": {
"$oid": "581cc2f430c34502e36eb148"
},
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
{
"_id": {
"$oid": "581cc2f530c34502e36eb158"
},
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
Apparently, location does have to be in the format,
location: [longitude, latitude]
or geoNear will not be able to find your document.
The really interesting finding to me, was that if you have a collection, with 10 documents, and there are documents in your collection that don't have:
location: [longitude, latitude]
geoNear will not get any results either. If even one document does not have that field, location: [longitude, latitude], geoNear will also not be able to find anything, or return any results after having done some more testing.
The following worked for me after creating those 2 new documents in a new collection for testing purposes using mongoose and mlab.
exports.geoNear = function (lon, lat ,res, next)
{
console.log("Inside geoNear");
var job = {};
job.lon = JSON.parse(lon);
job.lat = JSON.parse(lat);
console.log(job.lon);
console.log(job.lat);
locations.geoNear([job.lon, job.lat], {spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});
}
var AM = require('./modules/account-manager');
app.get('/geoFind', function(req, res) {
AM.geoNear('-73.99279', '40.719296', function(o){
res.send(200,o);
});
});
and I got the following result:
{
"waitedMS": 0,
"results": [
{
"dis": 1.4957325341976439e-7,
"obj": {
"_id": "581cc2f430c34502e36eb148",
"truckerID": "b233a9eaaedc63730e71a8b542606ee82e0aa5e5",
"name": "Justin",
"email": "Justin#gmail.com",
"company": "Justins Shipping",
"user": "justin1",
"pass": "D6Mvu6rUur758f37eac7010958c14557bb4df9871a",
"phone": "1234567890",
"location": [
-73.9928,
40.7193
]
}
},
{
"dis": 0.6482546796756842,
"obj": {
"_id": "581cc2f530c34502e36eb158",
"truckerID": "b233a9eaaedc63731e72a8b542606ee82e0aa7a6",
"name": "Alan",
"email": "Alan#gmail.com",
"company": "Alans Shipping",
"user": "alan1",
"pass": "D6Mvu6fUur758f37eac7010958c14557bb4df9872c",
"phone": "1234567890",
"location": [
-122.4194155,
37.7749295
]
}
}
],
"stats": {
"nscanned": 24,
"objectsLoaded": 2,
"avgDistance": 0.3241274146244688,
"maxDistance": 0.6482546796756842,
"time": 6
},
"ok": 1
}
I hope this answer helps someone else down the line who is also wondering why geoNear isn't returning any results.
EDIT: After doing even more research, it was actually because the collection needs to have a geospatial index created from the get-go. I tried adding the location field to all documents in the collection but still could not get any results back. Once I removed my collection entirely in mlab called "trucker" and re-added it, I was finally able to search that index and get results.
This line is needed initially:
trucker.createIndex( { location : "2dsphere" } )
Then you can use geoNear to find users near your location such as the following:
trucker.geoNear([job.lon, job.lat], {maxDistance:5000, distanceMultiplier: 6378137, spherical: true}, function(err, success){
if(success) {
res(success);
} else {
console.log('Response error' + err);
}
});

Resources