Populate Search MongoDB Objects - mongodb

Can you please tell me how to search for a nested object (only _id appears in it and is expanded via .populate() )?
I need to search by title and album.title, how can I do this?
async getAll(searchTerm?: string) {
let options = {}
if (searchTerm) {
options = {
$or: [
{
title: new RegExp(searchTerm.trim(), 'gi'),
},
],
}
}
return this.TrackModel.find(options)
.select('-updatedAt -__v')
.sort({ createdAt: 'desc' })
.populate('album author')
.exec()
}
image of JSON below
[
{
"_id": "638cec330c055283f3eeb227",
"poster": "/uploads/tracks/Coolio/Coolio-cover.jpg",
"title": "Gangsta's Paradise",
"slug": "gangstasparadise",
"duration": 240,
"countPlays": 7269322,
"trackUrl": "/uploads/tracks/Coolio/Coolio - Gangstas Paradise.mp3",
"album": [
{
"_id": "638ceb960c055283f3eeb225",
"title": "Gangsta's Paradise",
"slug": "gangstasparadise",
"poster": "/uploads/tracks/Coolio/Coolio-cover.jpg",
"author": [
"638ce9f50c055283f3eeb223"
],
"createdAt": "2022-12-04T18:48:54.306Z",
"updatedAt": "2022-12-04T18:48:54.306Z",
"_v": 0
}
]
}
]
I tried to search using $or, but I ran into a problem that at the time of the search, only _id is stored in the album array.

Related

How can I filter records with multiple $in condition (some are optional $in) with arrays of string? Mongoose/MongoDB

You can see my Mongodb Records at last... I am now trying to implement search functionality,
I mad checkbox filtration for my project and below I listed those arrays after I clicked multiple checkboxes (see 1, 2 and 3).
I tried in aggregate with multiple match queries with $in, but it doesn't worked. Below arrays are used to check the records.
for example:
["Restaurant", "Mall"] need to check with "commercialType" in records, at the same time ["AC Rooms", "3 Phase Electricity"] need to check with "propertyFeatures.name" in records.. so all matching records must display if records exist with those filtrations.
I tried with multiple $in queries like this, but it gives empty records.
"$match": {
"commercialType": {
"$in": ["Restaurant", "Hotel"]
},
{
"propertyFeatures.name": {
"$in": ['AC Rooms']
}
},
... other match filters
}
1. Below Array is used to find commercialType (field in doc)
[
'Restaurant',
'Office space',
'Hotel'
]
2. Below Array is used to find landType (field in doc)
[
'Bare land',
'Beachfront land',
'Coconut land'
]
3. Below Array is used to find "propertyFeatures.name" (field in doc)
[
'AC Rooms',
'3 Phase Electricity',
'Hot Water'
]
[
{
"_id": {
"$oid": "6343b68edf5e889a575c8502"
},
"propertyType": "House",
"propertyFeatures": [
{
"id": 1,
"name": "AC Rooms",
"value": true
}
]
},
{
"_id": {
"$oid": "6343b68edf5e889a575c8502"
},
"propertyType": "Land",
"landType": "Bare land",
"propertyFeatures": [
{
"id": 1,
"name": "Wider Road",
"value": true
}
]
},
{
"_id": {
"$oid": "6343b68edf5e889a575c8502"
},
"propertyType": "Commercial",
"commercialType": "Restaurant",
"propertyFeatures": [
{
"id": 1,
"name": "3 Phase Electricity",
"value": true
}
]
}
]
You are probably missing $or operator, so your example pipeline becomes
[
{"$match": {
"$or": [
{
"commercialType": {
"$in": ["Restaurant", "Hotel"]
},
{
"propertyFeatures.name": {
"$in": ['AC Rooms']
}
}
]
}
]
MongoDB docs: https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/#error-handling

How to save deletion in a deeply nested MongoDB document

I am new to MongoDB and I am using MongoDB shell to perform the operations.
I am working to remove the array named Process from all the Items, but it seems that I do not grasp the remove concept correctly.
The documents we use are deeply nested - we do not know how many items there are, or how deep the level of nesting.
What I tried so far is to use recursion to iterate through the items:
function removeAllProcessFields(docItems)
{
if(Array.isArray(docItems))
{
docItems.forEach(function(item)
{
print("idItem: "+item._id);
if(item.Process == null)
{
print("Process null");
}
else
{
$unset: { Process: ""}
}
removeAllProcessFields(item.Items);
})
}
}
var docs = db.getCollection('MyCollection').find({})
docs.forEach(function(doc)
{
print("idDoc: "+doc._id);
removeAllProcessFields(doc.Items);
})
But I have difficulties on using unset properly to save the operation.
An example document would be:
{
"_id": "622226d319517e83e8ed6151",
"Name": "test1",
"Description": "",
"Items": [{
"_id": "622226d319517e83e8ed614e",
"Name": "test-item",
"Description": "",
"Process": [{
"Name": "Step1"
}, {
"Name": "Step2"
}],
"Items": [{
"_id": "622226d319517e83e8ed614f",
"Name": "test-subItem1",
"Description": "",
"Process": [{
"Name": "StepSub1"
}, {
"Name": "StepSub2"
}, {
"Name": "StepSub3"
}],
"Items": []
},
{
"_id": "622226d319517e83e8ed6150",
"Name": "test-subItem2",
"Description": "",
"Process": [{
"Name": "StepSub4"
}, {
"Name": "StepSub5"
}, {
"Name": "StepSub6"
}],
"Items": []
}
]
}]
}
What I hope to achieve would be:
{
"_id": "622226d319517e83e8ed6151",
"Name": "test1",
"Description": "",
"Items": [{
"_id": "622226d319517e83e8ed614e",
"Name": "test-item",
"Description": "",
"Items": [{
"_id": "622226d319517e83e8ed614f",
"Name": "test-subItem1",
"Description": "",
"Items": []
},
{
"_id": "622226d319517e83e8ed6150",
"Name": "test-subItem2",
"Description": "",
"Items": []
}
]
}]
}
Something like this maybe using the $[] positional operator:
db.collection.update({},
{
$unset: {
"Items.$[].Items.$[].Process": 1,
"Items.$[].Process": 1
}
})
You just need to construct it in the recursion ...
playground
JavaScript recursive function example:
mongos> db.rec.find()
{ "_id" : ObjectId("622a6c46ae295edb276df8e2"), "Items" : [ { "a" : 1 }, { "Items" : [ { "Items" : [ { "Items" : [ ], "Process" : [ 1, 2, 3 ] } ], "Process" : [ 4, 5, 6 ] } ], "Process" : [ ] } ] }
mongos> db.rec.find().forEach(function(obj){ var id=obj._id,ar=[],z=""; function x(obj){ if(typeof obj.Items != "undefined" ){ obj.Items.forEach(function(k){ if( typeof k.Process !="undefined" ){ z=z+".Items.$[]";ar.push(z.substring(1)+".Process") }; if(typeof k.Items != "undefined"){x(k)}else{} }) }else{} };x(obj);ar.forEach(function(del){print( "db.collection.update({_id:ObjectId('"+id+"')},{$unset:{'"+del+"':1}})" );}) })
db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Process':1}})
db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Process':1}})
db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Items.$[].Process':1}})
mongos>
Explained:
Loop over all documents in collection with forEach
Define recursive function x that will loop over any number of nested Items and identify if there is Process field and push to array ar
Finally loop over array ar and construct the update $unset query , in the example only printed for safety , but you can improve generating single query per document and executing unset query ...
Assuming you are on v>=4.4 you can use the "merge onto self" feature of $merge plus defining a recursive function to sweep through the collection and surgically remove one or a list of fields at any level of the hierarchy. The same sort of needs arise when processing json-schema data which is also arbitrarily hierarchical.
The solution below has extra logic to "mark" documents that had any modifications so the others can be removed from the update set passed to $merge. It also can be further refined to reduce some variables; it was edited down from a more general solution that had to examine keys and values.
db.foo.aggregate([
{$replaceRoot: {newRoot: {$function: {
body: function(obj, target) {
var didSomething = false;
var process = function(holder, spot, value) {
// test FIRST since [] instanceof Object is true!
if(Array.isArray(value)) {
for(var jj = value.length - 1; jj >= 0; jj--) {
process(value, jj, value[jj]);
}
} else if(value instanceof Object) {
walkObj(value);
}
};
var walkObj = function(obj) {
Object.keys(obj).forEach(function(k) {
if(target.indexOf(k) > -1) {
delete obj[k];
didSomething = true;
} else {
process(obj, k, obj[k]);
}
});
}
// ENTRY POINT:
if(!Array.isArray(target)) {
target = [ target ]; // if not array, make it an array
}
walkObj(obj);
if(!didSomething) {
obj['__didNothing'] = true;
}
return obj;
},
// Invoke!
// You can delete multiple fields with an array, e.g.:
// ..., ['Process','Description']
args: [ "$$ROOT", 'Process' ],
lang: "js"
}}
}}
// Only let thru docs WITHOUT the marker:
,{$match: {'__didNothing':{$exists:false}} }
,{$merge: {
into: "foo",
on: [ "_id" ],
whenMatched: "merge",
whenNotMatched: "fail"
}}
]);

MongoDB: find if ID exist in array of objects

I was wondering, is there a way in MongoDB to get all the documents from one collection excluding those that exists in another, without using aggregation.
Let me explain myself more;
I have a collection of "Users", and another collection where there is an array of objects.
Something like this:
User
}
"_id": "61e6bbe49d7efc57f895ab50",
"name": "User 1"
},
{
"_id": "61e6b9239d7efc57f895ab02",
"name": "User 2"
},
{
"_id": "61cae6176d0d9a36efd8f190",
"name": "User 3"
},
{
"_id": "61cade886d0d9a36efd8f11a",
"name": "User 4"
},
The other collection looks like this:
{
users: [
{
user: {
"_id": "61e6b9239d7efc57f895ab02",
"name": "User 2",
},
...
},
{
user: {
"_id": "61cae6176d0d9a36efd8f190",
"name": "User 3",
},
...
},
],
},
I would like to get all the users in "array 1" excluding those in "array 2".
So the result should be:
[
{
"_id": "61e6b9239d7efc57f895ab02",
"name": "User 1"
},
{
"_id": "61cae6176d0d9a36efd8f190",
"name": "User 4"
},
],
So, is there a way to do that without the need to do aggregation.
you can use the $and like this:
const found = await User.find({
$and: [
{ _id: { $in: array1 } },
{ _id: { $nin: array2 } }
]
});
For anyone looking for a solution, if you want to add new element to an array of objects, where you, somehow, forgot to initialize the _id field, remember you have to, because mongo adds that field automatically (to use it for filtering/indexation).
All you have to do, is to write something like this;
const data = Data.create({
_id: mongoose.Types.ObjectId.createFromHexString(uid),
users: [{ user: userId, initiator: true, _id: userId}],
})
and for the searching, you write what you always write;
const found = await User.find({ _id: { $nin: data.users } });

How to use aggregation to pull information from a nested array on objects in mongoDB

I am trying to get the hang of the aggregation in mongoDB, I am new at this and I think I kinda got lost.
I have a collection of users, each user has an array of social networks, and each item in this array is an object.
How do I get value only from a given social network/s object/s.
{
"user":{
"_id": "d10430c8-e59",
"username": "John",
"password": "f7wei93",
"location": "UK",
"gender": "Male",
"age": 26,
"socials": [
{
"type": "instagram",
"maleFollowers": 23000,
"femaleFollowers": 65000,
"posts": 5400,
"avgFollowerAge": 22
},
{
"type": "facebook",
"maleFollowers": 4000,
"femaleFollowers": 6700,
"posts": 330,
"avgFollowerAge": 25
},
{
"type": "snapchat",
"maleFollowers": 873,
"femaleFollowers": 1200,
"posts": 1200,
"avgFollowerAge": 21
},
]
}
}
I want to get the totalFollowersCount ( maleFollowers + femaleFollowers)
for each social network type that is given in an array.
for example ["instagram", "snapchat"] will only give me the totalFollowersCount for this two specific networks.
I started playing around with aggregation but didn't figure it out all the way.
First, we need to flatten the socials array. Then, we group by socials type + 'user' fields and sum( maleFollowers + femaleFollowers).
With $match operator, we filter desired social networks.
db.users.aggregate([
{
$unwind: "$user.socials"
},
{
$group: {
_id: {
user: "$user._id",
type: "$user.socials.type"
},
"totalFollowersCount": {
$sum: {
$add: [
"$user.socials.femaleFollowers",
"$user.socials.maleFollowers"
]
}
}
}
},
{
$match: {
"_id.type": {
$in: [
"instagram",
"snapchat"
]
}
}
}
])
MongoPlayground

search query on array elements in mongodb

I am new to mongodb and still learning it so my question can be naive so please bear with it :)
I have only one json object in mongodb which looks like this.
json object
{
"URLStore": [
{
"description": "adf description",
"url": "www.adf.com"
},
{
"description": "pqr description",
"url": "www.pqr.com"
},
{
"description": "adf description",
"url": "www.adf.com"
}
]
}
I need to query description for url which matches given input. e.g here www.adf.com . I have a code which queries mongodb
mongodb query
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("URLStore.url","www.pqr.com");
BasicDBObject fields=new BasicDBObject("URLStore.description", "");
cursor = collection.find(whereQuery,fields);
but the result is something like
{
"_id": {
"$oid": "554b4046e4b072dd9deaf277"
},
"URLStore": [
{
"description": "pqr description"
},
{
"description": "adf description"
},
{
"description": "adf description"
}
]
}
Actually only 1 description should have returned as matching objects with key www.pqr.com is only one. What is wrong with my query? m I missing something here ?
I have already tried question Retrieve only the queried element in an object array in MongoDB collection but using solution mentioned there will return only one object / first match
Use the following aggregation pipeline, should give you the desired results:
db.collection.aggregate([
{
"$match": {
"URLStore.url": "www.adf.com"
}
},
{
"$unwind": "$URLStore"
},
{
"$match": {
"URLStore.url": "www.adf.com"
}
},
{
"$group": {
"_id": {
"url": "$URLStore.url",
"description": "$URLStore.description"
}
}
},
{
"$project": {
"_id": 0,
"description": "$_id.description"
}
}
])