有插入文档,就有需求要删除文档。

MongoDB shell
shell中删除文档很简单,其集合提供了一个remove()、deleteOne()、deleteMany()、findOneAndDelete()四个方法用于删除文档。

我们首先看一下前三个方法的定义。

db.collection.remove()方法有两种语法:
1.可以带一个查询文档和一个可选的justOne布尔值:

db.collection.remove(
   <query>,
   <justOne>
)

2.可以采用查询文档和可选的删除选项文档(2.6以后版本有效):

db.collection.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>,
     collation: <document>
   }
)

deleteOne()方法定义:

db.collection.deleteOne(
   <filter>,
   {
      writeConcern: <document>,
      collation: <document>
   }
)

deleteMany()方法定义:

db.collection.deleteMany(
   <filter>,
   {
      writeConcern: <document>,
      collation: <document>
   }
)

从上面三个方法的定义可以看出,remove()方法可以说是deleteOne()、deleteMany()的集合。remove()方法是有那个boolean值的justOne参数来分别是否只删除一个文档,也就是区分deleteOne()\deleteMany()。所以这里只讲remove方法。

我们看一下上面的定义的一些可以传入的参数:

query – 这是一个过滤条件(filter),用于规定一个查询规则,筛选出符合该查询条件的所有文档,删除操作将作用于经过该查询条件筛选之后的文档,类似与关系型数据库的where后面的过滤条件,(后面的查询、修改的方法都会用到该参数,这个参数比较重要,因为mongoDB中对于query的组合有点绕,后面会单起一篇博文来介绍各种的query组合)。如果想要删除所有的文档,在2.6版本以前只需不传入该参数即可,但是2.6版本以后,该参数必传,但可以仅仅传入一个空文档( { } ) 来删除所有的文档。当然该方法的删除所有文档不会删除集合中的索引,如果你通过集合的drop()方法来删除所有的文档,会删除该集合的所有文档和索引,但drop比remove速度快很多。

justOne – 可选参数, boolean值,用于确定是否仅仅删除符合查询条件文档的一个(应该是第一个,大家可以试试),其默认值为false,表示删除所有符合条件的文档。

writeConcern – 可选参数,写入关注,其值为一个文档,具体的就不讲了前面的博文有讲。

collation – 可选参数,指定用于操作的collation。collation允许用户指定特定于语言的字符串比较规则,例如大小写和重音标记等的规则。具体可见官方文档

在shell中 ,remove()方法返回一个WriteResult对象。

我们再看下findOneAndDelete()方法的定义:

db.collection.findOneAndDelete(
   <filter>,
   {
     projection: <document>,
     sort: <document>,
     maxTimeMS: <number>,
     collation: <document>
   }
)

该方法是3.2版本以后的新方法。该方法用于根据筛选条件和排序标准删除单个文档,并返回已删除的文档。其有三个参数:

projection – 可选参数,用于选择返回的文档的字段,选择的字段必须是文档字段的子集,如果你想返回全部字段,该参数可以省略。

sort – 可选参数。指定由过滤器匹配后的文档的排序顺序。

maxTimeMS – 可选的。指定操作必须在其中完成的时间限制(以毫秒为单位)。如果超出限制将引发错误。

从上面的定义可以看出,该方法与上面三个方法的区别就是删除数据后,其会返回删除的文档,其他的一致,这里就不演示了。

下面是我在shell中的示例:

mongodb 删除 filter mongodb 删除collection数据_mongoDB删除文档


上面操作了两个删除操作,一个是删除“_id”值为“ObjectId(“5ad847765bd47dbcb4d21f54”)”的记录,另一个删除存在“dsdfs”字段的文档。删除操作返回一个“WriteResult”对象,用于表示删除的结果。

js脚本

js脚本操作数据库的优点这里就不多说了。直接上实例代码,首先创建一个js文件,输入下面的代码:

//获取数据库链接
var db = connect("localhost/words","yfl","yfl");
//编辑查询过滤器,获取test字段的值为“5484”,并且files的值大于10的文档
var query = {test:"5484",files:{$gt:10}}
//编辑可选参数的writeConcern和justOne
var options = {writeConcern:{ w: "majority", wtimeout: 5000},justOne:true}

//先打印出test的值为“5484”的文档
printjson(db.mongoTest.find({test:"5484"}).toArray());

//执行删除操作,该次删除理论上删除不成功,因为没有符合的文档
db.mongoTest.remove(query,options)

//执行删除完成之后,打印出test的值为“5484”的文档
printjson(db.mongoTest.find({test:"5484"}).toArray());

//修改过滤条件
var query1 = {test:"5484",files:{$gt:4}}

//执行删除操作
db.mongoTest.remove(query1,options)

//执行第二次删除完成之后,打印出test的值为“5484”的文档
printjson(db.mongoTest.find({test:"5484"}).toArray());

其打印结果为:

mongodb 删除 filter mongodb 删除collection数据_mongoDB删除文档_02

从上面可以看出,首先打印出test字段为“5484”的记录,然后执行删除“test”字段的值为“5484”并且“files”字段值大于10的记录,然后打印出test字段为“5484”的记录,可以看到依然能够打印出记录,说明刚才没有删除记录,这是因为删除的条件不符合,记录的files字段的值为5,而条件要求大于10,然后将条件改为删除“test”字段的值为“5484”并且“files”字段值大于4的记录,再次打印出test字段为“5484”的记录,发现打印结果为一个空数组,说明删除成功。

MongoDB Compass

MongoDB Compass删除集合和数组都是非常方便的,但是其只能删除整个集合或者某个文档,而不能删除集合的一部分文档,操作方法如下图:

mongodb 删除 filter mongodb 删除collection数据_MongoDB_03


mongodb 删除 filter mongodb 删除collection数据_mongTemplate_04


点击“DELETE”按钮即可删除数据。

MongoDB java Driver

MongoDB的java驱动提供了12个方法用于删除数据,如下图:

mongodb 删除 filter mongodb 删除collection数据_mongTemplate_05


可以分成三类:deleteMany()、deleteOne()、findOneAndDelete()
分别用于删除多条记录、删除一条记录、删除一条记录并返回删除的记录。每类方法根据参数的不同又分为四种,下面我们看下方法中出现的参数:

Bson – 该参数用于删除时的查询过滤条件,其对象为Bson对象,该对象为一个接口类,是一种能够将自己渲染成BsonDocument的类型的接口,该参数有两种方式传入,一种是使用其实现类:驱动提供了6个实现类BasicDBObject, BsonDocument, BsonDocumentWrapper, CommandResult, Document, RawBsonDocument,这六个实现类除了BsonDocumentWrapper, CommandResult两个实现类,其余都可以传入作为Bson参数,当多个需要条件组合过滤时,可以使用其提供的append(),当然每个实现类需要传入的数据是不同的,有兴趣的可以研究下,甚至于BsonDocumentWrapper, CommandResult这两个实现类在某些情况下也是可以作为参数传入的。另外一种则是使用Filters提供的方法,其提供了所有的查询过滤的操作方法,所有方法返回为Bson对象,多个方法直接使用“.”链接即可,强烈推荐这种方式,因为特别简单。

DeleteOptions – 该参数是在执行操作是设置的选项,其提供了collation()方法来借助collation对象对操作进行设置,比如区域设置、大小写设置等等,详情大家可以参考collation的官方文档。

FindOneAndDeleteOptions – 设置以原子方式查找文档并将其删除的操作的选项。其和上面类似,不过其多了自身提供了一些特有的方法,比如设置排序规则等等。大家可以查看其官方文档

ClientSession – 与此删除操作关联的客户端会话

下面是我使用驱动操作MongoDB的一些代码:

/**
     * 用于演示MongoDB原生驱动删除数据
     */
    public void removeDataDemo1(){

        // 使用URI链接MongoDB,MonoClientURI格式为:mongodb://[用户名:密码@]host:port[/数据库]
        MongoClient client = new MongoClient(new MongoClientURI("mongodb://yfl:yfl@localhost:27017/words"));
        //获取MongoDatabase 对象
        MongoDatabase database = client.getDatabase("words");
        //获取集合对象
        MongoCollection col = database.getCollection("mongoTest");

        //测试删除前key为“test”的数据
        System.out.println("删除前key为test数据:");
        for (Document doc : (FindIterable<Document>)col.find(eq("key", "test"))){
            System.out.println( doc.toJson());
        }
        //删除key的值为“test”一个文档(测试删除第几个值)
        col.deleteOne(eq("key", "test"));
        //删除后key为“test”的数据
        System.out.println("删除后key为test数据:");
        for (Document doc : (FindIterable<Document>)col.find(eq("key", "test"))){
            System.out.println(doc.toJson());
        }
        //设置删除配置
        DeleteOptions op = new DeleteOptions();
        op.collation(Collation.builder().build());
        col.deleteOne(eq("key", "test"),op);
        //删除后key为“test”的数据
        System.out.println("删除后key为test数据:");
        for (Document doc : (FindIterable<Document>)col.find(eq("key", "test"))){
            System.out.println(doc.toJson());
        }

        //删除前key为“test1”的数据
        System.out.println("删除前key为test1数据:");
        for (Document doc : (FindIterable<Document>)col.find(eq("key", "test1"))){
            System.out.println(doc.toJson());
        }
        //设置删除配置
        col.deleteMany(eq("key", "test1"),op);
        //删除后key为“test1”的数据
        System.out.println("删除后key为test1数据:");
        for (Document doc : (FindIterable<Document>)col.find(eq("key", "test1"))){
            System.out.println(doc.toJson());
        }


        //使用Bson实现类过滤数据
        //设置过滤条件,javakey字段值为sdsd,比in关切value字段值大于10
        BasicDBObject ob = new BasicDBObject("javakey","sdsd");
        ob.append("value",new BasicDBObject("$gt",15));
        col.deleteMany(ob);
        //使用BsonDocument实现类设置查询过滤条件,其构造或append第二个参数需要传入BsonValue的实现类
        BsonDocument bd = new BsonDocument("javakey",new BsonString("sdsd"));
        bd.append("value",new BsonDocument("$gt",new BsonInt32(15)));
        col.deleteMany(bd);

    }

其返回结果如下图:

mongodb 删除 filter mongodb 删除collection数据_mongodb 删除 filter_06

mongoTemplate

Spring对MongDB的封装 Spring-data-mongo 提供的对象mongoTemplate提供了11个方法用于删除数据,

mongodb 删除 filter mongodb 删除collection数据_remove()_07


其主要分为4类:remove(Class<T> domainType)、remove()、findAllAndRemove()、findAndRemove()

其中第一个比较特殊,它就一个方法,需要传入实体类的class,该方法后面再讲,我们先讲remove()这一系列方法,

这一系列方法根据传入的参数不同来确定查询过滤条件、集合名称等,其有几个参数:

Object – 该对象代表所有实体类对象,其功能有两个,一,是根据实体类对象获取其id属性,组成一个以id为查询条件的Query对象。所以传入的实体类必须有id属性,且其值不能为空。二,是根据实体类的getClass()方法获取其Class对象,然后调用determineCollectionName()获取默认集合名称。所以一个Object对象若不传入其他参数,其可以默认两个必要参数。

Query – 用于组织查询过滤条件的对象,其借助与Criteria对象组成过滤条件,具体的可以查看下面的示例。

Class<?>

collectionName – 指定要在那个集合中执行删除操作,因为在执行操作时,集合名称是必须的,所以若没有传入此参数,必须传入实体类或实体类的Class来确定默认集合名称。

上面的remove()一系列方法虽然根据传入的参数不同,但是其在mongoTemplate对象中最终执行的方法都是同一个:protected <T> DeleteResult doRemove(final String collectionName, final Query query, @Nullable final Class<T> entityClass) {}其虽然传入的参数不同,但是mongoTemplate方法内部会自动将参数进行转换,比如将Object对象转换成以id为查询条件的Query对象,亦或者根据传入的实体类或实体类对象转化成默认集合名称,其流程如下:Object -> getClass() -> Class<?> -> determineCollectionName() -> collectionName

remove(Class<T> domainType)

现在我们来看一下remove(Class<T> domainType)这个方法,该方法在官方API给出的定义是:为给定的domainType创建一个删除操作对象。

注意:这里给出的定义是创建一个删除对象,而不是执行一个操作,其实际上是执行了new ExecutableRemoveOperationSupport.ExecutableRemoveSupport(this.tempate, domainType, ALL_QUERY, (String)null);这句代码,即创建了一个ExecutableRemoveOperationSupport.ExecutableRemoveSupport对象,该对象本身没有提供方法,但是其继承了ExecutableRemoveOperation.RemoveWithCollection、ExecutableRemoveOperation.RemoveWithQuery、ExecutableRemoveOperation.TerminatingRemove三个类的四个方法:inCollection()、matching()、all(), findAndRemove().

所以我们调用该方法想要删除数据,我们还得要调用其findAndRemove()方法,用于删除查找到的数据,并返回删除的文档。

该方法只是传入一个实体类的class,那么他是怎么确定删除哪个集合中的数据的?
从源代码可以知道,其集合的确定是根据其class来确定的默认集合名称。其查询过滤条件则是一个new出来的新的空的Query对象,也就匹配默认集合名称中的所有数据。

那么方法的功能也就明了了:创建一个匹配默认集合名称中的所有数据的删除对象,调用findAndRemove()方法可删除默认集合名称中的所有数据。

findAllAndRemove()、findAndRemove()

这两类方法功能差不多,都是删除匹配的文档,并返回删除的文档,只不过一个是删除一个并返回,一个删除所有文档并返回。

其参数的用法和上面的一致,只不过需要注意一个地方就是,其必需传入一个参数Class<?>,该参数是如果不传,则会默认为Object类,该类有两个用处:一,根据determineCollectionName()方法确定默认集合名称。二 ,确定返回的文档的实体类型,其会将返回的文档封装成对应的实体类,有对应的键则赋值,无对应的值则忽略,如果实体类的属性和文档的字段全部不符合,则返回一个空对象,而不是null。

下面是一些示例:

“`
@Service
public class MongodbRemoveDemo {

@Autowired
private MongoTemplate mongoTemplate;

/**
 * 使用spring封装mongodb的对象mongoTemplate删除数据
 */
public void removeDataDemo2(){

    //删除mongoAddDemo集合中的全部文档并返回。其中MongoAddDemo实体类需要id属性,否则会报错
    List result =  mongoTemplate.remove(MongoAddDemo.class).findAndRemove();
    //传入一个实体类对象,该实体类需要youid属性,且如果不传入集合名称,则默认选择实体类名的字符串首字母小写形成的默认集合名
    MongoAddDemo entity = new MongoAddDemo();
    entity.setId("1");
    mongoTemplate.remove(entity,"mongoTest");

    //设置过滤查询条件,获取item为“opt”,qss为sesr的文档
    Query query = new Query(Criteria.where("item").is("opt").and("qss").is("sesr"));
    mongoTemplate.remove(query,"mongoTest");

    //从mongoAddDemo集合中删除tem为“opt”,qss为sesr的文档
    mongoTemplate.remove(query,MongoAddDemo.class);

    //设置过滤查询条件,获取_id 为 ObjectId("5ad86004c5749c0734097b21")的文档
    Query query1 = new Query(Criteria.where("_id").is(new ObjectId("5ad86004c5749c0734097b21")));
    mongoTemplate.remove(query1,"mongoTest");


    //设置过滤查询条件,获取item为“opt”,qss为sesr的文档,
    Query query2 = new Query(Criteria.where("item").is("opt").and("qss").is("sesr1"));
    //findAndRemove方法删除符合过滤的条件的第一个文档,并将文档返回,返回对象为传入的实体类,
    // 将删除的文档的字段赋值到实体类对象中,有则赋值,没有的则不赋值,若返回的文档的字段在实体类中都没有,则返回一个空对象,而不是null对象
    MongoAddDemo1 result1 = mongoTemplate.findAndRemove(query2,MongoAddDemo1.class,"mongoTest");
    System.out.println(result1);

}

注:接下来的日子开始有工作忙了,只能抽时间写了,更新可能会慢些,请海涵!!