mongodb(2022)
了解
文档数据库MongoDB用于记录文档结构的数据,如JSON、XML结构的数据。一条文档就是一条记录(含数据和数据结构),一条记录里可以包含若干个键值对。键值对由键和值两部分组成,键又叫做字段。键值对的值可以是普通值,如字符串、整型等;也可以是其他值,如文档、数据及文档数据
{
GoodName: "战神", // 字符串类型值
Price: 44, // 实数类型值
AddedTime: 2016-3-1, // 日期类型值
Groups["书","it书"] // 数组类型值
{ISBN:"2214323423",Press:"电子工业"} //文档类型值
}
安装
1.下载MongoDB 3.4.4安装包到本机
2.建立安装路径,如D:\MongoDB\data。在D:\MongoDB\data路径下分别创建“\db”和"\log"子路径,
D:\MongoDB\data\db # 用于存放数据库文件
D:\MongoDB\data\log # 用于存放日志文件(mongod.log)
注意
必须要有data子文件路径,db和log子路径必须在data下面,不然安装完成后,执行MongoDB将会出错
bin路径下执行文件介绍
1.mongob.exe,是MongoDB最核心的服务器端数据库管理软件,不能暴露在公共网络上,主要实现服务器端数据库的数据处理、数据访问管理及其他后台管理,存在于每台数据库服务器上。数据库服务端
2.mongo.exe,客户端shell运行支持程序,为数据库系统管理提供了交互式操作数据库统一界面,也为系统开发人员测试数据库等操作提供了方便。Mongo实质是一个JavaScript代码交互式执行平台
3.mongos.exe,路由管理程序,用于MongoDB分片集群环境下的业务系统访问的路由管理
4.mongostat.exe,MongoDB运行状态监控工具,可以快速查看当前运行的mongod或mongos实例的状态
5.mongotop.exe,监控工具,可以根据时间持续对读写数据进行统计,默认1秒返回一次监控信息。
6.mongodump.exe,以人工执行方式,通过Mongod或Mongos,以二进制形式实现对数据库业务数据的导出备份。
7.mongorestore.exe,以人工执行方式,通过Mongod或Mongos,以二进制形式实现对备份数据的恢复,配合mongodump.exe一起使用
8.mongoexport.exe,在人工执行方式下,以JSON或CSV格式导出数据库数据
9.mongoimport.exe,在人工执行方式下,对mongoexport.exe导出的数据恢复到数据库系统之中。
- bsondump.exe,将BSON文件转换为可阅读的格式,如可以把Mongodump生成的输出文件转为JSON格式的可阅读文件
- mongofiles.exe,把任何数据类型的独立文件上传到MongoDB数据库中,以GridFS形式分块存储,并可以读取相应文件:MongoDB支持的各种编程语言的API接口都提供类似读写功能
- mongooplog.exe,以Oplog轮询的方式实现对远程服务器上的数据,同步到本地服务器上
- mongoperf.exe,用来测试磁盘IO性能的工具
开机自启/关闭服务
# 首先进入到mongodb安装目录的bin路径下
D:\Mongo\mongdb\bin>mongod --dbpath "d:\Mongo\mongdb\data\db" --logpath "d:\Mongo\mongdb\data\log\MongoDB.log" --install --serviceName "MongoDB"
D:\Mongo\mongdb\bin>net start MongoDB
MongoDB 服务正在启动 .
MongoDB 服务已经启动成功。
# 关闭服务
net stop MongoDB
文档值数据类型
数据类型 | 描述 | 举例 |
---|---|---|
null | 表示空值或者未定义的对象 | |
布尔值 | 真或假 | |
32位整数 | shell不支持该类型,默认会转换成64位浮点数,也可以使用NumberInt类 | |
64位整数 | shell不支持该类型,默认会转换成64位浮点数,也可以使用NumberLong类 | |
64位浮点数 | shell中的数字就是这一种类型 | |
字符串 | utf-8字符串 | |
符号 | shell不支持。它会将数据库中的符号类型的数据自动转换成字符串 | |
对象id | 文档的12字节的唯一id,保证一条文档记录的唯一性。可以在服务器端自动生成,也可以在代码端生成,允许程序员自行指定id值 | |
日期 | 从标准纪元开始的毫秒数 | |
正则表达式 | 文档中可以包含正则表达式,遵循JavaScript的语法 | |
代码 | 文档中可以包含JavaScript代码 | {"nodeprocess": function() |
undefined | 未定义 | |
数组 | 值的集合或者列表 | |
内嵌文档 | JSON、XML等文档本身 |
数据库类型
1.admin数据库:一个权限数据库,如果创建用户的时候将该用户添加到admin数据库中,那么该用户就自动继承了所有数据库的权限
2.local数据库:这个数据库永远不会被负责,可以用来存储本地单台服务器的任意集合
3.config数据库:当MongoDB使用分片模式时,config数据库在内部使用,用于保存分片的信息
4.test数据库:MongoDB安装后的默认数据库,可以用于数据库命令的各种操作,包括测试
mongodb数据库建立相关命令
创建自定义数据库名
use goodsdb
# 如果goodsdb数据库不存在,则新建立数据库,如果存在,则连接数据库,然后可以在该数据库上做各种命令操作
查看数据库
show dbs
// 对于刚刚建立的goodsdb数据库并没有显示出来,原因是goodsdb数据库里没有内容。
统计数据库信息
db.stats()
#
{
"db" : "goodsdb", # 数据库名
"collections" : 0, # 集合数量,刚刚安装为0
"views" : 0, #
"objects" : 0, # 文档对象的个数,所有集合的记录数之和
"avgObjSize" : 0, # 平均每个对象的大小,通过dataSize/objects得到
"dataSize" : 0, # 当前库所有集合的数据大小
"storageSize" : 0, # 磁盘存储大小
"numExtents" : 0, # 所有集合的扩展数据量统计数
"indexes" : 0, # 已建立索引数量
"indexSize" : 0, # 索引大小
"fileSize" : 0, # 文件大小
"ok" : 1
}
删除数据库
use goodsdb
db.dropDatabase()
# 显示删除成功
{ "ok" : 1 }
查看当前数据库下的集合名称
db.getCollectionNames()
查看数据库用户角色权限
show roles
增删改查
插入
1.插入一条简单文档
> use goodsdb
switched to db goodsdb
> db.goodsbaseinf.insert({name:"go语言",price:32})
WriteResult({ "nInserted" : 1 }) # 插入成功提示
> db.goodsbaseinf.find() # 显示集合内容
{ "_id" : ObjectId("62cc34c636d52f097f472808"), "name" : "go语言", "price" : 32 }
说明
1.insert命令,自动产生一个_id值
2.insert命令可以用过save命令代替。若给save命令指定
_id
值,则会更新默认的_id
值,如db.set1.save({_id:1002,x:"OK"})3.如果没有goodsbaseinf集合对象,则第一次insert时自动建立集合;若存在集合,则变成多条文档的集合
2.插入一条复杂文档
> db.goodsbaseinf.insert({name:"C语言",bookprice:33.2,adddate:2017-10-1,allow:true,baseinf:{ISBN:1882828121,pr
ess:"清华大学"},tags:["good","book","it","Program"]})
WriteResult({ "nInserted" : 1 })
> db.goodsbaseinf.find()
{ "_id" : ObjectId("62cc34c636d52f097f472808"), "name" : "go语言", "price" : 32 }
{ "_id" : ObjectId("62cc366c36d52f097f472809"), "name" : "C语言", "bookprice" : 33.2, "adddate" : 2006, "allow
" : true, "baseinf" : { "ISBN" : 1882828121, "press" : "清华大学" }, "tags" : [ "good", "book", "it", "Program
] }
3.插入多条文档
> db.goodsbaseinf.insert([{item:"小学教程",name:"小学一年级",price:12},{item:"初中教程",name:"初中一年级",price:15}])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 2,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
注意
多条文档一次性插入,利用了insert的原子性事务特征,保证所有插入文档要么插入成功,要么不成功
4.用变量方式插入文档
document=({name:"go语言",price:32})
db.goodsbaseinf.insert(document)
5.有序插入多条文档
>db.goodsbaseinf.insert(
[
{
_id:10,item:"小学教程",name:"小学一年级",price:12
},
{
_id:11,item:"初中教程",name:"初中一年级",price:15
}
],
{ordered:true} # 有序插入设置
)
注意
如数据库中以有"_id:11"记录,那么执行记录时,将失败。不能重复插入。但是当有ordered时,当为true时,一条都不插入,为false时,除了出错记录外,其他记录继续插入
6.自定义写出错确认级别(含insert命令出错返回对象显示)
db.goodsbaseinf.insert(
{
_id:1,item:"小学教程",name:"小学一年级",price:12
},
{
writeConcern: { w: "majority",wtimeout:5000 }
}
)
7.插入单条
db.goodsbaseinf.insertOne(
{
item:"小学教程",name:"小学一年级",price:12
}
)
8.一次性插入多条文档
db.goodsbaseinf.insertMany(
[
{
item:"小学教程",name:"小学一年级",price:12
},
{
item:"初中教程",name:"初中一年级",price:15
}
]
)
查询
1.查询集合所有文档
db.goodsbaseinf.find()
db.goodsbaseinf.find().pretty() # 更好看的格式化输出
2.等价条件查询
db.goodsbaseinf.find(
{name:"go语言"}
)
3.不显示id及值,并指定显示值
# 显示name和price,不显示_id
db.goodsbaseinf.find(
{
name,"go语言"
},
{
name:1,price:1,_id:0 # 0或false不显示指定字段,1或true显示指定字段
}
)
4.嵌套文档查询
db.goodsbaseinf.find(
{
"baseinf.press":"清华大学"
}
)
5.数组查询
# 等价查询某一数组
db.goodsbaseinf.find(
{
tags:["good","book","it","Program"]
}
)
# 查询数组里的某一个值
db.goodsbaseinf.find(
{
tags:"good"
}
)
# 查询有4个元素的数组
db.goodsbaseinf.find(
{
tags:{$size:4}
}
)
6.查找null值字段,查找指定无值字段
db.goodsbaseinf.insert(
[
{_id:2222,toy:null},
{_id:1112}
]
)
# 查找null值字段
db.goodsbaseinf.find(
{_id:2222,toy:null}
)
# 查找的值不存在
db.goodsbaseinf.find(
{
_id:1112,toy:{$exists:false}
}
)
7.查找返回值游标操作
# 打印显示游标获取的集合
var showCursor=db.goodsbaseinf.find()
showCursor.forEach(printjson)
8.limit和skip方法查询
# 返回第一条文档
db.goodsbaseinf.find().limit(1)
# 显示第3条开始的文档记录
db.goodsbaseinf.find().skip(2)
9.带$in运算符的查询
# 用带$in运算符实现或(or)条件查找
# 查找_id等于12或ObjectId("3123566745123423421")的记录文档
db.goodsbaseinf.find(
{
_id:{
$in:[12,ObjectId("3123566745123423421")]
}
}
)
10.通过查询操作符来查询
操作符 | 格式 | 实例 | mysql类似语句 |
---|---|---|---|
小于 | {:{$lt:}} | db.goodsbaseinf.find({price:{$lt:15}}) | where price<15 |
小于等于 | {:{$lte:}} | db.goodsbaseinf.find({price:{$lte:15}}) | where price<=15 |
大于 | {:{$gt:}} | db.goodsbaseinf.find({price:{$gt:15}}) | where price>15 |
大于等于 | {:{$gte:}} | db.goodsbaseinf.find({price:{$gte:15}}) | where price>=15 |
不等于 | {:{$ne:}} | db.goodsbaseinf.find({price:{$ne:15}}) | where price!=15 |
与(and) | db.goodsbaseinf.find({name:"xx",price:12}) | where name="xx" and price=12 | |
或(or) | {$or:[{key1:value1},{key2:value2},...]} | db.goodsbaseinf.find({$or:[{name:"xx"},{price:12}]}) | where name="xx" or price=12 |
模糊匹配 | 查询以固定值结尾的 | db.goodsbaseinf.find({name:{$regex:/语文$/}}) | where name like "%语言" |
查询以固定值开头的 | db.goodsbaseinf.find({name:{$regex:/^C语言/}}) | where name like "C语言%" | |
查询以固定值的任意部分 | db.goodsbaseinf.find({name:{$regex:/C/}}) | where name like "%C%" |
11.区间条件查找
# 查询价格范围大于3小于33的值,可用于文档数值字段,也可以用于数组字段
db.goodsbaseinf.find(
{
price:{$gt:3,$lt:33}
}
)
更新
1.update命令参数
参数名称 | 说明 |
---|---|
query | update的查询条件,类似sql update查询where子句后面的查询条件 |
update | update的更新对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询set子句后面的更新内容 |
upsert | 可选,如果不存在update的记录,是否插入objNew;true为插入,默认是false,不插入 |
multi | 可选,mongodb默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新 |
writeConcern | 可选,自定义写出错确认级别 |
collation | 可选,指定特定国家语言的更新归类规则 |
返回值:
更新成功,返回WriteResult({"nUpdated":n})对象
更新失败,返回结果中包含WriteResult.writeConcernError对象字段内容
2.修改一条简单文档
# 将购物单1修改为购物单2
use goodsdb
db.order.insert(
{
title:"商品购物单1",
amount:35,
}
)
# 改订单名称
db.order.update(
{title:"商品购物单1"},
{$set:{title:"商品购物单2"}}
)
# 修改数值
db.order.update(
{title:"商品购物单2"},
{$inc:{amount:5}} # $inc做加法运算,加5
)
# 修改titlss为title
db.order.update({_id:10,{$rename:{"titlss":"title"}}})
# 删除一个字段
db.order.update(
{
_id:10
},
{
$unset:{unit:"元"}
}
)
# 将$min给出的值与当前文档字段值进行比较,当给定值较小时则修改当前文档值为给定值
db.order.update(
{
_id:10
},
{
$min;{amount:50}
}
)
# 将$max给出的值与当前文档字段值进行比较,当给定值较大时则修改当前文档值为给定值
db.order.update(
{
_id:10
},
{
$max;{amount:50.5}
}
)
# 用ISODate更新当前文档时间字段的值
db.order.update(
{
_id:11
},
{
lasttime: ISODate("2019")
}
)
3.修改一条文档里的数组和嵌套文档
db.order.update(
{
_id:12
},
{
$set:{
# 采用下标的方式来指定要修改的对象
# 数组采用索引,从0开始
"detail.1":{name:"大米",price:40},
# 文档采用取key的方式
"overview.address": "天津市和平区"
}
}
)
4.多文档修改
# 修改所有面粉价格小于30的文档记录,把面粉价格改为40
db.order.update(
{
"detail.name":"面粉","detail.price":{$lte:30}
},
{
$set:{
"detail.1":{name:"面粉",price:40}
}
},
{
multi:true
}
)
5.增加文档字段
# update命令在特定情况下,可以增加文档的字段,甚至实现insert命令功能。这个特定条件是要修改的文档没有要修改的字段,而且update命令带upsert选项
db.order.update(
{
_id:10
},
{
$set:{"detail.0":{name:"西瓜",price:10},unit:"元"}
},
{
upsert:true
}
)
6.自定义写确认级别
# 当update命令在5秒内没有执行完成时,取消该命令操作,并返回错误值
db.order.update(
{
item:""
},
{
$set:{title:"测试",price:50}
},
{
multi:true,
writeconcern:{w: "majority", wtimeout:5000}
}
)
7.3个简化的修改命令
db.collection.updateOne()
db.collection.updateMany()
db.collection.replaceOne()
删除
1.删除参数说明
参数 | 说明 |
---|---|
必选,设置删除文档条件 | |
justOne: | 可选,false为默认值,删除符号条件的所有文档;true删除符合条件的一条文档 |
writeConcern: | 可选,自定义写出错确认级别 |
collation: | 可选,指定特定国家语言的删除归类规则 |
返回值:
删除成功,返回WriteResult({"nRemoved":n})对象。
删除失败,返回结果中会包含WriteResult.writeConcernError对象字段内容(跟writeConern配合使用)
2.删除一个集合里的所有文档记录
db.tests.remove({})
3.删除整个集合包括索引
db.tests.drop()
4.删除符合条件的所有文档记录
# 删除价格大于3的所有文档记录
db.tests.remove(
{
price:{$gt:3}
}
)
5.自定义写出错确认级别
# 删除价格小于3的所有文档记录,当时间超过5秒时,或者副本集大多数已完成该命令执行时,就中断该命令的执行,返回该命令的操作结果
db.tests.remove(
{
price:{$lt:3}
},
{
writeConern: {w: "majority", wtimeout:5000}
}
)
6.删除满足条件的单个文档记录
# 删除价格大于3的第一个文档记录
db.tests.remove(
{
price:{$gte:3}
},
{
justOne:true
}
)
7.删除含特殊语言符号的文档记录
db.tests.remove(
{
item:"cafe",price:{$lte:20}
},
{
justOne:true
},
{
cllation:{locale: "fr", strength:1}
}
)
索引
MongoDB数据库自动为集合_id建立唯一索引,可以避免重复插入同一_id的文档记录
单一字段(键)索引
语法:db.collection_name.createIndex({:})
命令说明:对一个集合文档的键建立索引,key为键名,n=1为升序,n=-1为降序
1.对单一键建立索引
db.books.createIndex(
{name:1}
)
2.嵌套文档单字段索引
db.books.insert(
{
name:"故事",
price:30,
tags:{press:"x出版社",call:"18222223321"}
}
)
db.books.createIndex({"tags.press":1})
字段唯一索引
语法:db.collection_name.createIndex({:,:,....},{unique:true})
命令说明:对一个或多个字段建立唯一索引。key为键名,n=1为升序,n=-1为降序
1.字段值唯一索引
db.books.createIndex({name:1}, {unique:true})
2.多字段索引
db.books.createIndex(
{
price:1,color:-1
}
)
3.多字段唯一索引
db.books.createIndex({name:1,price:1},{unique:true})
文本索引
语法:db.collection_name:createIndex({:"text",:"text2",...})
命令说明:在集合里,为文本字段内容的文档建立文本索引
1.基本文档索引
db.books.createIndex({name:"text"})
2.指定权重文本索引
db.books.createIndex(
{
name:"text",
price:"text"
},
{
weigths:{name:10}, # 为name指定索引权重
name:"TextIndex" # 默认情况下,price权重为1
}
)
3.通配符文本索引
db.books.createIndex({"$**":"text"})
hash索引
语法:db.collection_name.createIndex({key:"hashed"})
命令说明:key为含有哈希值的键
1.建立哈希索引
db.collection.createIndex({_id:"hashed"})
hashed索引不支持多字段索引
hashed会把浮点数的小数部分自动去掉,所以对浮点数字段进行索引时,要注意该特殊情况
hashed不支持唯一索引
索引相关的其他方法
db.collection.dropIndex(index) # 移除集合指定的索引功能。index参数为指定需要删除的集合索引名,可用getIndexes()函数获取集合的所有索引名称
db.collection.dropIndexes() # 移除一个集合的所有索引功能
db.collection.getIndexes() # 返回一个指定集合的现有索引描述信息的文档数组
db.collection.reIndex() # 删除指定集合上所有索引,并重新构建所有现有索引,在具有大量数据集合的情况下,该操作将大量消耗服务器的运行资源,引起运行性能急剧下降等问题的发生。
db.collection.totalIndexSize() # 提供指定集合索引大小的报告信息
mongodb(2022)的更多相关文章
- 2022年官网下安装MongoDB最全版与官网查阅方法(5.0.6)
一.下载安装 1.百度搜索,找到官网,或直接访问:https://www.mongodb.com/ 2.寻找下载位置,双击下载. 3.找到本地位置,双击执行,进入欢迎界面,选择next. 4.勾选协议 ...
- mongodb 副本集+分片集群搭建
数据分片节点#192.168.114.26mongod --shardsvr --replSet rsguo --port 2011 --dbpath=/data/mongodb/guo --logp ...
- SpringBoot集成MongoDB之导入导出和模板下载
前言 自己很对自己在项目中集成MongoDb做的导入导出以及模板下载的方法总结如下,有不到之处敬请批评指正! 1.pom.xml依赖引入 <!-- excel导入导出 --> <de ...
- CA周记 - Build 2022 上开发者最应关注的七大方向主要技术更新
一年一度的 Microsoft Build 终于来了,带来了非常非常多的新技术和功能更新.不知道各位小伙伴有没有和我一样熬夜看了开幕式和五个核心主题的全过程呢?接下来我和大家来谈一下作为开发者最应关注 ...
- SpringBoot之MongoDB附件操作
前言 近期自己针对附件上传进一步学习,为了弥足项目中文件上传的漏洞,保证文件上传功能的健壮性和可用性,现在我将自己在这一块的心得总结如下: 一.pom.xml依赖的引入 <dependency& ...
- MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(1)-后端项目框架搭建
前言: 前面的四个章节我们主要讲解了MongoDB的相关基础知识,接下来我们就开始进入使用.NET7操作MongoDB开发一个ToDoList系统实战教程. MongoDB从入门到实战的相关教程 Mo ...
- 万字详解,吃透 MongoDB!
本文已经收录进 JavaGuide(「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识.) 少部分内容参考了 MongoDB 官方文档的描述,在此说明一下. MongoDB ...
- ASP.NET Core开发者指南(2022版路线图)
ASP.NET Core开发者指南 2022年 ASP.NET Core 开发者指南: 在下面,您可以看到一个图,说明可以采用的路径以及要成为ASP.NET Core开发人员所想要学习的库.我将此图作 ...
- 7&的2022年终总结
7&的2022年终总结 文章目录 7&的2022年终总结 1.前言 2.技术 3.生活 4.展望未来 博客搬家的需要: var code = "49d515c3-0238-4 ...
- 【翻译】MongoDB指南/聚合——聚合管道
[原文地址]https://docs.mongodb.com/manual/ 聚合 聚合操作处理数据记录并返回计算后的结果.聚合操作将多个文档分组,并能对已分组的数据执行一系列操作而返回单一结果.Mo ...
随机推荐
- [转帖]sqlserver on linux vs windows
简单对比下sqlserver on windows与linux的特点,发现新的继续添加 对比项 sqlserver on windows sqlserver on Linux 备注 费用 需要wind ...
- 【转帖】通过pip命令安装好包之后,在pycharm中不显示此库,也不能调用
目录 1. 问题描述 2. 解决方法1 3. 解决方法2 1. 问题描述 在cmd输入pip list 命令可以看到我的库都已经安装好了,但是pycharm中却没有显示. 在PyCharm查找,并没有 ...
- [转帖]Linux中的Page cache和Buffer cache详解
1.内存情况 在讲解Linux内存管理时已经提到,当你在Linux下频繁存取文件后,即使系统上没有运行许多程序,也会占用大量的物理内存.这是因为当你读写文件的时候,Linux内核为了提高读写的性能和速 ...
- [转帖]查看x86 cpu睿频命令
查看cpu是否开启睿频,offline掉一些cpu核心后,查看cpu睿频是否升高? turbostat统计X86 处理器的频率.空闲状态.电源状态.温度等状态等 [root@rootbird~]# t ...
- [转帖]SPECjvm2008 User's Guide
SPECjvm2008 User's Guide https://spec.org/jvm2008/docs/UserGuide.html#UsePJA Version 1.0Last modifie ...
- wap2app下拉刷新
支持全局刷新,支持vue项目 目前支持wap2app,uin-app全局下拉刷新 戳我阅读原文 --转载自微信公众号:酿俗
- 小白学k8s(6)使用kubespray部署k8s
kubespray部署k8s 准备 需要关闭防火墙 配置hosts 处理镜像 配置文件 运行 通过对应的镜像 运行代码 查看结果 出现的问题 墙 错误的配置 kubespray部署k8s 准备 kub ...
- Python实现栈、队列、双端队列
栈的实现 class Stack(): def __init__(self): self.items = [] def push(self, item): self.items.append(item ...
- Program文件的作用
Program.cs文件分析 Program.cs文件是至关重要的一个文件,它包含应用程序启动的代码,还可以配置所需要的服务和应用管道的中间件. 需要掌握: 6.0版本前后生成的Program.cs文 ...
- HBase相关面试题汇总
1.HBase是什么? (1) HBase一个分布式的基于列式存储的数据库,基于Hadoop的hdfs存储,zookeeper进行管理. (2) HBase适合存储半结构化或非结构化数据,对于数据结构 ...