MongoDB学习笔记(十二)——删除文档
有插入文档,就有需求要删除文档。
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中的示例:
上面操作了两个删除操作,一个是删除“_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());
其打印结果为:
从上面可以看出,首先打印出test字段为“5484”的记录,然后执行删除“test”字段的值为“5484”并且“files”字段值大于10的记录,然后打印出test字段为“5484”的记录,可以看到依然能够打印出记录,说明刚才没有删除记录,这是因为删除的条件不符合,记录的files字段的值为5,而条件要求大于10,然后将条件改为删除“test”字段的值为“5484”并且“files”字段值大于4的记录,再次打印出test字段为“5484”的记录,发现打印结果为一个空数组,说明删除成功。
MongoDB Compass
MongoDB Compass删除集合和数组都是非常方便的,但是其只能删除整个集合或者某个文档,而不能删除集合的一部分文档,操作方法如下图:
点击“DELETE”按钮即可删除数据。
MongoDB java Driver
MongoDB的java驱动提供了12个方法用于删除数据,如下图:
,
可以分成三类: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:aaa@qq.com: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);
}
其返回结果如下图:
mongoTemplate
Spring对MongDB的封装 Spring-data-mongo 提供的对象mongoTemplate提供了11个方法用于删除数据,
其主要分为4类:remove(Class<T> domainType)
、remove()、findAllAndRemove()、findAndRemove()
其中第一个比较特殊,它就一个方法,需要传入实体类的class,该方法后面再讲,我们先讲remove()这一系列方法,
这一系列方法根据传入的参数不同来确定查询过滤条件、集合名称等,其有几个参数:
Object – 该对象代表所有实体类对象,其功能有两个,一,是根据实体类对象获取其id属性,组成一个以id为查询条件的Query对象。所以传入的实体类必须有id属性,且其值不能为空。二,是根据实体类的getClass()方法获取其Class对象,然后调用determineCollectionName()获取默认集合名称。所以一个Object对象若不传入其他参数,其可以默认两个必要参数。
Query – 用于组织查询过滤条件的对象,其借助与Criteria对象组成过滤条件,具体的可以查看下面的示例。
Class<?>
– 实体类的Class,一般传入此参数都是用于调用determineCollectionName()获取默认集合名称
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);
}
注:接下来的日子开始有工作忙了,只能抽时间写了,更新可能会慢些,请海涵!!