charliebrownie
charliebrownie

Reputation: 6117

How to insert data into subdocument using $push in query, instead of retrieving doc and saving it back

Edit: this was actually working

As the Mongoose - Subdocs: "Adding subdocs" documentation says, we can add a subdoc using the push method (i.e. parent.children.push({ name: 'Liesl' });)

But I want to go further, and would like to use the $push operator to insert subdocuments.

I have two Schemas: the ThingSchema:

var ThingSchema = mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  description: {
    type: String
  }
});

and the BoxSchema, the main document that has an array of subdocuments (things) of ThingSchema:

var BoxSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  description: {
    type: String
  },
  things: {
    type: [ThingSchema]
  }
});

var BoxModel = mongoose.model('Box', BoxSchema);

I need every subdocument in things to have unique names - that is, that it would be impossible to insert a new document into this array that has a name value that already exists in the subdocs.

I'm trying to do something like:

var thingObj = ... // the 'thing' object to be inserted

BoxModel.update({
  _id: some_box_id, // a valid 'box' ObjectId
  "things.name": { "$ne": thingObj.name }
},
{
  $push: { things: thingObj}
}, 
function(err) {
  if (err) // handle err
  ...
});

but not getting any desired results.

What would be the correct way to add a ThingSchema subdocument into BoxSchema's thing array using the $push operator to do so in the query (must not add the subdoc if there's another subdoc named the same), instead of the Mongoose Docs way?


Edit: this is actually the issue

I made a mistake, the code above works as expected but now the problem I have is that when thingObj does not match the ThingSchema, an empty object is inserted into the things array:

// now thingObj is trash
var thingObj = { some: "trash", more: "trash" };

When executing the query given the above trash object, the following empty object is inserted into the subdocs array:

{ _id: ObjectId("an_obj_id") }

What I want this case, when the thingObj doesn't match the ThingSchema, is nothing to be added.

Upvotes: 2

Views: 814

Answers (1)

kio
kio

Reputation: 374

$addToSet adds something unique to the array (as in it checks for duplicates). But it only works for primitives.

What you should do is put things into their own collection and make a unique index on name. Then, make this change

things: {
  type: [{type: ObjectId, ref: 'thingscollection'}]
}

this way you can do

BoxModel.update({
  _id: some_box_id, // a valid 'box' ObjectId
  "things": { "$ne": thingObj._id }
},
{
  $addToSet: { things: thingObj._id}
}, 
function(err) {
  if (err) // handle err
  ...
});

And when you fetch use .populate on things to get the full documents in there.

It's not exactly how you want it, but that's a design that might achieve what you're aiming for.

Upvotes: 1

Related Questions