How to find a document by its position/index in the array? - node.js

I need to retrieve let's say the documents at position 1,5 and 8 in a MongoDB database using Mongoose.
Is it possible at all to get a document by its position in a collection? If so, could you show how to do that?
I need something like this:
var someDocs = MyModel.find({<collectionIndex>: [1, 5, 8]}, function(err, docs) {
//do something with the three documents
})
I tried to do the following to see what indexes are used in collection but I get the 'getIndexes is not a function' error:
var indexes = MyModel.getIndexes();
Appreciate any help.

If by position 5 for example, you mean the literal 5th element in your collection, that isn't the best way to go. A Mongo collection is usually in the order in which you inserted the elements, but it may not always be. See the answer here and check the docs on natural order: https://stackoverflow.com/a/33018164/7531267.
In your case, you might have a unique id on each record that you can query by.
Assuming the [1, 5, 8] you mentioned are the ids, something like this should do it:
var someDocs = MyModel.find({ $or: [{ id: 1 }, { id: 5 }, { id: 8 }]}}, function(err, cb) {
//do something with the three documents
})
You can also read about $in to replace $or and clean up the query a bit.

Assume you have this document in users collections:
{
_id: ObjectId('...'),
name: 'wrq',
friends: ['A', 'B', 'C']
}
Code below to search first and thrid friend of user 'wrq':
db.users.aggregate(
[
{
$match: {
name: 'wrq'
}
},
{
$project:{
friend1: {
$arrayElemAt: ["$friends", 0]
},
friend3: {
$arrayElemAt: ["$friends", 2]
}
}
}
]
)

Related

How do I get count in MongoDB based on specific fields

I have documents like this in my MongoDB Listings collection.
listingID: 'abcd',
listingData: {
category: 'resedetial'
},
listingID: 'xyz',
listingData: {
category: 'resedetial'
},
listingID: 'efgh',
listingData: {
category: 'office'
}
I am trying to get total count of all listings and count according to category.
I can get total count of listings with aggregation query. But I am not sure how to get output like this resedentialCount: 2, officeCount: 1 , ListingsCount: 3
This is my aggregation query
{
$match: {
listingID,
},
},
{
$group: {
_id: 1,
ListingsCount: { $sum: 1 },
},
}
Try this:
let listingAggregationCursor = db.collection.aggregate([
{$group: {_id:"$listingData.category",ListingsCount:{$sum:1} }}
])
let listingAggregation=await listingAggregationCursor.toArray();
(I got this query from https://www.statology.org/mongodb-group-by-count)
This will give you an array of objects with each listing category as well as how many times they occur.
For getting the total listingsCount, sum up all of the count fields from the array of objects. You can do that like this:
let listingsCount=0;
for(listingCategory of listingAggregation) {
listingsCount+=listingCategory.count;
}
You should have the data you need at this point. Now it's just a matter of extracting and formatting it as you see fit.
Hope this helps!

Is it possible to delete the firsts elements in an array?

I'm coding an application, and in one of the parts of the code I need to delete a certain amount of elements from an array, but I don't have much experience with MongoDB/Mongoose.
For example, my query needs to delete the first n elements of an array:
let array = ["a", "b", "c", "d", "e"];
if (n < array.length) {
array = array.slice(n); // Delete the first n values
}
I could loop my code, but it will do a lot of queries, deleting one by one, which is bad for performance. My other alternative is to query the array elements, slice in the code, and update the database array with another query. It's also a good alternative, but I prefer to ask here because maybe there is a way to do it with just one query.
I know in MongoDB there is a way to just remove the first/last element, but I haven't found anything about deleting more than one.
Model.updateOne(filter, { $pop: { list: -1 } });
Hey you can check out the $slice operator when you query. That said, it works well, if you're removing multiple elements from the end or multiple elements from the start of the array. But if you're removing from the middle of the array, it may be easier to find the document first, apply a standard .slice() operation in JavaScript, and then save the document with the new array, by calling the .save() method on the document.
As examples for the $slice operator:
const document = { "_id" : 3, "scores" : [ 89, 70, 100, 20 ] }
db.students.update(
{ _id: 3 },
{
$push: {
scores: {
$each: [ ],
$slice: -2
}
}
}
)
/*
Our result is the last two elements, and we removed the first two.
*/
const result = { "_id" : 3, "scores" : [ 100, 20 ] }
Or you can something like the following:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]
}}}
]);
Where P is the index of element you want to stop removing in the array, leaving you with P to the end elements. (meaning all elements prior to P are removed)
Or this:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", P]
}}}
]);
Where P is the index of element you want to stop removing in the array from the end, leaving you with start to P elements. (meaning all elements after P are removed)

Mongo db - how to join and sort two collection with pagination

I have 2 collections:
Office -
{
_id: ObjectId(someOfficeId),
name: "some name",
..other fields
}
Documents -
{
_id: ObjectId(SomeId),
name: "Some document name",
officeId: ObjectId(someOfficeId),
...etc
}
I need to get list of offices sorted by count of documetns that refer to office. Also should be realized pagination.
I tryied to do this by aggregation and using $lookup
const aggregation = [
{
$lookup: {
from: 'documents',
let: {
id: '$id'
},
pipeline: [
{
$match: {
$expr: {
$eq: ['$officeId', '$id']
},
// sent_at: {
// $gte: start,
// $lt: end,
// },
}
}
],
as: 'documents'
},
},
{ $sortByCount: "$documents" },
{ $skip: (page - 1) * limit },
{ $limit: limit },
];
But this doesn't work for me
Any Ideas how to realize this?
p.s. I need to show offices with 0 documents, so get offices by documets - doesn't work for me
Query
you can use lookup to join on that field, and pipeline to group so you count the documents of each office (instead of putting the documents into an array, because you only case for the count)
$set is to get that count at top level field
sort using the noffices field
you can use the skip/limit way for pagination, but if your collection is very big it will be slow see this. Alternative you can do the pagination using the _id natural order, or retrieve more document in each query and have them in memory (instead of retriving just 1 page's documents)
Test code here
offices.aggregate(
[{"$lookup":
{"from":"documents",
"localField":"_id",
"foreignField":"officeId",
"pipeline":[{"$group":{"_id":null, "count":{"$sum":1}}}],
"as":"noffices"}},
{"$set":
{"noffices":
{"$cond":
[{"$eq":["$noffices", []]}, 0,
{"$arrayElemAt":["$noffices.count", 0]}]}}},
{"$sort":{"noffices":-1}}])
As the other answer pointed out you forgot the _ of id, but you don't need the let or match inside the pipeline with $expr, with the above lookup. Also $sortByCount doesn't count the member of an array, you would need $size (sort by count is just group and count its not for arrays). But you dont need $size also you can count them in the pipeline, like above.
Edit
Query
you can add in the pipeline what you need or just remove it
this keeps all documents, and counts the array size
and then sorts
Test code here
offices.aggregate(
[{"$lookup":
{"from":"documents",
"localField":"_id",
"foreignField":"officeId",
"pipeline":[],
"as":"alldocuments"}},
{"$set":{"ndocuments":{"$size":"$alldocuments"}}},
{"$sort":{"ndocuments":-1}}])
There are two errors in your lookup
While passing the variable in with $let. You forgot the _ of the $_id local field
let: {
id: '$id'
},
In the $exp, since you are using a variable id and not a field of the
Documents collection, you should use $$ to make reference to the variable.
$expr: {
$eq: ['$officeId', '$$id']
},

find the document which equals any one of the array element query with a non array field

mongo db schema variable
status:{
type: Number,
enum: [0,1,2,3,4,5], //0-NOT ACCEPTED,1-COMPLETED,2-PENDING
default: 0
}
status stored in db like 0 or 1 or 2. status search with user selection is array of datas like
status: {1,2}
how to get the documents which has any one the of the array element. I can't do a static search because array size can change every time
// if(status){
// query = {
// ...query,
// "status": status
// }
// }
console.log(body_status);
if(body_status){
query = {
...query,
"status": {"$in":body_status}
}
}
this works for me.
I don't know if I've understand the question but I think you want something like this:
db.collection.find({
"status": {
"$in": [
1,
2,
4
]
}
})
Example here
Please check if it works as expected or not and in this case update the question with more information.
Or maybe you want something like this:
db.collection.find({
"status": 1
})

mongodb $pull doesn't work with array in subdocument

I have a problem with an update with MongoDB.
My schema look like this:
Project: {
_id: ObjectId(pro_id)
// some data
dashboard_group: [
{
_id: ObjectId(dgr_id)
dgr_name: "My Dashboard"
dgr_tasks: [
id1,
id2,
...
]
},
// other dashboards
]
}
I want to remove id2 but the $pull operator seems not work. Mongo return me this :
result: {
lastErrorObject: {
n: 1,
updatedExisting: true
},
ok: 1
}
This is my request:
db.Project.findOneAndUpdate({
"dashboard_group._id": dgr_id
}, {
$pull: {
"dashboard_group.$.dgr_tasks": id2
}
});
dgr_id is already cast to ObjectId before the query and I verified the value that I want to remove.
Can anyone have an idea ?
You will need to select the particular array element using "$elemMatch" like this
Query : {"dashboard_group":{"$elemMatch":{dgr_name:"My Dashboard"}}}
Update : {$pull:{"dashboard_group.$.dgr_tasks":"id2"}}
So, I found a solution with the $[] identifier. It's not its basic utility, but it fit to my case.
A task ID cannot be at 2 location, it belongs to 1 and only 1 dashboard. So if you make a request like :
db.Project.findOneAndModify({
"dashboard_group._id": dgr_id
}, {
$pull: {
"dashboard_group.$[].dgr_tasks": id2
}
});
Mongo will remove all value that match id2. Without the {multi: true} option, it will make the update 1 time, and my item is indeed remove from my nested array.

Resources