mongoose return null in the query - node.js

my mongodb has a collection with data like this
{
"_id" : ObjectId("62ead2a8dd6922cfd6f466e4"),
"t" : "d",
"u" : {
"_id" : ObjectId("621d3469dd01e282b9a62321"),
"username" : "helxsz"
},
"users" : [
ObjectId("621d3469dd01e282b9a62321"),
ObjectId("628ee99ed0a58e00496a0730")
],
"createdAt" : ISODate("2022-08-03T19:55:20.965Z"),
"updatedAt" : ISODate("2022-08-03T19:55:20.965Z")
}
I am using node.js and mongoose to query the document.
let query = {
u:{
_id: "621d3469dd01e282b9a62321",
username: "helxsz"
},
t:'d',
};
collection
.findOne(query, 'u t ')
.exec(getResult);
why the executed query returns null to me

Maybe is because in your DB the u._id is ObjectId and in your query is a string. Mongoose should (?) parse it but I've faced not-parsed error like this many times.
So try parsing to ObjectId, in this example works.
Also an other problem is trying to search an object like this:
{
u:{
_id: "621d3469dd01e282b9a62321",
username: "helxsz"
}
}
Because in this way mongo looks for by the objects with the same order. You have to use dot notation
As an example, check how this query not work all times. To ensure the result you have to use dot notation:
let query = {
"u._id": "621d3469dd01e282b9a62321", // maybe casting to ObjectId is necessary
"u.username": "helxsz",
t: "d"
}
Example here
More info about Match an Embedded/Nested Document

Related

Invalid Regex Mongoose

I am new to MongoDB and I need to apply a regex to the find method using NodeJS Mongoose. The db contains elements such as:
{
"_id" : ObjectId("1234567899788675432454677"),
"nodes" : "[{\"name\":\"BINS\",\"oxygenation\":7.8,\"oxygenSaturation\":86,\"temperature\":13.3},{\"name\":\"CEST\",\"oxygenation\":4.6,\"oxygenSaturation\":52,\"temperature\":14.7},
"orp" : "0",
"chlorophyll" : "0",
"date" : "1615995776000",
"__v" : 0 }
and I need to go inside the nodes string to get all "name" values (in the example BINS and CEST). The regex that allows me to do it is
/(?<=name\\\"\:\\")\w+(?=\\)/gm
but I get errors when trying to use it both in mongo and Mongoose. For mongo I tried to escape all special characters:
db.my_collection.find( { nodes: { $regex: "\(\?<=\\\"name\\\": \\\"\)\\w\+\(\?=\\\\\)" } } )
but I get an empty result.
On Mongoose I tried several escaping such as the following, but I always get an error of invalid regex.
db.my_collection.find({ nodes : { $regex : '\(?<=name\\\\\\\"\\:\\\\\"\)\\w+\(?=\\\\\)' } }).
exec(function (err, waterSensorsReadings) { ... });

complicated mongoose pull list of data from api and insert into mongodb if it doesn't already exist

I am connecting to the Yelp API using the RapidAPI module in Nodejs. I am able to request a token, connect, and request data, retrieve that data, and insert the relevant information for each result it into mongodb. Here's where it gets complicated...
Let's say I make a Yelp API request and search for bars. I get a list of bars and insert them into the database. Let's say one of these in the list is "Joe's Bar & Grill". One of the fields in my mongodb is "type" and it's an array. So now, this particular document will look something like this:
{
id: 'joes-bar-and-grill',
name: 'Joe\'s Bar & Grill',
type: ['bar']
}
But then I run another request on the Yelp API on "restaurants", and in this list "Joe's Bar & Grill" shows up again. Instead of inserting a new duplicate document into mongodb, I'd like the existing document to end up looking like this:
{
id: 'joes-bar-and-grill',
name: 'Joe\'s Bar & Grill',
type: ['bar', 'restaurant']
}
In addition to this, let's say I run another request again for "bars", and "Joe's Bar & Grill" comes up again. I don't want it to automatically insert "bar" into the type array again, if "bar" already exists in its array.
I've tried findOneAndUpdate with upsert: true and a $push of new data into the array, but I cannot get it to work at all. Does anyone have any ideas?
You can use findOneAndUpdate, combined with $addToSet (to make sure that an entry in the array only exists once) and $each (to allow passing arrays to $addToSet):
Bar.findOneAndUpdate({ id : 'joes-bar-and-grill' }, {
id : 'joes-bar-and-grill',
name : 'Joe\'s Bar & Grill',
$addToSet : { type : { $each : [ 'restaurant' ] } }
}, { upsert : true })
EDIT: now that you posted your entire code, the problem becomes more obvious.
For one, I'm not sure if the third and fourth arguments that you're passing to Location.update() make sense. As far as I know, the third should be an option object, and the fourth an async function.
Secondly, it looks like you're just ignoring any update errors.
And lastly, this isn't going to work:
for (var i = 0; i < payload.businesses.length; i++) { Location.update(...) }
Because Location.update() is asynchronous, the i variable will get clobbered (you should browse around on SO to find the explanation for that; for example, see this question).
You're going to need a library that will provide you with better async support, and preferably one that will also help limiting the number of update queries.
Once such library is async, and using it, your code would become something like this:
const async = require('async');
...
async.eachLimit(payload.businesses, 5, function(business, callback) {
Location.update({ yelpID : business.id }, {
name : business.name,
latitude : business.location.latitude,
longitude : business.location.longitude,
address1 : business.location.address1,
address2 : business.location.address2,
address3 : business.location.address3,
city : business.location.city,
state : business.location.state,
zip_code : business.location.zip_code,
country : business.location.country,
timezone : 'CST'
$addToSet : { type : 'bar' }
}, { upsert : true }, callback);
}, function(err) {
if (err) {
console.error(err);
} else {
console.log('All documents inserted');
}
});
You may use $addToSet operator
The $addToSet operator adds a value to an array unless the value is
already present, in which case $addToSet does nothing to that array.
$addToSet only ensures that there are no duplicate items added to the
set and does not affect existing duplicate elements. $addToSet does
not guarantee a particular ordering of elements in the modified set.
If the field is absent in the document to update, $addToSet creates
the array field with the specified value as its element.
If the field is not an array, the operation will fail.
The below solution assumes that on each update, you receive a single type and not an array. If the input document is an array itself, you may use robertklep's solution with $each operator
db.mycoll.update(
{ "id" : "joes-bar-and-grill" },
{
$set:{
name : 'Joe\'s Bar & Grill',
},
$addToSet : { type : 'restaurant' }
},
true, false);
I have also used $set operator.
The $set operator replaces the value of a field with the specified
value.
The $set operator expression has the following form:
{ $set: { field1: value1, ... } }
Here is the mongo shell output to explain it further :
> db.mycoll.find({ "id" : "joes-bar-and-grill" });
// NO RESULT
> db.mycoll.update(
... { "id" : "joes-bar-and-grill" },
... {
... $set:{
... name : 'Joe\'s Bar & Grill',
... },
... $addToSet : { type : 'restaurant' }
... },
... true, false);
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("58e719b4d543c5e30d615d59")
})
// INSERTED A NEW DOCUMENT AS IT DOES NOT EXIST
> db.mycoll.find({ "id" : "joes-bar-and-grill" }); // FINDING THE OBJECT
{ "_id" : ObjectId("58e719b4d543c5e30d615d59"), "id" : "joes-bar-and-grill", "name" : "Joe's Bar & Grill", "type" : [ "restaurant" ] }
> db.mycoll.update(
... { "id" : "joes-bar-and-grill" },
... {
... $set:{
... name : 'Joe\'s Bar & Grill',
... },
... $addToSet : { type : 'bar' }
... },
... true, false);
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// UPDATING THE DOCUMENT WITH NEW TYPE : "bar"
> db.mycoll.findOne({ "id" : "joes-bar-and-grill" });
{
"_id" : ObjectId("58e719b4d543c5e30d615d59"),
"id" : "joes-bar-and-grill",
"name" : "Joe's Bar & Grill",
"type" : [
"restaurant",
"bar"
]
}

MongoDB full text search on string array

So I'm using Node.js with MongoDB for my web application. I'm having some trouble creating a text index for my schema and searching for text within an array. I've looked at the mongo docs but haven't found anything related to this specifically.
My current implementation searches successfully on regular String values, but querying for text matching in [String]'s don't return anything.
Here's my REST call:
...console.log("Query string: " + str);
var qry = {
"$text": {
"$search": str
}
};
model.find(qry, function (err, results) {...
And when I create my schema:
var blah = new Schema({
foo : String,
bar : [String],
...
blah.index({
foo: 'text',
bar: 'text'
});
Any query won't return the results that match in bar. A query string for something within foo works fine.
Double check that you've created the correct indexes on the correct collections and the queries are being issued to the correct collections. Indexing an array works for me:
> db.test.drop()
> db.test.insert({ "_id" : 0, "a" : "dogs are good" })
> db.test.insert({ "_id" : 1, "a" : "I like dogs", "b" : ["where's my dog?", "here, have a cat"] })
> db.test.insert({ "_id" : 2, "b" : ["she borrowed my dog", "my frogs are croaking"] })
> db.test.ensureIndex({ "a" : "text", "b" : "text" })
> db.test.find({ "$text" : { "$search" : "dogs" } }, { "_id" : 1 })
{ "_id" : 0 }
{ "_id" : 2 }
{ "_id" : 1 }
Okay, I finally figured it out! Turns out, grunt serve doesn't update indexes in the database. I had created a text index for "foo" only and that didn't update when I added "bar" to the index. I had to run - in mongo shell:
db.dropDatabase()
The next time I ran it, the database was recreated and the proper indexes were set. If anyone else runs across this issue, try running db.getIndexes().

How to query parent based on subdocument's _id?

consider the following records:
user record
{
"_id" : ObjectId("5234ccb7687ea597eabee677"),
"class" : [
{ "_id" : ObjectId("5234ccb7687ea597eabee671", "num" : 10, "color" : "blue" },
{ "_id" : ObjectId("5234ccb7687ea597eabee672", "num" : 100, "color" : "blue" }
]
}
this user has two class sub records, now I need a query that finds all users that have class property where "class._id" has a value of at least one users "class._id"
here is a more detail example:
suppose there is four user:
A:{_id:432645624232345,class:[{_id:123,name:'foo'}]}
B:{_id:432645624232555,class:[{_id:555,name:'foo'},{_id:123,name:'foo'}]}
C:{_id:432645344232345,class:[{_id:555,name:'foo'},{_id:111,name:'www'}]}
D:{_id:432644444232345,class:[{_id:222,name:'sss'},{_id:555,name:'www'},{_id:123,name:'foo'}]}
now if B login , I need to query all the user whose class subdocument contains at least one document which's _id==555 or _id==123 (555 and 123 come from B user), in this case the query result should be:
A:{_id:432645624232345,class:[{_id:123,name:'foo'}]} // match _id=123
B:{_id:432645624232555,class:[{_id:555,name:'foo'},{_id:123,name:'foo'}]} //match _id=123 and _id=555
C:{_id:432645344232345,class:[{_id:555,name:'foo'},{_id:111,name:'www'}]} //match _id=555
D:{_id:432644444232345,class:[{_id:222,name:'sss'},{_id:555,name:'www'},{_id:123,name:'foo'}]} ///match _id=123 and _id=555
which is all the user.
so far i get this:
{"class._id" : { $in : ["5234ccb7687ea597eabee671", "5234ccb7687ea597eabee672"] } }
but when different user login the class._id query condition is different. So is there any operator to do this
{"class._id" : { $in : req.user.class } }
hope I made myself clear.
In order to achieve what you want, first you must isolate the class _ids in an array, and then use it in the query argument.
var classIds = [];
var i = 0;
while (i < req.user.class.length) {
classIds.push(req.user.class[i]._id);
i++;
}
After that you can use classIds array in the query:
{"class._id" : { $in : classIds } }
The following query condition would give you all the users that have at least one class with id equal to any of the elements in the given array:
{"class._id" : { $in : ["5234ccb7687ea597eabee671", "5234ccb7687ea597eabee672"] } }
In the array for the $in clause you may provide any id's you needed , comma separated.
In addition, if you needed such, the below query condition should check for existence of nested document within "class" property that has a property "_id" :
{ "class._id" : { $exists : true } }
Both conditions should work no matter if "class._id" is a single-valued property or an array (mongo supports that).

Learning Map-Reduce in MongoDB

While searching through the internet, I found out that joins can be emulated in mongodb through the map-reduce function. Going through the docs was confusing.
I have two collections: one with a list of friends of one user. And the other collection is of all the users. I want to fetch the profile pictures of all the friends. how do I create a mongodb query to get the desired results?
The USERS collection:
{
"_id" : ObjectId("524c194a6e3715ce0a000001"),
"email" : "qwerty#abc.com",
"password" : "",
"phone" : "",
"salt" : "",
"upic" : "someuser2fd2751259bb7519d7b760ffee9b7fce203ad1f34.jpg",
"username" : "someuser2"
}
{
"_id" : ObjectId("524be475fafb35480a000001"),
"email" : "",
"password" : "",
"phone" : "",
"salt" : "",
"upic" : "amitverma2522b7a52e054c350f78fd7f3558919f2e2dab58.jpg",
"username" : "amitverma"
}
The friends of each user collection:
{
"_id" : ObjectId("526547ed2389630000000001"),
"friends" : [
{
"_id" : ObjectId("524be475fafb35480a000001"),
"username" : "amitverma"
},
{
"_id" : ObjectId("524be475fafb35480a000001"),
"username" : "someuser2"
}
],
"upic" : "macbookfd2751259bb7519d7b760ffee9b7fce203ad1f34.jpg",
"username" : "someuser"
}
Help would be appreciated.
There are no official docs for this as it's not a recommended best practice. It's complex that you need to do multiple passes carefully outputting the same results into the same collection.
You'd be better served by gathering the list of friends and using the $in operator (reference) to fetch the users and projecting the results to only include the fields you require (like the image).
Ideally, you'd cache those results locally to avoid needlessly requesting image paths. Following is untested code that should work in the shell:
db.friends.find({ username: 'someuser'}).forEach(friend_list) {
// this would gather the list of friend's _ids
// the _id will be passed as an array for the $in operator
var friends = friend_list.friends.map(function(friend) { return this._id; });
// gather up the images for each of the friends
var upics = db.users.find({_id : { $in : friends }},
{ _id: 1, upic: 1 }).toArray();
// now, do something with upics -- outside of the MongoDB shell, this will
// return asynchronously ....
});

Resources