6

I recently spent some time trying to use the $pull operator through Spring's Data MongoOperations interface, so I thought it would be nice to share my findings in case anyone bumps into a similar problem.

So here it goes...

I have 2 java POJOs like so :

@Document
public class OutterObject{

    private String id;
    private String name;
    private List<InnerDocument> innerDocs;


    //SETTERS - GETTERS Ommited


public class InnerDocument{


    private String id;
    private String name;

         //SETTERS - GETTERS Ommited

This is stored in a Mongo collection like so :

 "_id" : "doc2",
 "_class" : "OutterObject",
 "name" : "doc2",
 "innerDocs" : [{
      "_id" : "innerDoc21",
      "name" : "innerDoc21"
  }, {
      "_id" : "innerDoc22",
      "name" : "innerDoc22"
  }]

I'm trying to use the $pull operator in order to remove all objects inside the innerDoc collection having a name value = "innerDoc22".

I know how to accomplish this using the mongo driver like so :

 List<String> ids = 
         Arrays.asList("innerDoc22");

 BasicDBObject find = new BasicDBObject();

 match.put("innerDocs.name", 
           BasicDBObjectBuilder.start("$in", ids).get());

    BasicDBObject update = new BasicDBObject();
        update.put(
                "$pull",
                BasicDBObjectBuilder.start("innerDocs",
                        BasicDBObjectBuilder.start("name", "innerDoc22").get()).get());

  DBCollection col= mongoOperations.getDb().getCollection("outterObject");

  col.update(find , update);

I'm trying to accomplish the same thing using Spring's MongoOperations Interface. Here is my code using the MongoOperations interface :

List<String> ids = Arrays.asList("innerDoc22");

Query removeQuery = Query.query(Criteria.where("innerDocs.name").in(ids));

WriteResult wc = mongoOperations.upsert(
                    removeQuery, 
                    new Update().pull("innerDocs.name", "innerDoc22"), 
                    OutterObject.class);
System.out.println(wc.getLastError());

I'm not getting any errors when calling getLastError() the update is simply not done in the database.

I know a similar question has already been asked here but the answer that was given does not use the MongoOperations interface.

Community
  • 1
  • 1
ufasoli
  • 1,038
  • 2
  • 19
  • 41

7 Answers7

5

After searching a bit and looking at the source code I realized that I needed to pass an InnerDocument object as a second parameter to the pull method so that the spring classes would be able to do the mapping correctly.

As it turns out I can navigate objects while selecting objects (I'm using "innerDocs.name" in the removeQuery) but I cannot (or havent found a way) do the same when updating a document.

Below is how I implemented the query using MongoOperations :

List<String> ids = Arrays.asList("innerDoc22", "innerDoc21");

Query removeQuery = Query.query(Criteria.where("innerDocs.name").in(ids));

WriteResult wc = 
       mongoOperations.upsert(removeQuery, 
           new Update().pull("innerDocs", 
           new InnerDocument("innerDoc22", null)), 
           OutterObject.class);

System.out.println(wc.getLastError());
ufasoli
  • 1,038
  • 2
  • 19
  • 41
  • 2
    this [http://stackoverflow.com/a/27153010/234110] worked for me better, since _class was creating an issue in my case. – Anand Rockzz Jan 28 '16 at 20:00
2

You can also use the BasicDBObject instead of the InnerDocument I found this out by accident. By printing out the update object, you can see the actual mongo shell query json, which is super helpful for debugging. :

Update updateObj = new Update()
         .pull("innerDocs", new BasicDBObject("innerDocs.name","innerDoc22"));

System.out.println("UPDATE OBJ: " + updateObj.toString());

results in:

UPDATE OBJ: { "$pull" : { "innerDocs" : { "innerDocs.name" : "innerDoc22"}}}

med116
  • 1,526
  • 17
  • 16
2

I tried the solution given by med116 and I had to modify it to work :

Update updateObj = new Update().pull("innerDocs", new BasicDBObject("name","innerDoc22"));

Because otherwise there was no matching object in my array.

Amélie D.
  • 21
  • 1
0

in spring data mongo,just like this:


//remove array's one elem
UpdateResult wc = mongoTemplate.upsert(removeQuery,new Update().pull("tags",Query.query(Criteria.where("tag").is("java"))),TestPull.class);
//remove array's multi-elem
UpdateResult wc1 = mongoTemplate.upsert(removeQuery,new Update().pull("tags",Query.query(Criteria.where("tag").in(Arrays.asList("mongo", "netty")))),TestPull.class);
0

If you simply want to remove an element from array which does not have any other property like name then write the query you wish and

Update update = new Update();
update.pull("Yourarrayname","valueyouwishtodelete");  
mongoTemplate.upsert(query,update,"collectionname");
David Buck
  • 3,752
  • 35
  • 31
  • 35
0

That's my solution - thanks to @ufasoli:

  public Mono<ProjectChild> DeleteCritTemplChild(String id, String idch) {
    Query query = new Query();
    query.addCriteria(Criteria
                           .where("_id")
                           .is(id)
                           .and("tasks._id")
                           .is(idch)
                     );

    Update update = new Update();
    update.set("tasks.$", null);

    return template
         // findAndModify:
         // Find/modify/get the "new object" from a single operation.
         .findAndModify(
              query, update,
              new FindAndModifyOptions().returnNew(true), ProjectChild.class
                       );
  }
GtdDev
  • 748
  • 6
  • 14
0

This works for me!

UpdateResult updateResult = mongoTemplate.updateMulti(new Query(where("id").is(activity.getId())), new Update().pull("comments", new Query(where("id").is(commentId))), Activity.class);

It will generate:

Calling update using query: { "_id" : { "$oid" : "61f900e7c7b79442eb3855ea"}} and update: { "$pull" : { "comments" : { "_id" : "61fac32e3f9ab5646e016bc8"}}} in collection: activity
Nimantha
  • 6,405
  • 6
  • 28
  • 69
kopeclu2
  • 1
  • 1
  • 1