I have a document that lists post item ids for an author separated out by topic. This results in a document such as the following:
{
_id: "sdkafjsadkfjads3023",
Author: "SomeGuy"
RecentPosts: {
"topic-1": {
Count: 4,
Posts: ["postitemid1","postitemid2","postitemid2","postitemid3"]
}
"topic-2": {
Count: 3
Posts: ["postitem5","postitem6","postitem8"]
}
}
}
Most of the time I am doing atomic pushes to each of these post arrays in the same update. What I want to do is limit the arrays above to 10 items at all times. This way, anytime I do a pushall to the same topic/posts. Is what I'm asking even possible, or should I do this a different way?
Thanks in advance
If I understand correctly Capped Collections are what you want.
http://www.mongodb.org/display/DOCS/Capped+Collections
As it turns out, this was a longstanding issue in MongoDB that was since added in MongoDB 2.4 release using the $slice operator.
db.students.update(
{ _id: 1 },
{ $push: { scores: { $each : [
{ attempt: 3, score: 7 },
{ attempt: 4, score: 4 }
],
$sort: { score: 1 },
$slice: -3
}
}
}
)
http://docs.mongodb.org/manual/tutorial/limit-number-of-elements-in-updated-array/
Related
This has been extensively covered here, but none of the solutions seems to be working for me. I'm attempting to remove an object from an array using that object's id. Currently, my Schema is:
const scheduleSchema = new Schema({
//unrelated
_id: ObjectId
shifts: [
{
_id: Types.ObjectId,
name: String,
shift_start: Date,
shift_end: Date,
},
],
});
I've tried almost every variation of something like this:
.findOneAndUpdate(
{ _id: req.params.id },
{
$pull: {
shifts: { _id: new Types.ObjectId(req.params.id) },
},
}
);
Database:
Database Format
Within these variations, the usual response I've gotten has been either an empty array or null.
I was able slightly find a way around this and accomplish the deletion by utilizing the main _id of the Schema (instead of the nested one:
.findOneAndUpdate(
{ _id: <main _id> },
{ $pull: { shifts: { _id: new Types.ObjectId(<nested _id>) } } },
{ new: true }
);
But I was hoping to figure out a way to do this by just using the nested _id. Any suggestions?
The problem you are having currently is you are using the same _id.
Using mongo, update method allows three objects: query, update and options.
query object is the object into collection which will be updated.
update is the action to do into the object (add, change value...).
options different options to add.
Then, assuming you have this collection:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
},
{
"_id": 3
}
]
}
]
If you try to look for a document which _id is 2, obviously response will be empty (example).
Then, if none document has been found, none document will be updated.
What happens if we look for a document using shifts._id:2?
This tells mongo "search a document where shifts field has an object with _id equals to 2". This query works ok (example) but be careful, this returns the WHOLE document, not only the array which match the _id.
This not return:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
}
]
}
]
Using this query mongo returns the ENTIRE document where exists a field called shifts that contains an object with an _id with value 2. This also include the whole array.
So, with tat, you know why find object works. Now adding this to an update query you can create the query:
This one to remove all shifts._id which are equal to 2.
db.collection.update({
"shifts._id": 2
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example
Or this one to remove shifts._id if parent _id is equal to 1
db.collection.update({
"_id": 1
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example
I am fairly new to MongoDB and cant seem to find a solution to this problem.
I have a database of documents that has this structure:
{
id: 1
elements: [ {elementId: 1, nr1: 1, nr2: 3}, {elementId:2, nr1:5, nr2: 10} ]
}
I am looking for a query that can add a value nr3 which is for example nr2/nr1 to all the objects in the elements array, so that the resulting document would look like this:
{
id: 1
elements: [ {elementId: 1, nr1: 1, nr2: 3, nr3:3}, {elementId:2, nr1:5, nr2: 10, nr3: 2} ]
}
So I imagine a query along the lines of this:
db.collection.updateOne({id:1}, {$set:{"elements.$[].nr3": nr2/nr1}})
But I cant find how to get the value of nr2 and nr1 of the same object in the array.
I found some similar questions on stackoverflow stating this is not possible, but they were 5+ years old, so I thought maybe they have added support for something like this.
I realize I can achieve this with first querying the document and iterate over the elements-array doing updates along the way, but for the purpose of learning I would love to see if its possible to do this in one query.
You can use update with aggregation pipeline starting from MongoDB v4.2,
$map to iterate loop of elements
divide nr2 with nr1 using $divide
merge current object and new field nr3 using $mergeObjects
db.collection.updateOne(
{ id: 1 },
[{
$set: {
elements: {
$map: {
input: "$elements",
in: {
$mergeObjects: [
"$$this",
{ nr3: { $divide: ["$$this.nr2", "$$this.nr1"] } }
]
}
}
}
}
}]
)
Playground
db.collection.update(
{ id:1},
{ "$set": { "elements.$[elem].nr3":elements.$[elem].nr2/elements.$[elem].nr1} },
{ "multi": true }
);
I guess this should work
I have the following collection:
{
_id: 12345,
quizzes: [
{
_id: 111111,
questions: []
}
]
},
{
_id: 78910,
quizzes: [
{
_id: 22222
}
]
}
I want to select the documents of a certain quiz from the quizzes that do not have the questions array and want to make sure that it uses the appropriate questions index. So I use the following query:
Answer.find({ 'quizzes.0.questions': { $exists: false } }).explain('queryPlanner');
Which returns:
{
queryPlanner: {
plannerVersion: 1,
namespace: 'iquiz.answers',
indexFilterSet: false,
parsedQuery: { 'quizzes.0.questions': [Object] },
winningPlan: { stage: 'COLLSCAN', filter: [Object], direction: 'forward' },
rejectedPlans: []
}
}
The query is not using any index as seen from the output. I have tried the following indexes and none get used:
{ quizzes.$**: 1 }
{ quizzes.questions: 1 }
{ quizzes.[$**].questions: 1 }
{ quizzes: 1 }
The only 1 that actually gets used:
{ quizzes.0.questions: 1 }
However this is not really practical as I may target any quiz from the quizzes array not just the first one. Is there a certain syntax for the index in my case or this is a current limitation of mongodb? Thanks!
Indexes generally do not help to answer queries in the form of "X does not exist". See also mongodb indexes covering missing values.
To verify whether the index is used, look up some data using a positive condition.
I'm trying to minimize the number of database calls in an application.
Is it possible to complete these two queries in a single call?
system_0 = System.objects(platform_id=platform_id, type=0).count()
system_1 = System.objects(platform_id=platform_id, type=1).count()
I do not know what is mongoengine, but I think you will be capable of translating my mongo shell answer to what is appropriate for you. Yes, you can achieve it with aggregation. For example:
db.collection.aggregate([{
$match : { platform_id : ... }
}, {
$group: {
_id: "$type",
count: { $sum: 1 }
}
}]);
If you have more types than 0, 1, you can exclude them in $match as well.
I have mongo collection with documents like below:
{
_id: [ObjectId]
writeDate: [DateTime]
publishDate: [DateTime]
...
}
I usually display list of such documents sorting by publishDate first and then on writeDate.
Now when I get given document _id I need to fetch list containing: 2 previous documents, this document and 2 next documents. So it should look like as follows:
[1,2,4,3,6,7,8,5,9,0]
if given id is 6 I should get
[4,3,6,7,8]
and if id is 4 I should get
[1,2,4,3,6]
The thing is that publish dates may be the same (then I additionally sort by writeDate), so I suppose I can't just order using $gte and $lte with given document's date. Also _id are not guaranteed to be in order.
Do you have any clues on how to do this?
You can not do this in one query, but you will have to use three instead:
// current
r = db.so.findOne( { _id: 6 } );
// previous 2
db.so
.find( { publishDate: { $lte: r.publishDate }, _id: { $ne: 6 } )
.sort( { publishDate: -1 } )
.limit( 2 );
// next 2
db.so
.find( { publishDate: { $gte: r.publishDate }, _id: { $ne: 6 } )
.sort( { publishDate: 1 } )
.limit( 2 );