Mongoose Query with array of object - mongodb

I want to query using _id in array of object.
const members = [
{ _id: 60fb6f2859fd441f38e21172 },
{ _id: 60fb70a059fd441f38e21175 },
{ _id: 60fb6d9459fd441f38e2116c }
]
I know if it's an simple array, we can use $in operator. Please help how to query using above array.
const files = await File.find({ _id: { $in: members._id } });

Related

How to get a string in ObjectId in MongoDB v3.6?

I have an aggregation query whose MongoDB repsonse is :
_id: ObjectId('5e822d6c87502b3a9b751786')
I would like to get the string inside the ObjectId which is 5e822d6c87502b3a9b751786.
[ Problem ]
I have searched this question but so far there are only three operators that are capable to do this, namely $toString, $toObjectId, and $convert :
$project: {
_id: {
$toString: "$_id"
}
}
$project: {
_id: {
$toObjectId: "$_id"
}
}
$project: {
_id: {
$convert: {
input: "$_id"
to: "string"
}
}
}
MongoDB v3.6 does not support them if I am not mistaken.
Is there any workaround in MongoDB v3.6 to get a string inside an ObjectId?
Any help is much appreciated :)
For MongoDB v3.6, as $toString and $convert is not available until v4.0, you may need to resort to JS/application-level access to the _id.
db.testCollection.insertMany([
{
"_id": ObjectId("5e822d6c87502b3a9b751786")
}]);
db.testCollection.find().forEach( doc => {
// put your logic for process here
console.log(JSON.stringify(doc._id))
});
output:
"5e822d6c87502b3a9b751786"
Just want to add ray's answer after some findings.
Having defined a model in the app, here are some workaround that worked for me:
Promises
const TestCollection = require('../models/testCollection');
function getStringInObjectId() {
TestCollection.find().then(t => {
t.forEach(doc => {
// Put some logic here...
console.log('string in ObjectId :', JSON.stringify(doc._id));
});
});
}
Async/Await
const TestCollection = require('../models/testCollection');
async function getStringInObjectId() {
const t = await TestCollection.find();
t.forEach(doc => {
// Put some logic here...
console.log('string in ObjectId :', JSON.stringify(doc._id));
});
}
Same goes to aggregation :
Promises
const TestCollection = require('../models/testCollection');
function getStringInObjectId() {
TestCollection.aggregate([
{ $match: { 'name': 'marc' } }, // <- { 'name': 'marc' } is just an example, feel free to change it
// Put some stages here if required...
]).then(t => {
console.log('string in ObjectId : ', t[0]._id.toString());
});
}
Async/Await
const TestCollection = require('../models/testCollection');
async function getStringInObjectId() {
const t = await TestCollection.aggregate([
{ $match: { 'name': 'marc' } }, // <- { 'name': 'marc' } is just an example, feel free to change it
// Put some stages here if required...
]);
console.log('string in ObjectId : ', t[0]._id.toString());
}
Either JSON.stringify() or toString() can be used to convert it to string. Feel free to correct variable names.

Fetch Non Matching result from MonogoDB Collection

How I Can get the result set with IDs which are not there in MongoDB Collection.
Consider the below scenario :
I am passing 5 IDs Eg : [1,2,3,4,5] to check whether its exist or not in the collection.
out of 5, only 3 IDs are present [2,3,5]
then as a result, I should get [1,4]
Is there any way to achieve this with the help of aggregation or any other method in MongoDb.
I would personally just do it with some code, using distinct like so:
const ids = [1,2,3,4,5];
const existing = await db.collection.distinct('id', { id: { $in: ids } })
const output = ids.filter(i => !existing.includes(i))
You can also achieve this in a single call, but it has some overhead compared to this approach:
const ids = [1,2,3,4,5];
const output = db.collection.aggregate([
{
$match: {
id: { $in: ids },
},
},
{
$group: {
_id: null,
ids: { $addToSet: '$id' },
},
},
{
$project: {
ids: { $setDifference: [ids, '$ids'] },
},
},
]);

Update or append to a subcollection in mongo

I have a collection containing a subcollection. In one request, I would like to update a record in the subcollection or append to it if a match doesn't exist. For a bonus point I would also like this update to be a merge rather than an overwrite.
A crude example:
// Schema
{
subColl: [
{
name: String,
value: Number,
other: Number,
},
];
}
// Existing record
{
_id : 123,
subColl: [
{name: 'John',
value: 10,
other: 20}
]
}
// example
const update = { _id: 123, name: 'John', other: 1000 };
const { _id, name, other } = update;
const doc = await Schema.findById(_id);
const idx = doc.subColl.findIndex(({ name: nameInDoc }) => nameInDoc === name);
if (idx >= 0) {
doc.subColl[idx] = { ...doc.subColl[idx], other };
} else {
doc.subColl.push({ name, other });
}
doc.save();
Currently I can achieve this result by pulling the record, and doing the update/append manually but I am assuming that achieving it with a pure mongo query would be much faster.
I have tried:
Schema.findOneAndUpdate(
{ _id: 123, 'subColl.name': 'John' },
{ $set: { 'subColl.$': [{ name: 'John', other: 1000 }] } }
)
but this won't handle the append behaviour and also doesn't merge the object with the existing record, rather it overwrites it completely.
I am not sure is there any straight way to do this in single query,
Update with aggregation pipeline starting from MongoDB v4.2,
$cond to check name is in subColl array,
true condition, need to merge with existing object, $map to iterate loop, check condition if matches condition then merge new data object with current object using $mergeObjects
false condition, need to concat arrays, current subColl array and new object using $concatArrays
const _id = 123;
const update = { name: 'John', other: 1000 };
Schema.findOneAndUpdate(
{ _id: _id },
[{
$set: {
subColl: {
$cond: [
{ $in: [update.name, "$subColl.name"] },
{
$map: {
input: "$subColl",
in: {
$cond: [
{ $eq: ["$$this.name", update.name] },
{ $mergeObjects: ["$$this", update] },
"$$this"
]
}
}
},
{ $concatArrays: ["$subColl", [update]] }
]
}
}
}]
)
Playground

Return an array from Model.findOne?

I have a query which retuns an array of documents as a promise (.exec).
Further I have a (promised) function which also returns an array. Both are combined in a Promise.all.
Now I want to move the .find to a .findOne to speed up the filter. But with findOne I would get no array back. So I decided to use .find().limit(1)
My question is if this is a valid approach, instead of the use of .findOne and if not: Is there a simple way to return the result of findOne as an array? Maybe with .lean ?
var query = Model.find({ $and: [ { _id: id },
{ $or: [ { owner: userID },
{ isPublic: true } ]}
]}).limit(1);
Promise.all([query.exec(), this._sources(user)]).then((doc: Array<any>) => {....}
findOne returns an object, not an array. You can create an array with the result like this: return [obj]. I'm not sure but you can try this one
var queryResult = Model.findOne({ $and: [ { _id: id },
{ $or: [ { owner: userID },{ isPublic: true } ]}]})
.exec(function(err, data){
if(err) return [];
return [data];
});
Promise.all([queryResult, this._sources(user)]).then

How can I find all records where an id is in one array, or another id is in another array?

I need to perform a query that returns all results where an id, or array of ids in an array of ids AND another id, or array of ids, is in another array of ids. Perhaps an example will better explain what I'm trying to do:
Schema:
var somethingSchema = mongoose.Schema({
space_id : String,
title : String,
created : {
type: Date,
default: Date.now
},
visibility : {
groups : [{
type : String,
ref : 'Groups'
}],
users : [{
type : String,
ref : 'User'
}]
}
});
Query:
something.find({
space_id: req.user.space_id,
$and: [
{ $or: [{ "visibility.groups": { $in: groups } }] },
{ $or: [{ "visibility.users": { $in: users } }] }
]
}, function (err, data) {
return res.json(data);
});
In this example, both groups and users are arrays of ids. The query above isn't working. It always returns an empty array. What am I doing wrong?
You should be including all clauses to OR together in a single $or array:
something.find({
space_id: req.user.space_id,
$or: [{ "visibility.groups": { $in: groups } },
{ "visibility.users": { $in: users } }]
}, function (err, data) {
return res.json(data);
});
Which translates to: find all docs with a matching space_id AND that have a visibility.groups value in groups OR a visibility.users value in users.