MongoDB索引方面的学习实践
文章发布较早,内容可能过时,阅读注意甄别。
cmdb 使用 MongoDB 作为底层数据存储,为了便于检索,本文将探讨研究一些 MongoDB 的索引使用。
# 准备数据
在开始学习了解之前,先准备一些测试数据如下:
db.datas.insert(
[
{"datas_identify":"eryajf","data":[{"name":"aaa","identify":"aaa-1","create_time":"2020-01-01"}]},
{"datas_identify":"eryajf","data":[{"name":"bbb","identify":"bbb-1","create_time":"2021-01-01"}]},
{"datas_identify":"eryajf","data":[{"name":"ccc","identify":"ccc-1","create_time":"2022-01-01"}]},
{"datas_identify":"eryajf","data":[{"name":"ddd","identify":"ddd-1","create_time":"2023-01-01"}]},
{"datas_identify":"eryajf","data":[{"name":"eee","identify":"eee-1","create_time":"2024-01-01"}]},
{"datas_identify":"liql","data":[{"name":"fff","identify":"fff-1","create_time":"2024-01-01"}]},
{"datas_identify":"liql","data":[{"name":"ggg","identify":"ggg-1","create_time":"2026-01-01"}]},
{"datas_identify":"liql","data":[{"name":"hhh","identify":"hhh-1","create_time":"2027-01-01"}]},
{"datas_identify":"liql","data":[{"name":"iii","identify":"iii-1","create_time":"2028-01-01"}]},
{"datas_identify":"liql","data":[{"name":"aaa","identify":"aaa-1","create_time":"2029-01-01"}]}])
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
常规情况下,我们可以针对具体字段进行查询:
db.getCollection('datas').find({"datas_identify":"eryajf"})
db.getCollection('datas').find({"data.name":"aaa"})
1
2
2
这种针对指定字段的查询没有问题,不过如果想要实现一个智能识别的,或者针对当前数据表能够全表搜索的,就需要用到索引了。
# 创建索引
MongoDB 可以对任意字段建立全文索引(text index),但需要注意:1 个 collection 中只能包含至多 1 个全文索引,且只能对 String 或 String 数组的字段建立文本索引。
我们可以通过如下命令创建一个文本索引:
db.datas.createIndex({datas_identify: "text"})
1
这个语句表示将datas_identify
字段添加为全文索引,当然也可以指定多个字段,方法如下:
db.datas.createIndex({datas_identify: "text", name: "text"})
1
执行完毕之后,可以通过如下命令查看当前集合的索引:
$ db.datas.getIndexes()
/* 1 */
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"_fts" : "text",
"_ftsx" : 1
},
"name" : "datas_identify_text",
"weights" : {
"datas_identify" : 1
},
"default_language" : "english",
"language_override" : "language",
"textIndexVersion" : 3
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- 索引创建之后默认支持的语言是英文,需要注意,MongoDB 目前还不支持中文,点击这里 (opens new window)可以查看当前支持的语言列表。
- 当索引字段有多个的时候,可以通过 weights 字段控制索引字段的权重。
# 数据查询
查询的语句格式如下:
{
$text:
{
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>,
$diacriticSensitive: <boolean>
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
$search
:后面跟的是将要搜索的关键字。$language
:指定搜索的语言。$caseSensitive
:设置是否区分大小写。$diacriticSensitive
设置是否区别发音符号。
那么查询 datas_identify
为eryajf
的方式如下:
$ db.datas.find({ $text: {$search: "eryajf"}})
1
# 其他进阶
# 数组索引
MongoDB 可以给一个数组添加索引,从而提高指定数组的检索效率。
创建一个数组的索引:
$ db.datas.createIndex({"data.name":"text","data.identify":"text","datas_identify":"text"})
1
然后直接进行检索:
$ db.datas.find({ $text: {$search: "bbb"}})
/* 1 */
{
"_id" : ObjectId("621b7bbff00df89221ebebd6"),
"datas_identify" : "eryajf",
"data" : [
{
"name" : "bbb",
"identify" : "bbb-1",
"create_time" : "2021-01-01"
}
]
}
$ db.datas.find({ $text: {$search: "aaa"}})
/* 1 */
{
"_id" : ObjectId("621b7bbff00df89221ebebde"),
"datas_identify" : "liql",
"data" : [
{
"name" : "aaa",
"identify" : "aaa-1",
"create_time" : "2029-01-01"
}
]
}
/* 2 */
{
"_id" : ObjectId("621b7bbff00df89221ebebd5"),
"datas_identify" : "eryajf",
"data" : [
{
"name" : "aaa",
"identify" : "aaa-1",
"create_time" : "2020-01-01"
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
这是在明确知道数组内字段的情况下的方案,在一些实际应用场景中,一个数组内的字段有可能是不固定的,这种时候就没办法对指定字段进行索引了。这个时候就需要用到全文索引了。
# 全文索引
建立一个通配符全文索引的方法是:
$ db.datas.createIndex({"$**":"text"})
1
全文索引创建完毕之后,我们可以对整个集合进行检索。
测试效果如下:
$ db.datas.find({ $text: { $search: "aaa" } })
/* 1 */
{
"_id" : ObjectId("621b7bbff00df89221ebebde"),
"datas_identify" : "liql",
"data" : [
{
"name" : "aaa",
"identify" : "aaa-1",
"create_time" : "2029-01-01"
}
]
}
/* 2 */
{
"_id" : ObjectId("621b7bbff00df89221ebebd5"),
"datas_identify" : "eryajf",
"data" : [
{
"name" : "aaa",
"identify" : "aaa-1",
"create_time" : "2020-01-01"
}
]
}
# ============ #
$ db.datas.find({ $text: { $search: "2022" } })
/* 1 */
{
"_id" : ObjectId("621b7bbff00df89221ebebd7"),
"datas_identify" : "eryajf",
"data" : [
{
"name" : "ccc",
"identify" : "ccc-1",
"create_time" : "2022-01-01"
}
]
}
# ============ #
$ db.datas.find({ $text: { $search: "2024" } })
/* 1 */
{
"_id" : ObjectId("621b7bbff00df89221ebebda"),
"datas_identify" : "liql",
"data" : [
{
"name" : "fff",
"identify" : "fff-1",
"create_time" : "2024-01-01"
}
]
}
/* 2 */
{
"_id" : ObjectId("621b7bbff00df89221ebebd9"),
"datas_identify" : "eryajf",
"data" : [
{
"name" : "eee",
"identify" : "eee-1",
"create_time" : "2024-01-01"
}
]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 其他补充
基于全文索引的搜索,有一些需要注意的点,这里做一下记录。
如果查询中有通配符,使用如下方式转义
$ db.datas.find({ $text: { $search: "\"10.6.6.66\"" } })
1多条件逻辑或
$ db.datas.find({ $text: { $search: "2024 bbb" } })
1多条件逻辑与
$ db.datas.find({ $text: { $search: '"2024" "liql"' } })
1如果想要排除某个结果,则用
-
查询$ db.datas.find({ $text: { $search: '"2024" -liql' } }) /* 1 */ { "_id" : ObjectId("621b7bbff00df89221ebebd9"), "datas_identify" : "eryajf", "data" : [ { "name" : "eee", "identify" : "eee-1", "create_time" : "2024-01-01" } ] }
1
2
3
4
5
6
7
8
9
10
11
12
13golang 检索时的代码
type Data struct { DatasIdentify string `json:"datas_identify"` Data []struct { Name string `json:"name"` Identify string `json:"identify"` CreateTime string `json:"create_time"` } `json:"data"` } func FindTest() { filters := bson.D{} filter := bson.E{Key: "datas_identify", Value: "eryajf"} searchFilter := bson.E{Key: "$text", Value: bson.M{"$search": "2022"}} filters = append(filters, filter, searchFilter) datas, err := ListData(filters, options.FindOptions{}) if err != nil { fmt.Printf("get data failed: %v\n", err) } for _, v := range datas { fmt.Println(v) } } // ListData 获取数据列表 func ListData(filter bson.D, options options.FindOptions) ([]*Data, error) { table := DB.Collection("datas") cus, err := table.Find(ctx, filter, &options) if err != nil { fmt.Printf("find data failed: %v\n", err) } defer func(cus *mongo.Cursor, ctx context.Context) { err := cus.Close(ctx) if err != nil { return } }(cus, ctx) list := make([]*Data, 0) for cus.Next(ctx) { data := new(Data) err := cus.Decode(&data) if err != nil { fmt.Printf("decode data failed: %v\n", err) } list = append(list, data) } return list, nil }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
以上就是 MongoDB 中索引的一些实践,一些内容需要结合实际场景进行使用。
上次更新: 2024/11/19, 23:11:42