MongoDB高級應用 Author:SimpleWu 聚合 聚合(aggregate)主要用於處理數據(諸如統計平均值,求和等),並返回計算後的數據結果。有點類似sql語句中的 count( )。 $group 使用$group是對篩選的數據進行分組。類似於mysql中的group by關鍵字。 ...
MongoDB高級應用
Author:SimpleWu
聚合
聚合(aggregate)主要用於處理數據(諸如統計平均值,求和等),並返回計算後的數據結果。有點類似sql語句中的 count(*)。
//統計員工總數
db.emp.aggregate([{$count:"countName"}])
//或者
db.emp.find().count()
$group
使用$group是對篩選的數據進行分組。類似於mysql中的group by關鍵字。
//根據員工gender來分組並且統計數量
db.emp.aggregate([{$group : {_id : "$gender", count: {$sum : 1}}}])
說明:
- 這裡_id是表示分組的欄位,名字是固定的。
- count表示聚合生成列的名稱。
- $sum表示聚合函數。
- 1統計的值,其他聚合函數也可以是欄位。
聚合表達式
表達式 | 描述 | 實例 |
---|---|---|
$sum | 計算總和。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}]) |
$avg | 計算平均值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}]) |
$min | 獲取集合中所有文檔對應值得最小值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}]) |
$max | 獲取集合中所有文檔對應值得最大值。 | db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}]) |
$push | 在結果文檔中插入值到一個數組中。 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}]) |
$addToSet | 在結果文檔中插入值到一個數組中,但不創建副本。 | db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}]) |
$first | 根據資源文檔的排序獲取第一個文檔數據。 | db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}]) |
$last | 根據資源文檔的排序獲取最後一個文檔數據 | db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}]) |
這些聚合函數基本上與mysql,oracle中效果是一致的。
索引
所以這東西學習過資料庫的都知道是不可缺少的,當然我們的MangoDB也是有的。
索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取數據時必須掃描集合中的每個文檔,並選取那些符合查詢條件的記錄。
創建索引語法:
db.collection.createIndex(keys, options)
/*
key:你要創建的索引欄位,1 為指定按升序創建索引,如果你想按降序來創建索引指定為 -1 即可。如果多個欄位使用,隔開
*/
db.emp.createIndex({"name":1}) #創建單列索引
db.emp.createIndex({"name":1,"age":-1}) #創建多列索引。
索引的常見操作:
//查看集合索引
db.emp.getIndexes()
//查看集合索引大小
db.emp.totalIndexSize()
//刪除集合所有索引
db.emp.dropIndexes()
//刪除集合指定索引
db.emp.dropIndex("索引名稱")
索引的種類、
在mysql裡面索引有許多種類當然我們的MongoDB中也有很多種類:id索引、單鍵索引、多鍵索引、複合索引、過期索引、全文索引。
id索引
ID索引也稱為主鍵索引,是我們創建一個集合時,自動創建的索引。
集合的預設排序是按照id來進行排序的。在mongodb中id是根據ObjectId()來生成的,這個順序是以時間撮來進行生成。
單鍵索引
單鍵索引是最普通的索引。
和id索引不同,單鍵索引不會自動創建,需要我們手動創建。
db.col.createIndex({"name":1})//創建單列索引,對name列創建索引
多鍵索引
多鍵索引和單鍵索引創建形式相同,區別在於欄位的值。
單鍵索引:值是一個單一的值,例如:字元串,數字或者日期。
多鍵索引:值有多個記錄,例如:數組。
db.emp.createIndex({"name":1,"age":-1})//創建多列索引,對name和age創建索引
複合索引
當我們的查詢條件不只一個時,就需要建立符合索引。符合索引是在多個列上同時創建索引。
db.col.createIndex({"name":1,"age":-1}) //創建複合索引。
索引的命名
預設情況下,索引的命名是列+1或者-1,這種方式不是很方面記憶,而且刪除是也不太方面。這時候我們就需要為索引創建一個名稱。
//創建索引並命名為ix_name。
db.students.createIndex({name:-1},{name:"ix_name"})
唯一索引
我們可以為索引添加一個唯一性,從而保存該列的數據不允許重覆。
//創建索引並命名為ix_name。
db.students.createIndex({name:-1},{name:"ix_name",unique:true})
過期索引
過期索引:就是在一段時間後會自動過期的索引。在索引過期後,相應的數據也會被刪除。
適合存儲一些希望一段時間後會失效的數據,比如用戶登錄信息,存儲的日誌等。
db.collections.createIndex({time:1},{expireAfterSeconds:10})
過期索引的一些限制:
- 過期索引的值必須是指定的時間類型,必須使用ISODate或者ISODate數組,不能使用時間撮,否則不會被自動刪除。
- 如果指定的是ISODate數組,則按照最小時間刪除。
- 過期索引不能是複合索引。
- 刪除時間是有一定的誤差,由於刪除過程是由後臺程式每60秒跑一次,而且刪除數據也需要一定的時間。所以存在誤差。
全文索引
當要對一篇文章中的文本內容進行搜索的時候,這個時候可以考慮全文索引。全文索引可以加快檢索內容關鍵字的效率。全文索引只能對字元串或者字元串數組有效。
//創建全文索引
db.students.createIndex({name:"text",info:"text"})
使用全文索引
創建好全文索引後,我們就可以來使用全文索引,使用全文索引需要使用$text和$search兩個運算符。
//查找全文索引中包含了zhangsan的文檔。
db.students.find({$text:{$search:"zhangsan"}})
//查找全文索引中包含了zhangsan或者zhangsanfeng的文檔。
db.students.find({$text:{$search:"zhangsan zhangsanfeng"}})
//查找全文索引中包含了zhangsan,但不包含zhangsanfeng的文檔。
db.students.find({$text:{$search:"zhangsan -zhangsanfeng"}})
//查找全文索引中包含了zhangsan和zhangsanfeng的文檔。
db.students.find({$text:{$search:"\"zhangsan\" \"zhangsanfeng\""}})
全文索引的相似度
我們在百度中搜索時,經常會看到和我們關鍵字匹配度越高的,排行就越靠前。在mongodb中,我們還可以返回查詢結果的相似度,與sort一起使用效果會更好。
使用方式:在find後面跟上{score:{$meta:"textScore"}}
db.students.find({$text:{$search:"zhangsan"}},{score:{$meta:"textScore"}})
.sort({score:{$meta:"textScore"}})
全文索引的限制
- 每次查詢只能指定一個text。
- text操作符不能出現在$nor查詢中。
- 查詢中如果包含了text則hint將不再起作用。
- mongodb的全文索引對中文支持不是很好。
索引的註意事項
索引像一把雙刃劍,用得好可以提高查詢效率,如果用不好可能會導致性能的降低。
- $where和$exists完全不能走索引
- ne取反操作效率很低
- $not、$nin$or、$in
explain執行計劃
索引的性能如何,我們可以通過explain執行計劃來進行分析,從而使索引的性能達到最優。
explain的使用方式非常簡單,我們只需要在執行的find()命令後添加一個explain()方法即可。
db.students.find().explain();
文檔之間的關係
很多時候資料庫中的數據不是單獨存在的,數據和數據之間會有一些相互之間的聯繫。我們mongodb可以配置這種數據之間的關係。
文檔之間的關係
- 一對一(one to one)
- 一對多(one to many)
- 多對一(many to one)
- 多對多(many to many)
每種關係又可以有兩種方式來實現。
嵌入式:嵌套在一個document文檔中。
引用式:通過外鍵引用的方式來實現。
Java操作MongoDB
下載MongoDB驅動http://mongodb.github.io/mongo-java-driver/
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
<version>3.9.0</version>
</dependency>
連接資料庫,你需要指定資料庫名稱,如果指定的資料庫不存在,mongo會自動創建資料庫。
public static void main( String args[] ){
try{
// 連接到 mongodb 服務
MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
// 連接到資料庫
MongoDatabase mongoDatabase = mongoClient.getDatabase("students");
System.out.println("Connect to database successfully");
}catch(Exception e){
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
}
}
我們可以使用 com.mongodb.client.MongoDatabase 類中的createCollection()來創建集合
我們可以使用com.mongodb.client.MongoCollection類的 insertMany() 方法來插入一個文檔。
我們可以使用 com.mongodb.client.MongoCollection 類中的 find() 方法來獲取集合中的所有文檔。
你可以使用 com.mongodb.client.MongoCollection 類中的 updateMany() 方法來更新集合中的文檔。
要刪除集合中的第一個文檔,首先你需要使用com.mongodb.DBCollection類中的 findOne()方法來獲取第一個文檔,然後使用remove 方法刪除。
個人測試結果,基本操作:
package com.simple.nosql.mongodb;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
/**
* MangoDB基本操作
* @author SimpleWu
*
*/
public class Test1 {
private MongoClient mongoClient = null;
/**
* 獲取MongoDB客戶端連接
*
* @return
*/
public MongoClient getMongoClient() {
this.mongoClient = new MongoClient("localhost", 27017);
return this.mongoClient;
}
private MongoDatabase mongoDatabase = null;
/**
* 連接資料庫,你需要指定資料庫名稱,如果指定的資料庫不存在,mongo會自動創建資料庫。
*/
public MongoDatabase createConnection() {
getMongoClient();// 獲取客戶端連接
// 連接到資料庫
mongoDatabase = mongoClient.getDatabase("students");
System.out.println("Connect to database successfully");
return this.mongoDatabase;
}
@Before
public void before() {
createConnection();
}
/**
* 我們可以使用 com.mongodb.client.MongoDatabase 類中的createCollection()來創建集合
*/
@Test
public void createCollection() {
// 創建集合
mongoDatabase.createCollection("employee");
System.out.println("集合創建成功");
}
/**
* 獲取集合
*/
public MongoCollection<Document> getCollection() {
// 獲取指定名稱集合
MongoCollection<Document> collection = mongoDatabase.getCollection("employee");
System.out.println("集合獲取成功");
return collection;
}
/**
* 插入文檔
*/
@Test
public void testInsert() {
// 獲取集合
MongoCollection<Document> collection = getCollection();
Map<String, Object> map = new HashMap<>();
map.put("_id", 1);
map.put("name", "AA");
map.put("email", "[email protected]");
map.put("info", "my name is AA");
Document document = new Document(map);
collection.insertOne(document);
// 插入多個文檔
Document document1 = new Document();
document1.append("_id", 2);
document1.append("name", "BB");
document1.append("email", "[email protected]");
document1.append("info", "my name is BB");
Document document2 = new Document();
document2.append("_id", 3);
document2.append("name", "CC");
document2.append("email", "[email protected]");
document2.append("info", "my name is CC");
List<Document> list = Arrays.asList(document1, document2);
collection.insertMany(list);
System.out.println("插入文檔成功");
}
/**
* 檢索文檔
*/
@Test
public void testFind() {
// 獲取集合
MongoCollection<Document> collection = getCollection();
// 檢索所有文檔
FindIterable<Document> iterable = collection.find();
//獲取結果集
MongoCursor<Document> cursor = iterable.iterator();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
// 獲取name=AA的文檔
/*
* Document document = new Document(); document.append("name", "AA");
* FindIterable<Document> iterable = collection.find(document);
* select(iterable);
*/
}
/**
* 更新文檔
*/
@Test
public void update() {
// 獲取集合
MongoCollection<Document> collection = getCollection();
//創建需要更新的條件
Document document = new Document("name", "AA");
//創建需要更新的內容
Document upd = new Document("name", "AA-AA");
//更新文檔
Document document1 = new Document("$set", upd);
UpdateResult result = collection.updateOne(document, document1);
System.out.println("更新成功");
}
/**
* 指定條件查詢
*/
@Test
public void eqLtGt(){
MongoCollection<Document> collection = getCollection();
//創建條件
Document check = new Document("$eq", 1);
//指定判斷列
Document document1 = new Document("_id",check);
FindIterable<Document> iterable = collection.find(document1);
select(iterable);
}
/**
* 刪除文檔
*/
@Test
public void delete() {
// 獲取集合
MongoCollection<Document> collection = getCollection();
Document document = new Document("name", "BB");
DeleteResult result = collection.deleteOne(document);
System.out.println("刪除數量 : " + result.getDeletedCount());
}
/**
* 分頁
*/
@Test
public void testPage(){
getMongoClient();// 獲取客戶端連接
// 連接到資料庫
mongoDatabase = mongoClient.getDatabase("students");
// 獲取集合總記錄數
Long count = mongoDatabase.getCollection("employee").count();
MongoCollection<Document> collection = mongoDatabase.getCollection("employee");
int pageSize = 2;//當前頁
int pageCount = 10;//每頁文檔數
//開始頁數,每頁數量。取得結果值
FindIterable<Document> iterable = collection.find().skip( (pageSize - 1) *pageCount).limit(pageCount);
select(iterable);
}
public void select(FindIterable<Document> iterable) {
MongoCursor<Document> cursor = iterable.iterator();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
}