1

I have an express app, and I am using mongodb for my DB needs. My collection structure is as follow.

Collection has documents like the one below.

{
    _id: 5caa266c58b170106ff17f40,
    title: "Test2 88 bulkWrite",
    last_save_date: 2019-04-07T17:08:25.149+00:00,
    created_by: "McufNwnQX8YXC0tNSa1fdbt9Tus2",
    stories: [ { story_id: "1", objects: [...] },
               { story_id: "2", objects: [...] },
               { story_id: "3", objects: [...] },
                ... 
             ]    
 }

The ask is simple, when let's say a user clicks a button, the array element {story_id: "2", objects: [...]} is deleted, and all the elements with story_id of more than "2" ( s.a., "3", "4"...) are decremented by one (become "2" and "3").

The first part, i.e., delete has been accomplished, but it's the second part which eludes me. Here is the code:

var storyId = ... (fetch from a request);
var intStoryId = parseInt( storyId) ;

var arrayOfHigherNumber = function() {
    var temp = [];
    if ( intStoryId == 5 ) { return temp; }
    for ( var i = intStoryId + 1; i <= 5; i++ ) {
        temp.push( i.toString() );
    }
    return temp;
}

collection.bulkWrite([
// This works and is able to delete the particular story_id entry
    { updateOne: {
        'filter': {'_id' : ObjectID( docId ) },
        'update': { 
            '$pull': { 'stories': { 'story_id': storyId } }
        }
    }},
// This does NOT work. 
    { updateMany: {
        'filter': { '_id': ObjectID( docId ), 'story_id': { '$in': arrayOfHigherNumber() } },
        'update': {
            '$set': { 'stories': { 'story_id': ( parseInt( 'story_id', 10 ) - 1 ).toString() } 
         }
     }
   }}
])

I have been banging my head against the wall for the last 24 hours and have tried many approaches, including NumberInt, $int, $convert, and several more such as aggregation pipelines, but I have not been able to accomplish this. It seems simple to be accomplished easily, but alas! Please note the story_id is a string, hence it's not that straightforward to use, let's say, a $inc operator.

EDIT

Answer by Anthony Winzlet is correct. I handled it just a bit differently, that's all.

My method

collection.findOneAndUpdate(
    { "_id": ObjectID( docId) },
    { "$pull": { "stories": { "story_id": storyId } } },
    { returnOriginal: false }
)
.then( function( story ) {
    collection.findOneAndUpdate(
        { "_id" : ObjectID( docId ) },
        { "$set": {
            "stories": story.value.stories.map(a => {
                if ( parseInt( a.story_id, 10 ) > parseInt( storyId, 10 ) ) {
                    return { story_id: (parseInt( a.story_id, 10 ) - 1).toString(), objects: a.objects }
                } else {
                    return { story_id: a.story_id , objects: a.objects }
                }
            })
        }}
    )
    .then( function ( result ) {
        response.send( result );
        client.close();
    })
})
4
  • Firstly you cannot use the aggregation operators inside the update query. And second what is the type of the story_id field? Commented Apr 9, 2019 at 18:39
  • @AnthonyWinzlet It is a string. Commented Apr 9, 2019 at 18:39
  • 1
    It will be better if you can change it to integer value and then you can easily use arrayFilters to update the story_id. If not then you have to use iteration using find query first and then will have to update. Commented Apr 9, 2019 at 18:44
  • Actually I thought of that earlier as well, but as per my logic a whole JSON is being saved as a document, and I have no control for a pre-conversion in JS using NumberInt, etc. before adding the collection in the DB. However, find seems promising. Commented Apr 9, 2019 at 18:46

2 Answers 2

1

Instead of bulk query use two operations using async await

const story = await collection.findOneAndUpdate(
  { "_id" : ObjectID( docId ) },
  { "$pull": { "stories": { "story_id": storyId } }},
  { "new": true }
)

const update = await collection.findOneAndUpdate(
  { "_id" : ObjectID( docId ) },
  { "$set": {
    "stories": story.stories.map((a, i) => { if (parseInt(a.story_id) > parseInt(story_id)) return { story_id: parseInt(a.story_id) + 1 }})
  }}
)
Sign up to request clarification or add additional context in comments.

6 Comments

This seems promising, what is story here in story.stories.map(...) ? Should that be a positional operator, s.a., $ ? Running it in the same form will throw Reference Error
What is the error? And it should be $set operator. Updated the answer
Yeah I could get it'd be $set. Error: ReferenceError: story is not defined
Check my above both the query I have defined the story in the first query. So not possible
Let me check it more thoroughly, because logically you are right and everything should work.
|
0

collection.updateMany({ story_id : { $gte : deletedElementNo } }, { $inc : { story_id: -1 } });

I think this is something of the sort you are looking for?

1 Comment

story_id is a string unfortunately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.