Mongo计算难点总结归纳

参考文献:

《MongoDB分组统计》

《简化MongonDB的关联运算》

《玩转MongoDB运算》

MonagoDB属于NoSql中的基于分布式文件存储的文档型数据库,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,但是写起来并不简单。若能集算器SPL语言结合,处理起来就相对容易多了。

现在我们针对MongoDB在计算方面的问题进行讨论分析,通过集算器SPL语言加以改进,方便用户使用MongoDB。现从如下情况加以说明:

1. 单表内嵌数组结构的统计................................................. 1

2. 单表内嵌文档求和....................................................... 3

3. 分段分组结构:.......................................................... 5

4. 同构表合并............................................................. 6

5. 关联嵌套结构情况1...................................................... 8

6. 关联嵌套结构情况2..................................................... 10

7. 关联嵌套结构情况3..................................................... 11

8. 多字段分组统计........................................................ 14

9. 两表关联查询.......................................................... 16

10.多表关联查询.......................................................... 17

11.指定数组查找.......................................................... 19

12.关联表中的数组查找.................................................... 20

 

  1. 单表内嵌数组结构的统计

对嵌套数组结构中的数据统计处理查询考试科目的平均分及每个学生的总成绩情况。
 

测试数据:

_id

name

sex

Scroe

1

Tom

F

[{"lesson":" Physics", "mark":60 },
{"lesson":" Chemical", "mark":72 }]

2

Jerry

M

[{"lesson":" Physics", "mark":92 },
{"lesson":" Math", "mark":81 }]

 

期待统计结果:

Physics

76

 

Tom

132

Chemical

72

 

Jerry

173

Math

81

 

 

 

 

 

Mongodb脚本:

db.student.aggregate( [
{ $unwind : "$scroe"},

{$group: {

  "_id": {"lesson":"$scroe.lesson"} ,

  "qty":{"$avg": "$scroe.mark"}

  }

}

] )

db.student.aggregate( [
{ $unwind : "$scroe" },

{$group: {

  "_id": {"name" :"$name"} ,

  "qty":{"$sum" : "$scroe.mark"}

  }

}

 ] )

由于各科分数scroe是按课目、成绩记录的数组结构,统计前需要将它拆解,将每科成绩与学生对应,然后再实现分组计算。这需要熟悉unwind与group组合的应用。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"student.find()").fetch()

 

3

=A2.conj(scroe).groups(lesson:LESSON;avg(mark):AVG)

 

4

=A2.new(name:NAME,scroe.sum(mark):TOTAL)

 

5

>A1.close()

 

 

按课目统计的总分数

LESSON

AVG

Chemical

72.0

Math

81.0

Physics

76.0

 

每个学生的总成绩                                                                                                           

NAME

TOTAL

Tom

132

Jerry

173

 

脚本说明:

A1:连接mongo数据库。

A2:获取student表中的数据。

A3:将scroe数据合并成序表,再按课程分组,计算平均分。

A4:统计每个学生的成绩后返回列名为NAME、TOTAL的序表。new函数表示生成新序表。

A5:关闭数据库连接。

 

这个比较常用嵌套结构统计的例子许多人遭遇过、需要先拆解,主要是熟悉mongodb对嵌套数据结构的处理。

 

2. 单表内嵌文档求和

对内嵌文档中的数据求和处理, 下面要统计每条记录的income,output的数量和。

 

测试数据:

_id

income

output

1

{"cpu":1000, "mem":500, "mouse":"100"}

{"cpu":1000, "mem":600 ,"mouse":"120"}

2

{"cpu":2000, "mem":1000,
"mouse":"50","mainboard":500 }

{"cpu":1500, "mem":300 }

 

期待统计结果

_id

income

output

1

1600

1720

2

3550

1800

 

Mongodb脚本:

var fields = [ "income", "output" ];

db.computer.aggregate([ 

   { 

      $project:{ 

         "values":{ 

            $filter:{ 

               input:{ 

                  "$objectToArray":"$$ROOT"

               },

               cond:{ 

                  $in:[ 

                     "$$this.k",

                     fields

                  ]

               }

            }

         }

      }

   },

   { 

      $unwind:"$values"

   },

   { 

      $project:{ 

         key:"$values.k",

         values:{ 

            "$sum":{ 

               "$let":{ 

                  "vars":{ 

                     "item":{  

                        "$objectToArray":"$values.v"

                     }

                  },

                  "in":"$$item.v"

               }

            }

         }

      }

   },

   {$sort: {"_id":-1}},

   { "$group": {

    "_id": "$_id",

    'income':{ "$first": "$values" },

    "output":{ "$last": "$values" }

    }},

]);

filter将income,output部分信息存放到数组中,用unwind拆解成记录,再累计各项值求和,按_id分组合并数据。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"computer.find()").fetch()

 

3

=A2.new(_id:ID,income.array().sum():INCOME,output.array().sum():OUTPUT)

 

4

>A1.close()

 

 

统计结果

ID

INCOME

OUTPUT

1

1600.0

1720.0

2

3550.0

1800.0

 

脚本说明:

A1:连接数据库

A2:获取computer表中的数据

A3:将income、output字段中的数据分别转换成序列求和,再与ID组合生成新序表

A4:关闭数据库连接。

 

获取子记录的字段值,然后求和,相对于mongo脚本简化了不少。这个内嵌文档与内嵌数组在组织结构上有点类似,不小心容易混淆,注意与上例中的scroe数组结构比较,写出的脚本有所不同。

3. 分段分组结构

统计各段内的记录数量。下面按销售量分段,统计各段内的数据量,数据如下:

_id

NAME

STATE

SALES

1

Ashley

New York

11000

2

Rachel

Montana

9000

3

Emily

New York

8800

4

Matthew

Texas

8000

5

Alexis

Illinois

14000

 

分段方法:0-3000;3000-5000;5000-7500;7500-10000;10000以上。

期望结果:

Segment

number

3

3

4

2

 

Mongo脚本

var a_count=0;

var b_count=0;

var c_count=0;

var d_count=0;

var e_count=0;

db.sales.find({

   

}).forEach(

    function(myDoc) {

        if (myDoc.SALES <3000) {

            a_count += 1;

        }

        else if (myDoc.SALES <5000) {

            b_count += 1;

        }

        else if (myDoc.SALES <7500) {

            c_count += 1;

        }

        else if (myDoc.SALES <10000) {

            d_count += 1;

        }

        else {

            e_count += 1;

        }       

    }

    );

   

print("a_count="+a_count)

print("b_count="+b_count)

print("c_count="+c_count)

print("d_count="+d_count)

print("e_count="+e_count)

这个需求按条件分段分组,mongodb没有提供对应的api,实现起来有点繁琐,上面的程序是其中实现的一个例子参考,当然也可以写成其它实现形式。下面看看集算器脚本的实现。

 

SPL脚本:

 

A

B

1

[3000,5000,7500,10000,15000]

 

2

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

3

=mongo_shell(A2,"sales.find()").fetch()

 

4

=A3.groups(A1.pseg(int(~.SALES)):Segment;count(1): number)

 

5

>A2.close()

 

 

脚本说明:

A1:定义SALES分组区间。

A2:连接mongodb数据库。

A3:获取sales表中的数据。

A4:根据SALES区间分组统计员工数。其中函数pseg()表示返回成员在序列中的区段序号,int()表示转换成整数。

A5:关闭数据库连接。

 

    pseg的使用让SPL脚本精简了不少。

4. 同构表合并

具有相同结构的多表数据合并。下面将两个员工表数据合并。

Emp1:

_id

NAME

STATE

HIREDATE

DEPT

SALARY

1

Ashley

New York

2008-03-16

Finance

11000

2

Rachel

Michigan

2001-04-16

Sales

9000

3

Emily

New York

2011-07-11

HR

8800

4

Matthew

Texas

2003-03-06

R&D

8000

5

Alexis

Illinois

2008-03-10

Sale

14000

 

Emp2:

_id

NAME

STATE

HIREDATE

DEPT

SALARY

10

Jacob

New York

2009-03-14

Sales

13000

12

Jessica

Florida

2011-04-19

Sales

9500

13

Daniel

New York

2001-02-11

HR

7800

14

Alyssa

Montana

2013-09-06

R&D

8000

15

Hannah

Florida

2015-06-10

Sales

12500

 

合并数据结果:

_id

NAME

STATE

HIREDATE

DEPT

SALARY

1

Ashley

New York

2008-03-16

Finance

11000

2

Rachel

Michigan

2001-04-16

Sales

9000

3

Emily

New York

2011-07-11

HR

8800

4

Matthew

Texas

2003-03-06

R&D

8000

5

Alexis

Illinois

2008-03-10

Sale

14000

10

Jacob

New York

2009-03-14

Sales

13000

12

Jessica

Florida

2011-04-19

Sales

9500

13

Daniel

New York

2001-02-11

HR

7800

14

Alyssa

Montana

2013-09-06

R&D

8000

15

Hannah

Florida

2015-06-10

Sales

12500

 

 

Mongo脚本:

db.emp1.aggregate([

  { "$limit": 1 },

  { "$facet": {

    "collection1": [

      { "$limit": 1 },

      { "$lookup": {

        "from": " emp1",

        "pipeline": [{ "$match": { } }],

        "as": "collection1"

      }}

    ],

    "collection2": [

      { "$limit": 1 },

      { "$lookup": {

        "from": "emp2",

        "pipeline": [{ "$match": { } }],

        "as": "collection2"

      }}

    ]

  }},

  { "$project": {

    "data": {

      "$concatArrays": [

        { "$arrayElemAt": ["$collection1.collection1", 0] },

        { "$arrayElemAt": ["$collection2.collection2", 0] },

      ]

    }

  }},

  { "$unwind": "$data" },

  { "$replaceRoot": { "newRoot": "$data" } }

])

通过facet将两表数据先存入各自的数组中,然后concatArrays将数组合并,unwind拆解子记录后,并将它呈现在最外层。SPL脚本实现则没有那么多“花样”。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"emp1.find()").fetch()

 

3

=mongo_shell(A1,"emp2.find()").fetch()

 

4

=A2|A3

 

5

>A1.close()

 

 

脚本说明:

A1:连接mongodb数据库。

A2:获取emp1表中的数据。

A3:获取emp2表中的数据。

A4:合并两表数据。

A5:关闭数据库连接。

 

熟悉sql语句的mongo初学者面对数据合并的mongo脚本,估计首次遇到时有点“懵”,SPL脚本就显得自然易懂了。

 

5. 关联嵌套结构情况1

两个关联表,表A与表B中的内嵌文档信息关联,且返回的信息在内嵌文档中。表childsgroup字段childs是嵌套数组结构,需要合并的信息name在其下。

 

测试数据:

history:

_id

id

History

child_id

1

001

today worked

ch001

2

002

Working

ch004

3

003

now working

ch009

 

childsgroup:

_id

groupid

name

childs

1

g001

group1

{"id":"ch001","info":{"name":"a"}},

{"id":"ch002","info":{"name":"b"}}

2

g002

group1

{"id":"ch004","info":{"name":"c"}},

{"id":"ch009","info":{"name":"d"}}

 

表History中的child_id与表childsgroup中的childs.id关联,希望得到下面结果:

{

    "_id" : ObjectId("5bab2ae8ab2f1bdb4f434bc3"),

    "id" : "001",

    "history" : "today worked",

    "child_id" : "ch001",

    "childInfo" :

    {

         "name" : "a"

    }

………………

}

 

Mongo脚本

db.history.aggregate([

    {$lookup: {

        from: "childsgroup",

        let: { child_id: "$child_id" },

        pipeline: [

            { $match: { $expr: { $in: [ "$$child_id", "$childs.id" ] } } },

            { $unwind: "$childs" },

            { $match: { $expr: { $eq: [ "$childs.id", "$$child_id" ] } } },

            { $replaceRoot: { newRoot: "$childs.info" } }

            ],

            as: "childInfo"

        }},
{ "$unwind": "$childInfo" }

])

这个脚本用了几个函数lookup、pipeline、match、unwind、replaceRoot处理,一般mongodb用户不容易写出这样复杂脚本;那我们再看看spl脚本的实现:

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"history.find()").fetch()

 

3

=mongo_shell(A1,"childsgroup.find()").fetch()

 

4

=A3.conj(childs)

 

5

=A2.join(child_id,A4:id,info.name:name)

 

6

>A1.close()

 

 

关联查询结果:

_id

id

history

child_id

name

1

001

today worked

ch001

a

2

002

working

ch004

c

3

003

now working

ch009

d

脚本说明:

A1:连接mongodb数据库。

A2:获取history表中的数据。

A3:获取childsgroup表中的数据。

A4:将childsgroup中的childs数据提取出来合并成序表。

A5:表history中的child_id与表childs中的id关联查询,追加name字段, 返回序表。

A6:关闭数据库连接。

 

相对mongodb脚本写法,SPL脚本的难度降低了不少,省去了熟悉有关mongo函数的用法,如何去组合处理数据等,节约了不少时间。

 

6. 关联嵌套结构情况2

两个关联表,表A与表B中的内嵌文档信息关联,将信息合并到内嵌文档中。表txtPost

字段comment是嵌套数组结构,需要把comment_content合并到其下。

 

txtComment:

_ID

comment_no

comment_content

1

143

test test

2

140

math

txtPost

_ID

post_no

Comment

1

48

[{"comment_no" : 143, "comment_group" : 1} ]

2

47

[{"comment_no" : 140, "comment_group" : 2}
{"comment_no" : 143, "comment_group" : 3} ]

 

期望结果:

_ID

post_no

Comment

1

48

[{"comment_no" : 143, "comment_group" : 1"comment_content" : "test test"} ]

2

47

[{"comment_no" : 140, "comment_group" : 2"comment_content" : "math"}
{"comment_no" : 143, "comment_group" : 3,
"comment_content" : "test test"} ]

 

 

Mongo脚本

db.getCollection("txtPost").aggregate([

  { "$unwind": "$comment" },

  { "$lookup": {

    "from": "txtComment",

    "localField": "comment.comment_no",

    "foreignField": "comment_no",

    "as": "comment.comment_content"

  }},

  { "$unwind": "$comment.comment_content" },

  { "$addFields": { "comment.comment_content": "$comment.comment_content.comment_content" }},

  { "$group": {

    "_id": "$_id",

    'post_no':{ "$first": "$post_no" },

    "comment": { "$push": "$comment" }

    }},

 

  ]).pretty()

 

表txtPost按comment拆解成记录,然后与表txtComment关联查询,将其结果放到数组中,再将数组拆解成记录,将comment_content值移到comment下,最后分组合并。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"txtPost.find()").fetch()

 

3

=mongo_shell(A1,"txtComment.find()").fetch()

 

4

=A2.conj(comment.derive(A2.post_no:pno))

 

5

=A4.join(comment_no,A3:comment_no,comment_content:Content)

 

6

=A5.group(pno;~:comment)

 

7

>A1.close()

 

脚本说明:

A1:连接mongodb数据库。

A2:获取txtPost表中的数据。

A3:获取txtComment表中的数据。

A4:将序表A2下的comment与post_no组合成序表,其中post_no改名为pno。

A5:序表A4通过comment_no与序表A3关联,追加字段comment_content,将其改名为Content。

A6:按pno分组返回序表,~表示当前记录。

A7:关闭数据库连接。

 

7. 关联嵌套结构情况3

两个关联表,表A与表B中的内嵌文档信息关联,且返回的信息在记录上。表collection2字段product是嵌套数组结构,返回的信息是isCompleted等字段。

 

测试数据:

collection1:

{

   _id: '5bc2e44a106342152cd83e97',

   description:

    {

      status: 'Good',

      machine: 'X'

     },

   order: 'A',

   lot: '1'

   };

  

collection2:

{

   _id: '5bc2e44a106342152cd83e80',

   isCompleted: false,

   serialNo: '1',

   batchNo: '2',

   product: [ // note the subdocuments here

        { order: 'A', lot: '1' },

        { order: 'A', lot: '2' }

    ]

}

 

期待结果

{

   _id: 5bc2e44a106342152cd83e97,

   description:

       {

         status: 'Good',

         machine: 'X',

       },

   order: 'A',

   lot: '1' ,

   isCompleted: false,

   serialNo: '1',

   batchNo: '2'

}

 

Mongo脚本

db.collection1.aggregate([{

       $lookup: {

              from: "collection2",

              let: { order: "$order", lot: "$lot" },

              pipeline: [{

                     $match: {

                     $expr:{ $in: [ { order: "$$order", lot: "$$lot" }, "$product"] }

                     }

                     }],

                     as: "isCompleted"

                     }

              }, {

                     $addFields: {

                     "isCompleted": { $arrayElemAt: [ "$isCompleted", 0 ] }

                     }

              }, {

                     $addFields: { // add the required fields to the top level structure

                     "isCompleted": "$isCompleted.isCompleted",
                     "serialNo": "$isCompleted.serialNo",

                     "batchNo": "$isCompleted.batchNo"

              }

}])

lookup两表关联查询,首个addFields获取isCompleted数组的第一个记录,后一个addFields转换成所需要的几个字段信息

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"collection1.find()").fetch()

 

3

=mongo_shell(A1,"collection2.find()").fetch()

 

4

=A3.conj(A2.select(order:A3.product.order,lot:A3.product.lot).derive(A3.serialNo:sno,A3.batchNo:bno))

 

5

>A1.close()

 

脚本说明:

A1:连接mongodb数据库。

A2:获取collection1表中的数据。

A3:获取collection2表中的数据。

A4:根据条件order, lot从序表A2中查询记录,然后追加序表A3中的字段serialNo, batchNo,返回合并后的序表。
       A5:关闭数据库连接。

 

实现从数据记录中的内嵌结构中筛选,将符合条件的数据合并成新序表。

 

8. 多字段分组统计

统计分类项下的总数及各子项数。下面统计按addr分类book数及其下不同的book数。

addr

book

address1

book1

address2

book1

address1

book5

address3

book9

address2

book5

address2

book1

address1

book1

address15

book1

address4

book3

address5

book1

address7

book11

address1

book1

 

期望结果:

_id

Total

books

Count

address1

4

book1

3

 

 

book5

1

address15

1

book1

1

address2

3

book1

2

 

 

book5

1

address3

1

book9

1

address4

1

book3

1

address5

1

book1

1

address7

1

book11

1

 

Mongo脚本

db.books.aggregate([

    { "$group": {

        "_id": {

            "addr": "$addr",

            "book": "$book"

        },

        "bookCount": { "$sum": 1 }

    }},

    { "$group": {

        "_id": "$_id.addr",

        "books": {

            "$push": {

                "book": "$_id.book",

                "count": "$bookCount"

            },

        },

        "count": { "$sum": "$bookCount" }

    }},

    { "$sort": { "count": -1 } },

    { "$project": {

        "books": { "$slice": [ "$books", 2 ] },

        "count": 1

    }}

]).pretty()

先按addr,book分组统计book数,再按addr分组统计book数,调整显示顺序

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"books.find()")

 

3

=A2.groups(addr,book;count(book): Count)

 

4

=A3.groups(addr;sum(Count):Total)

 

5

=A3.join(addr,A4:addr,Total)

 

6

>A1.close()

 

 

计算结果:

Address

book

Count

Total

address1

book1

3

4

address1

book5

1

4

address15

book1

1

1

address2

book1

2

3

address2

book5

1

3

address3

book9

1

1

address4

book3

1

1

address5

book1

1

1

address7

book11

1

1

 

脚本说明:

A1:连接mongodb数据库。

A2:获取books表中的数据。

A3:按addr,book分组统计book数,
A4:再按addr分组统计book数。
A5:将A4中的Total按addr关联后合并到序表中.

A6:关闭数据库连接。

 

9. 两表关联查询

从关联表中选择所需要的字段组合成新表。

Collection1:

user1

user2

income

1

2

0.56

1

3

0.26

 

collection2:

user1

user2

output

1

2

0.3

1

3

0.4

2

3

0.5

期望结果:

user1

user2

income

output

1

2

0.56

0.3

1

3

0.26

0.4

 

Mongo脚本

db.c1.aggregate([

    { "$lookup": {

    "from": "c2",

        "localField": "user1",

        "foreignField": "user1",

        "as": "collection2_doc"

    }},

    { "$unwind": "$collection2_doc" },

    { "$redact": {

        "$cond": [

            { "$eq": [ "$user2", "$collection2_doc.user2" ] },

            "$$KEEP",

            "$$PRUNE"

        ]

    }},

    { "$project": {

        "user1": 1,

        "user2": 1,

        " income ": "$income",

        " output ": "$collection2_doc. output "

    }}

    ]).pretty()

lookup两表进行关联查询,redact对记录根据条件进行遍历处理,project选择要显示的字段。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"c1.find()").fetch()

 

3

=mongo_shell(A1,"c2.find()").fetch()

 

4

=A2.join(user1:user2,A3:user1:user2,output)

 

5

>A1.close()

 

脚本说明:

A1:连接mongodb数据库。

A2:获取c1表中的数据。

A3:获取c2表中的数据。
A4:两表按字段user1,user2关联,追加序表A3中的output字段,返回序表。
A5:关闭数据库连接。

 

通过join把两个关联表不同的字段合并成新表。

 

10. 多表关联查询

多于两个表的关联查询,结合成一张大表。

Doc1:

_id

firstName

lastName

U001

shubham

verma

 

Doc2:

_id

userId

address

mob

2

U001

Gurgaon

9876543200

 

Doc3:

_id

userId

fbURLs

twitterURLs

3

U001

http://www.facebook.com

http://www.twitter.com

 

合并后的结果:

{

    "_id" : ObjectId("5901a4c63541b7d5d3293766"),

    "firstName" : "shubham",

    "lastName" : "verma",

 

    "address" : {

        "address" : "Gurgaon"

    },

    "social" : {

        "fbURLs" : "http://www.facebook.com",

        "twitterURLs" : "http://www.twitter.com"

    }

}

 

 

Mongo脚本

db.doc1.aggregate([

    { $match: { _id: ObjectId("5901a4c63541b7d5d3293766") } },

    {

        $lookup:

        {

            from: "doc2",

            localField: "_id",

            foreignField: "userId",

            as: "address"

        }

    },

    {

        $unwind: "$address"

    },

    {

        $project: {

            "address._id": 0,

            "address.userId": 0,

            "address.mob": 0

        }

    },

    {

        $lookup:

        {

            from: "doc3",

            localField: "_id",

            foreignField: "userId",

            as: "social"

        }

    },

    {

        $unwind: "$social"

    },

 

  {  

    $project: {     

           "social._id": 0,     

           "social.userId": 0

       }

 }

]).pretty();

由于Mongodb数据结构原因,写法也多样化,展示也各不相同。

 

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"doc1.find()").fetch()

 

3

=mongo_shell(A1,"doc2.find()").fetch()

 

4

=mongo_shell(A1,"doc3.find()").fetch()

 

5

=A2.join(_id,A3:userId,address,mob)

 

6

=A5.join(_id,A4:userId,fbURLs,twitterURLs)

 

7

>A1.close()

 

此脚本与上面例子类似,只是多了一个关联表,每次join就新增加字段,最后叠加构成一张大表。.

SPL脚本的简洁性、统一性就非常明显。

  1. 指定数组查找

从指定的数组中查找符合条件的记录。所给的数组为:["Chemical", "Biology", "Math"]。

 

测试数据:

_id

Name

Lesson

1

jacker

[English, Chemical,Math, Physics]

2

tom

[Chinese, Chemical,Math, Biology]

3

Mint

[Chinese, History]

 

期望结果:

_id

Name

Lesson

1

Jacker

[Chemical,Math]

2

Tom

[Chemical,Math,Biology]

 

Mongodb脚本

var field = ["Chemical", "Biology", "Math"]

db.student.aggregate([

  { "$project": {

    "name":1,

    "lessons": {

      "$filter": {

        "input": "$lesson",

        "cond": {

          "$in": [

            "$$this",

            field

          ]

        }

      }

      },

    }},

    { "$project": {"name":1,"lessons":1,"sizeOflesson": {"$size": "$lessons"} }},

    { $match: { "sizeOflesson":{ $gt: 0}}}

])

 

查询选修课包含["Chemical", "Biology", "Math"]的同学。

 

SPL脚本:

 

A

B

1

[Chemical, Biology, Math]

 

2

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

3

=mongo_shell(A2,"student.find()").fetch()

 

4

=A3.select(lesson^A1!=[])

 

5

=A4.new(name, ~.lesson^A1)

 

6

>A2.close()

 

脚本说明:

A1:定义查询条件科目数组。

A2:连接mongodb数据库。

A3:获取student表中的数据。

A4:查询存在数组中的科目记录。
       A5:生成字段为name, lesson的新序表,其中符合条件的值存放在字段lesson中

A6:关闭数据库连接。

 

集算器对给定数组中查询记录的实现更简明易懂

 

11. 关联表中的数组查找

从关联表记录数据组中查找符合条件的记录, 用给定的字段组合成新表。

测试数据:

users:

_id

Name

workouts

1000

xxx

[2,4,6]

1002

yyy

[1,3,5]

workouts:

_id

Date

Book

1

1/1/2001

Othello

2

2/2/2001

A Midsummer Night's Dream

3

3/3/2001

The Old Man and the Sea

4

4/4/2001

GULLIVER’S TRAVELS

5

5/5/2001

Pickwick Papers

6

6/6/2001

The Red and the Black

期望结果:

Name

_id

Date

Book

xxx

2

2/2/2001

A Midsummer Night's Dream

xxx

4

4/4/2001

GULLIVER’S TRAVELS

xxx

6

6/6/2001

The Red and the Black

yyy

1

1/1/2001

Othello

yyy

3

3/3/2001

The Old Man and the Sea

yyy

5

5/5/2001

Pickwick Papers

 

 

Mongo脚本

db.users.aggregate([

  { "$lookup": {

    "from" : "workouts",

    "localField" : "workouts",

    "foreignField" : "_id",

    "as" : "workoutDocumentsArray"

  }},

  { $project: { _id:0,workouts:0} } ,

  { "$unwind": "$workoutDocumentsArray" },

 

  { "$replaceRoot": { "newRoot":  { $mergeObjects: [ "$$ROOT", "$workoutDocumentsArray" ] } } },

  { $project: { workoutDocumentsArray: 0} }

  ]).pretty()

 

把关联表users,workouts查询结果放到数组中,再将数组拆解,提升子记录的位置,去掉不需要的字段。

SPL脚本:

 

A

B

1

=mongo_open("mongodb://127.0.0.1:27017/raqdb")

 

2

=mongo_shell(A1,"users.find()").fetch()

 

3

=mongo_shell(A1,"workouts.find()").fetch()

 

4

=A2.conj(A3.select(A2.workouts^~.array(_id)!=[]).derive(A2.name))

 

5

>A1.close()

 

脚本说明:

A1:连接mongodb数据库。

A2:获取users表中的数据。

A3:获取workouts表中的数据。

A4:查询序表A3的_id值存在于序表A2中workouts数组的记录, 并追加name字段。
返回合并的序表。

A5:关闭数据库连接。

 

由于需要获取序列的交集不为空为条件,故将_id转换成序列。

 

Mongo存储的数据结构相对关联数据库更复杂、更灵活,其提供的查询语言也非常强、能适应不同的情况,需要了解函数也不少,函数之间的结合更是变化无穷,因此要掌握并熟悉应用它并非易事。集算器的离散性、易用性恰好能弥补Mongo这方面的不足,它降低了mongo学习成本及使用mongo操作的复杂度、难度,让mongo的功能得到更充分的展现,同时也希望mongo越来越受到广大爱好者的青睐。

 

参考文献:

《MongoDB分组统计》

《简化MongonDB的关联运算》

《玩转MongoDB运算》

 

 

你可能感兴趣的:(MongoDB,分组,关联,嵌套,数组)