mongoDB-特殊索引

全文檢索

什麼是?

    使用正則表達式模糊檢索文本內容,對於大段文本來說,效率很低,而且無法理解語義

    這個時候可以使用全文檢索,可以快速進行文本檢索,且內置多種語言分詞機制,可理解語義,

MongoDB提供文本索引來支持全文檢索,文本索引可以建立在任何字符串格式的鍵上,甚至可

以建立在以字符爲元素的數組上

問題:

        文本索引比普通索引可能導致更嚴重的性能問題:

            創建時,開銷較大,甚至可能導致MongoDB過載,故儘量在離線狀態下創建

            插入時,文本索引可能導致寫入性能比一般集合要差

何時使用?

        只有對大段文本檢索內容時才使用全文檢索

如何創建?

//創建理解語義的文本索引
db.myColl.ensureIndex({鍵1:"text",鍵2:"text"});
//對所有文本類型鍵創建文本索引
db.myColl.ensureIndex({"$**:"text""})

可同時在多個文本類型的鍵上建立符合文本索引,在檢索時,可以同時檢索

//全文索引
db.myColl.find({$text:{$search:'關鍵詞1 關鍵詞2'}});
//多個關鍵詞之間是"或"的關係

全文索引主要依賴於特定語言的分詞機制和語義,絕不等同於增則表達式的字符匹配

搜索詞法

默認,使用文本索引時,search鍵後的多個關鍵詞用空格分割,以OR關係分頭檢索

精準匹配:

//創建精準匹配
db.myColl.find({$text:{$search:'"關鍵詞1"'}})

使用雙引號包裹的關鍵詞或詞組要求必須完整匹配

實例:

db.movies.drop();
db.movies.insert([
    {
        title:"Titanic",
        content:"I'm king of the world!",
        year:1997
    },
    {
        title:"The Godfather",
        content:"Keep your friends close, but your enemies closer",
        year:1972
    },
    {
        title:"Gone With The Wind",
        content:"After all, tomorrow is another day!",
        year:1939},
    {
        title:"Forrest Gump",
        content:"Miracles happen every day",
        year:1994
    },
    {
        title:"The Lion King",
        content:"This is my kingdom. If I don't fight for it, who will?",
        year:1994},
    {
        title:"Titanic",
        content:"To make each day count",
        year:1997
    },
]);
//創建全文索引
db.movies.ensureIndex({content:"text"});

//查找
//db.movies.find({$text:{$search:'enemy'}})

//精確查詢
db.movies.find({$text:{$search:'"enemies"'}})

排除匹配:

在關鍵詞前加-

聚合

什麼是?

    通過一些操作符進行統計和彙總,返回我們想要的結果

聚合框架:

    通過構建管道,對數據逐級篩選,投射,分組,排序的彙總和統計過程

管道操作:

   1、$match

什麼是?

    專門對集合中的文檔進行篩選

何時使用?

    只要希望在聚合前過濾掉不符合條件的文檔,就可以使用$match

通常,$match用於聚合管道的第一步篩選:

好處:

        1、可減少統計的數據量;

        2、在投射和分組前篩選還可以應用索引

如何使用?

db.myColl.aggregate({$match:{查詢文檔}})

優化:在數據進入管道前,就過濾掉儘量多的文檔,可以極大提高管道操作的效率

實例:

db.emps.drop();
for(var i=0,arr=[];i<100000;i++){
    arr.push({
        ename:"e"+(100000+i+"").slice(1),
        address:{
            city:"city"+(1000+parseInt(Math.random()*100)+"").slice(1),
            street:"street"+(10000+parseInt(Math.random()*1000)+"").slice(1)
        }
    });
}
db.emps.insert(arr);

//篩選
db.emps.aggregate({$match:{'address.city':'city014'}})

   2、$project

什麼是?

    最簡單的$project操作就是從文檔中選擇想要的鍵。選取鍵時,可以指定返回或者不返回哪個鍵

何時使用?

    如果聚合只針對集合中文檔的個別鍵進行彙總,就可以先用$project僅選擇需要的鍵

如何使用?

db.myColl.aggregate({$project:{鍵1:1,鍵2:0}});

可以爲投射出的鍵重命名:

db.myColl.aggregate({$project:{別名:"$原鍵",鍵:1,...}})

其中:$原鍵表示,讓新別名引用原鍵的意思,$後面跟的是原鍵名

db.emps.drop();
for(var i=0,arr=[];i<100000;i++){
    arr.push({
        ename:"e"+(100000+i+"").slice(1),
        address:{
            city:"city"+(1000+parseInt(Math.random()*100)+"").slice(1),
            street:"street"+(10000+parseInt(Math.random()*1000)+"").slice(1)
        }
    });
}
db.emps.insert(arr);
//從結果集中指定要返回的鍵(類似find方法第二個參數)
db.emps.aggregate(
    {$project:{city:'$address.city'}})

算數表達式

什麼是?

    專門對指定鍵執行算數計算的表達式

何時使用?

    只要希望對選取的鍵進行計算後再統計時

操作符:

    $add 加,$subtract 減,$multiply 乘,$divide 除,$mod 取餘

如何使用?

{$project:{別名:{
    $算數操作:[
    "$鍵1"/值1,
    "$鍵2"/值2,
    ...]
    }
  }
}

注:

因爲計算後的鍵值和原鍵值肯定不一樣,所以通常都需要其別名

參與算數表達式的鍵或直接量,順序放在算數操作符後的數組中

例:

計算每個員工的工資和獎金的和
$add:["$salary","bonus"]
等價於$salary+$bonus

對鍵名執行算數運算,等效於對集合中每個文檔的對應鍵值都很執行相同的算數操作

實例:

db.emps.drop();
for(var i=0,arr=[];i<100000;i++){
    arr.push({
        ename:"e"+(100000+i+"").slice(1),
        address:{
            city:"city"+(1000+parseInt(Math.random()*100)+"").slice(1),
            street:"street"+(10000+parseInt(Math.random()*1000)+"").slice(1)
        }
    });
}
db.emps.insert(arr);

//從結果集中指定要返回的鍵(類似find方法第二個參數)
db.emps.aggregate(
    {$project:{city:'$address.city'}})

//算數運算表達式
db.movies.aggregate(
    {$project:
        {
            myYear:{
                $subtract:[
                    2018,
                    '$year'
                ]
            }
        }
    }
)

算術表達式的嵌套

什麼是?

    算術表達式可以多級嵌套,實現複雜的算數計算

    一個算數表達式的結果,可以繼續作爲另一個外層算數運算的參數值之一

如何使用?

{$project:{ 別名:{
$算數運算2:[
{$算數運算1:["$鍵1"/值1,"$鍵2"/值2]},"$鍵3"/值3
]
}}}

實例:

db.emps.drop();
for(var i=0,arr=[];i<100000;i++){
    arr.push({
        ename:"e"+(100000+i+"").slice(1),
        address:{
            city:"city"+(1000+parseInt(Math.random()*100)+"").slice(1),
            street:"street"+(10000+parseInt(Math.random()*1000)+"").slice(1)
        }
    });
}
db.emps.insert(arr);
//從結果集中指定要返回的鍵(類似find方法第二個參數)
db.emps.aggregate(
    {$project:{city:'$address.city'}})
//算數表達式的嵌套
//需求 從學生集合中,計算數學成績和語文成績的分數之和,並計算平均值(除以2)
db.stus.aggregate({
    $project:{
        averageScore:{
            $divide:[
                {
                  $add:
                   ['$math', '$chinese']
                },
            2
            ]
        }
    }
})

日期表達式

什麼是?

    日期表達式專用於獲取日期類型鍵值或日期對象的指定分量的值

何時使用?

    只要按照日期類型的鍵值進行統計時,都需要用日期表達式取出指定分量,再計算或者統計

如何使用?

{$日期操作符:"$日期類型值"/日期對象}

日期操作符

    $year,$month,$week,$dayOfWeek,

    $dayOfMonth,$dayOfYear,$hour,$minute

    $second

字符串表達式

什麼是?

    字符串表達式專門對指定鍵的值執行字符串操作

何時使用?

    只要希望再統計前對鍵值執行字符串操作時,就用字符串表達式

字符串操作符包括:

    $substr:專門負責獲取鍵值中的字符串

$substr:["$鍵" /值,開始位置,要獲取的字符個數]

    $concat:專門負責將多個子字符串拼接爲一個完整字符串

$concat:["$鍵"/值1,"$鍵"/值2,...]

實例:

db.products.drop();
db.products.insert([
    {
        "pname" : "iphone6",
        "count" : 80.0,
        "createdDate" : ISODate("2015-01-20T00:00:00.000+08:00"),
        "random" : 0.561293853912503,
        "RAM" : "64G"
    },

    /* 2 */
    {
        "pname" : "iphone6plus",
        "count" : 68.0,
        "createdDate" : ISODate("2015-06-12T00:00:00.000+08:00"),
        "random" : 0.561293853912503,
        "RAM" : "128G"
    },

    /* 3 */
    {
        "pname" : "iphone6s",
        "count" : 35.0,
        "createdDate" : ISODate("2016-06-04T14:59:23.409+08:00"),
        "random" : 0.561293853912503,
        "RAM" : "16G"
    },

    /* 4 */
    {
        "pname" : "iphone6splus",
        "count" : 36.0,
        "createdDate" : ISODate("2016-06-04T14:59:23.411+08:00"),
        "random" : 0.561293853912503,
        "RAM" : "128G"
    },

    /* 5 */
    {
        "pname" : "iphoneSE",
        "count" : 50.0,
        "random" : 0.561293853912503,
        "RAM" : "16G"
    }
]);

db.products.aggregate({
    $project:{
        spec:{
            $concat:[
                {
                    $substr:[
                        '$pname',6,6
                    ]
                },
                " ",
                "$RAM"
            ]
        }
    }
})

比較和關係表達式

什麼是?

    MongoDB中的邏輯表達式和條件運算

何時?

    只要希望根據鍵值判斷條件是否成立,或者夕陽根據不同條件返回不同的值,就用邏輯表達式

如何?

$cmp:["$鍵1"/值1,"$鍵2"/值2]

如果前>後,則返回證書,否則如果前<後,則返回負數,否則返回0

比較任意兩個字符串類型的鍵/值得大小關係

$strcasecmp:["$鍵1"/值1,"$鍵2"/值2]

關係表達式:  

簡單判斷兩鍵/值得大小,包括:

    $eq,$ne,$gt,$gte,$lt,$lte:["$鍵1"/值,"$鍵2"/值2]

如果滿足條件,則返回true,否則就返回false

邏輯表達式

什麼是?

    將多個關係表達式的結果綜合起來得出的最終結論

$and/$or/$not:[關係表達式1,... ...]

實例:查詢stus中語文成績>90和數學成績>90的數據

db.stus.aggregate({
	$project:{
		goodStu:{
			$and:[
				{$gte:['$chinese',90]},	
				{$gte:['$math',90]}	
		]
		}
	}
})

實例:查詢stus中語文成績>90或數學成績>90的數據

db.stus.aggregate({
	$project:{
		goodStu:{
			$or:[
				{$gte:['$chinese',90]},	
				{$gte:['$math',90]}	
		]
		}
	}
})

實例:查詢stus中語文成績不<=90,不數學成績<=90的數據

db.stus.aggregate({
	$project:{
		goodStu:{
			$not:[
				{$lte:['$chinese',90]},	
				{$lte:['$math',90]}	
		]
		}
	}
})

條件表達式

什麼是?

      根據不同的條件返回不同的值

$cond

    相當於程序中的條件運算的原理

如何使用?

$cond:[bool鍵/值,值1,值2]
//如果條件爲true,則返回值1,否則返回值2

實例:查找語文成績大於80的學生返回優秀否則良好,判斷是否大於60分如果大於60爲良否則爲不及格

db.stus.aggregate({
	
	$project:{
		
		pass:{
		
			$cond:[
		
				{$gt:['chinese',80]},
				
				'優秀',
				'良好'
			$cond:[
			
				{$gt:['$chinese',60]},


				'良',
				'不及格'


			]
			]
		}
	}
})

$ifNull 專門用於在值爲null時自動提供默認值

$ifNull:["$鍵"/值,替換值]

$group

什麼是?

分組就是將前一步提取出的鍵值,按指定範圍分組

何時使用?

專門用於分組統計

作用?

將前一步提取出的鍵值,按指定範圍分組

如何使用?

選擇了要分組的鍵,就要將鍵交給$group分組的_id鍵

{$group:{_id:"$鍵"/值}}

也可以按照多個鍵分組

{$group:{_id:{別名1:"$鍵1",別名:"$鍵2"}}}

實例:

//分組統計,按部門統計
db.tasks.aggregate({
	$group:{
		_id:'$dept'
	}
})

算數操作符

什麼是?

    算數操作符專門對分組後的鍵值執行算數操作

包括?

    $sum求和,$avg求平均

如何使用?

{$group:{_id:"$鍵"/表達式,別名:{$算數操作符:"$鍵"/值}}}

$sum

作用:

    既可以統計數量,又可以求和:

    $sum:"$鍵"/值,對給定的鍵值或者表達式的值求和

    $sum:1,表示吧分組內的每條記錄都當做1,這就相當於統計個數

    (沒有$count,$sum:1就相當於統計個數)

實例:

//分組統計,按部門統計,通過$sum:1求和並保存在$group中
db.tasks.aggregate({
	$group:{
		_id:'$dept',
		count:{$sum:1}
	}
})

$age

作用:

    專門計算平均數。相當於對數組內的數據求和以後,再除以組內的文檔個數

如何使用?

$avg:"$鍵"/值

值爲null或者不包含計算鍵的文檔,不包括在計算平均的分母中

實例:

//$avg計算平均值
db.scores.aggregate({
	$project:{
		class:1,	//需要班級
		score:1		//需要成績
	},//聚合操作
	//按照班級進行分組
	},
{
	$group:{
		_id:'$class',
		//計算成績的平均值
		avgScore:{
			$age:'$score'
		}
	}
}
})

極值操作符

作用:

    極值操作符專門用於獲得一個分組範圍內的最大值,最小值,第一個值和最後一個值

包括:

    $max最大值,$min最小值,$first分組中第一個值,$last分組中最後一個值

    注:其中$first和$last只有在配合排序操作時纔有意義

強調:

    值爲null或者不包含計算鍵的文檔,不參與最大值或者最小值的比較

    多數情況下,$max和$min效果上等效於先排序再獲得$first和$last,但是$max和$min顯然效率更高

$sort

作用:專門負責將統計的結果排序

如何使用

{$sort:{"$鍵1":1,"$鍵2":-1,...}}

值爲1 - 升序排列,值爲-1 - 降序排列

注:

    先後按照多個鍵排序時,生成的_id鍵是一個內嵌文檔,多個分組的鍵都是內嵌文檔的鍵。所以,排序時要用.運算符進入_id鍵

的內部,才能訪問到排序的兩個鍵

實例:

//從tasks集合中,按照部門分組,同時希望降序並且分頁查詢,一頁5調數據
var page = 1; //分頁
ab.tasks.aggregate({
	{	$group:{
			_id:'$dept'	//按照部門排序
	}
	},
	{
		$sort:{_id:-1}	        //降序排列
	},
	{
		$skip:(page-1)*5	//每次跳過(page-1)*5條數據
	},
	{
		$limit:5	        //5條顯示
	}
})
//0 - 5
//5 - 10
//10 - 15

注意:

    $sort可以按照需求寫在分組前,或者分組後,但是必須符合操作的邏輯:

        比如:獲得tasks集合中,每個部門最新的一項任務編號:就應該先排序,再獲得任務編號

        再比如:統計每個班的最高分,按照最高分降序排列:必須先統計出最高分,才能按統計結果的最高分排序

$limit和$skip

$limit:

    專門限制返回結果的個數,只接受一個數字n作爲值。表示僅返回n個結果

$skip:

    專門制定跳過的記錄格式。只接受一個數字n做爲跳過的記錄數

強調:

    管道操作講究順序,$skip操作必須在$limit操作之前

    和查詢中的skip操作一樣,聚合中的$skip同樣需要先查詢出所有,再丟棄,所以效率比較低

實例同上,結合

$unwind

作用:

    專門用於提取文檔中的內嵌再數組類型鍵內部的子文檔

何時:

    當希望將所有文檔內的內嵌再數組中的子文檔當做普通文檔看待,並統計時都要先用$unwind提取出來


實例:將weixin中的_id進行降序

if(db.getCollectionNames().indexOf("weixin")!=-1)
    db.weixin.drop();
for(var i=0,posts=[];i<10000;i++){   
    posts.push({     
	uname:"user"+parseInt(Math.random()*(90-10+1)+10),     
	content:"you jump I jump"+i,     
	comments:[
	    {uname:"user"+parseInt(Math.random()*(90-10+1)+10),         
	      content:Math.random()<0.5? "you can you up":"no zuo no die",       
	    },      
	    {uname:"user"+parseInt(Math.random()*(90-10+1)+10),         
	      content:Math.random()<0.5?"you can you up":"no zuo no die", 
	    },     
	]   
     }); 
} 
db.weixin.insert(posts);

db.weixin.aggregate({
{$unwind:'$comments'},	//轉爲普通文檔格式進行分組
{
	$group:{
		_id:'$comments.user',
		count:{$sum:1}		//求和
	}
},{

	$sort:{count:-1} //做降序
}
})

MapReduce

原理:

    用JavaScript執行彙總和統計的過程

階段:

    1、Map階段:用JavaScript對每個傳入的文檔內容按照指定邏輯計算得出一個結論

    2、Reduce階段:還是用JavaScript將Map階段爲每個文檔計算出的結果彙總,統計出最終結果

優點:

    有助於將複雜問題分段簡化,反而降低了聚合運算的難度

實例:第一個MapReduce操作:

    需求:統計所有文檔中,a和b兩個鍵值的和

    第一步:定義map階段的任務函數

db.nums.drop();
for(var i=0,arr=[];i<10;i++){

  arr[i]={

    a:parseInt(Math.random()*9+1),

    b:parseInt(Math.random()*9+1)

  }

}

db.nums.insert(arr);


//自定義map
function map(){
	//針對集合中的每個文檔中的a和b求和
	var subTotal = this.a + this.b;
	emit('subTotal',subTotal);
}
//執行完map會產生一箇中間集合
//[{subTotal:7},{subTotal:13}等等]

第二步:定義reduce

db.nums.drop();
for(var i=0,arr=[];i<10;i++){

  arr[i]={

    a:parseInt(Math.random()*9+1),

    b:parseInt(Math.random()*9+1)

  }

}

db.nums.insert(arr);


//自定義map
function map(){
	//針對集合中的每個文檔中的a和b求和
	var subTotal = this.a + this.b;
	emit('subTotal',subTotal);
}
//執行完map會產生一箇中間集合
//[{subTotal:7},{subTotal:13}等等]

//自定義reduce
function reduce(key,emites){
//key:subTatle
//emits:真正的數據->中間集合
//計算總和
	var total = value.reduce(/*箭頭函數*/(prev,elem)=>{
		return prev+elem;	
	})
		return total;
}

第三步:定義reduce階段的任務函數:

db.nums.drop();
for(var i=0,arr=[];i<10;i++){

  arr[i]={

    a:parseInt(Math.random()*9+1),

    b:parseInt(Math.random()*9+1)

  }

}

db.nums.insert(arr);


//自定義map
function map(){
	//針對集合中的每個文檔中的a和b求和
	var subTotal = this.a + this.b;
	emit('subTotal',subTotal);
}
//執行完map會產生一箇中間集合
//[{subTotal:7},{subTotal:13}等等]

//自定義reduce
function reduce(key,value){
//key:subTatle
//value:真正的數據->中間集合
//計算總和
	var total = value.reduce(/*箭頭函數*/(prev,elem)=>{
		return prev+elem;	
	})
		return total;
}
//mapReduce 調用系統提供方法
ab.nums.mapReduce(map,reduce,{out:'nums_total'/*將綜合輸出到nums_total*/});
//創建了一個集合nums_total
//去集合中看結果

關於mapReduce返回的結果對象:


發佈了46 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章