Mongoose: search for ObjectID by Array - mongodb

I want to filter my collection by aggregation for one of many ObjectIDs.
Because of some DocumentDB restrictions I can not build a single pipeline with uncorrelated subqueries. So my fix is to do it in two queries.
for example: I have an aggregation that returns all teamIds, for some conditions as an array of Object with the IDs.
[{_id: ObjectID("abcdef")}, {_id: ObjectID("ghijkl")}, {_id: ObjectID("vwxyz")}, ...]
I now want to have a second aggregation filter another collection using the ObjectIDs.
This would work in Mongo Compass:
{
"team": {
"$in": [ObjectId("60aabcb05c7462f42b3d7zyx"), ObjectId("60aabc7b05c7462f42b3dxyz")]
},
....
}
My issue is that i can not find the correct syntax for JS to generate such a pipeline.
What ever I try, JS always converts my Array of ObjectIDs to something like this:
{
"team": {
"$in": [{
"_id": "60aabcb05c7462f42b3d7zyx"
},{
"_id": "60aabc7b05c7462f42b3dxyz"
}]
},

I fixed it like this. I am not 100% why this syntax works because it is still just an array of objects, formatted like before, but I guess there is some stuff mongoose does, that is opaque to me.
let teams = await TeamMgmt.getTeamsAggregatedByFilter( teamFilter )
// make an array of ObjectIds so we can filter for them.
let idArray = []
Object.keys( teams ).map( function ( key, index ) {
idArray.push( new mongoose.Types.ObjectId( teams[ index ]._id.toString() ) )
} );
const shiftFilter = [
{
'$match': {
'team': {
"$in": idArray
},
....
}

Related

Display object list with a parameter on Mongoose

I have a find query that returns me a list of objects:
{
"_id": "5fb94fda487b9348c4291450",
"name": [
{
"NewConfirmed": 642686,
"TotalConfirmed": 49315431,
"NewDeaths": 9555,
"TotalDeaths": 1242785,
"NewRecovered": 288131,
"TotalRecovered": 32473892
},
{
"NewConfirmed": 116262,
"TotalConfirmed": 6014461,
"NewDeaths": 4640,
"TotalDeaths": 371913,
"NewRecovered": 77575,
"TotalRecovered": 2492884
},
{
...
Its all fine but I'm trying to make a new query with a status parameter with the value NewConfirmed or TotalConfirmed or NewDeaths to display only that specific field. So the endpoints would look like /something/status/:status.
I already tried an aggregation with filter and a simple find but still havent figured nothing out.
Anyone has any idea?
First of all, you need a query with this estructure:
db.collection.aggregate([
{
/**Your match object*/
},
{
"$project": {
"YourStatus": {
"$first": "$name.YourStatus"
}
}
}
])
Example here.
Using mongoose you need to create the object query in this way:
var query = {}
query[status] = {"$first": "$name."+status}
And do the mongoose query replacing the object by query object.
var aggregate = await model.aggregate([
{
//Your $match stage here
},
{
"$project": query
}
])
Also, I've tested in local but my mongo version (I think) doesn't recognize $first so I've used $arrayElemAt. According to mongo docs is the same as $first.
var status = "NewConfirmed"
var query = {}
query[status] = { $arrayElemAt: ["$name."+status, 0]}
Also you can add _id: 0 into $project aggregate to not return this field.
var query = {_id:0} //Here add _id: 0 to project object
query[status] = { $arrayElemAt: ["$name."+status, 0]} //And the rest of the stage

How to deselect one object in array MongoDB? [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 3 years ago.
In my situation I just need my result but without my objectID in my array.
This is my method :
return Room.findOne(
{
_id: idRoom,
participants: {$elemMatch: {$ne: this.currentUser.profile}},
},
{
'participants.$': 1,
}
)
With elementMatch, the problem is when you found the object, only the first object is returned.
This is my result :
"result": {
"_id": "5da5e77f51e08708b79565e8",
"participants": [
"5da4d5b40cc94f04a7aaad40"
],
And this is the real result I need
"result": {
"_id": "5da5e77f51e08708b79565e8",
"participants": [
"5da4d5b40cc94f04a7aaad40"
"fwnert9248yrhnqwid13982r" // I have another participants
],
And my model is just like this :
const RoomSchema = new Schema({
participants: [{type: Schema.Types.ObjectId,ref: 'Profile'}],
...
}, options)
For others reasons, I can't use aggregate, thank you if you have the solution
So I think you are trying to shape a resultset in mongo with the findOne() method, and any use of the aggregation pipeline framework is out of the question and unavailable for other reasons.
I am not sure this is possible. I believe you will need to perform multiple steps to achieve your desired results. If you can use aggregation pipeline framework here is a pipeline to suit the desired results (I believe)...
db.Room.aggregate(
[
{
"$match": { _id: ObjectId(idRoom)}
},
{
$project: {
"participants": {
$filter: {
input: "$participants",
as: "participant",
cond: {$ne: ["$$participant", this.currentUser.profile] }
}
}
}
}
]
)
...but if you cannot use aggregation pipeline then here is a mongoshell script that accomplishes the task in several steps. The strategy is to capture the whole document by _id then remove the data element from the array then echo the results...
var document = db.Room.findOne( { _id: ObjectId("5da64a62cd63abf99d11f210") } );
document.participants.splice(document.participants.indexOf("5da4d5b40cc94f04a7aaad40"), 1);
document;

How to query nested arrays in mongoDB to find documents with elements that don't exist

I'm trying to make an aggregation query to find all documents that do not contain a certain element. It needs to be an aggregation because I want to be able to edit the returned documents. Eg. I only want to return some fields and I also want to be able to do a group on eg. the "producer" element.
I already tried practically all I can think of. I tried unwinding the arrays, but then I created even more documents where the element packagingInformation was mission than originally. I tried using $ne, $eq, $gt, $lte,.. to find the documents needed,... but they always return all documents because of the nested array structure.
$ArrayToObject didn't do the trick either for me.
I'm clueless on how to achieve this. The tripple nested array structure beats my imagination.
The only thing that returns me the wanted result is the following query:
db.product.find({
"json.productData.productInformation.details.packagingInformation": { $exists: false }
})
But this doesn't suffice since it's not an aggregate, thus it doens't allow me to continue to do queries with the results. And the $exists doesn't work in aggregates.
This is the JSON structure which I'm struggling with (dummy data).
{
_id: 5ckflsmdk543klmf543klmtrkmgdfm,
productNumber: 001,
json: {
productData: {
productNumber: 001,
producer: coca-cola,
productInformation: [
{
trackingInformation: {
lastUpdate: 01-01-12,
creationDate: 01-01-11
},
details: [
packagingInformation: [
quantity: 5,
size: 20cm
],
productType: drinks,
otherMeaningLessInformation: whatever,
andEvenMoreInformationInArrays: [
andTheInformationGoesOn: wow,
andOn: nastyArrayStructures
]
]
]
}
}
}
}
The wanted result would be to return all the documents that do not contain the packagingInformation array or the packagingInformation.quantity element.
or even better, to return all documents but with an extra field:
containsPackagingInformation: true/false. With false being the result of all documents that do not contain packagingInformation or packagingInformation.quantity.
$exists DOES WORK in a aggregation.
$exists works the same way it works in .find
you can form a query like:
db.collection.aggregate({ $match: {
$or: [
{
"json.productData.productInformation.details.packagingInformation.quantity": {
$exists: false
}
},
{
"json.productData.productInformation.details.packagingInformation": {
$exists: false
}
}
] } })
Try this query here with dummy data

MongoDB $pull syntax

I'm having a (hopefully) small syntax problem with $pull in Mongodb.
bulk.find({_id: new mongo.ObjectID(req.session._id)}).updateOne({$pull: {
firstArray: {id: req.params.id},
'secondArray.firstArrayIds': req.params.id
'secondArray.$.firstArrayIds': req.params.id
}});
The firstArray $pull works just fine.
But the secondArray.firstArrayIds and/or secondArray.$.firstArrayIds does not. What am I doing wrong here?
This is my data structure:
clients: {
firstArray: [
{
_id: '153'.
someField1: 'someVal1',
}
...
]
secondArray: [
{
_id: '7423'.
someField1: 'someVal1',
firstArrayIds: [153, 154, 155, ...]
}
...
]
}
EDIT What if there are more than one embedded object in secondArray which firstArrayIds can contain the id i want to delete. In other words, when deleting an object in firstdArray i want to delete references in all secondArray's firstArrayIds Not just the first match.
Your "secondArray" has a nested element structure, so you must identify the outer element you want to match in your query when using a positional $ operator in the update. You basically need something like this:
bulk.find({
"_id": new mongo.ObjectID(req.session._id),
"secondArray._id": "7423"
}).update({
"$pull": {
"firstArray": { "_id": "153" },
"secondArray.$.firstArrayIds": 153
}
});
So there are in fact "two" id values you need to pass in with your request in addition to the general document id. Even though this is nested it is okay since you are only matching on the "outer" level and only on one array. If you tried to match the position on more than one array then this is not possible with the positional operator.

MongoDB query to find property of first element of array

I have the following data in MongoDB (simplified for what is necessary to my question).
{
_id: 0,
actions: [
{
type: "insert",
data: "abc, quite possibly very very large"
}
]
}
{
_id: 1,
actions: [
{
type: "update",
data: "def"
},{
type: "delete",
data: "ghi"
}
]
}
What I would like is to find the first action type for each document, e.g.
{_id:0, first_action_type:"insert"}
{_id:1, first_action_type:"update"}
(It's fine if the data structured differently, but I need those values present, somehow.)
EDIT: I've tried db.collection.find({}, {'actions.action_type':1}), but obviously that returns all elements of the actions array.
NoSQL is quite new to me. Before, I would have stored all this in two tables in a relational database and done something like SELECT id, (SELECT type FROM action WHERE document_id = d.id ORDER BY seq LIMIT 1) action_type FROM document d.
You can use $slice operator in projection. (but for what you do i am not sure that the order of the array remain the same when you update it. Just to keep in mind))
db.collection.find({},{'actions':{$slice:1},'actions.type':1})
You can also use the Aggregation Pipeline introduced in version 2.2:
db.collection.aggregate([
{ $unwind: '$actions' },
{ $group: { _id: "$_id", first_action_type: { $first: "$actions.type" } } }
])
Using the $arrayElemAt operator is actually the most elegant way, although the syntax may be unintuitive:
db.collection.aggregate([
{ $project: {first_action_type: {$arrayElemAt: ["$actions.type", 0]}
])