您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 15575|回复: 0
打印 上一主题 下一主题

[php学习资料] MongoDB高级查询用法大全

[复制链接]
跳转到指定楼层
楼主
发表于 2019-7-4 17:21:36 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
版本一:
3 u. ~& j- y$ V( G6 l' Z1 W4 L- M& R" t2 O$ |; a
1 ) . 大于,小于,大于或等于,小于或等于
: t: K4 m4 \. y' g  ~+ \3 u' r3 Y. V) ^8 C
$gt:大于
) P: l3 y+ D1 k& o0 P' p$lt:小于
0 q( L% p4 U# z0 p& C1 |* P6 ~. U$gte:大于或等于& E+ ]% C4 \1 ^
$lte:小于或等于
+ L8 H$ q& R9 p/ z0 R7 Z# g  v9 q6 S1 r: _; N
例子:
  1. db.collection.find({ "field" : { $gt: value } } );   // greater than  : field > value0 i; }; \+ ^. x# w- m- c
  2. db.collection.find({ "field" : { $lt: value } } );   // less than  :  field < value
    1 y) u& C4 F  {$ H! K2 D; k: `! e
  3. db.collection.find({ "field" : { $gte: value } } );  // greater than or equal to : field >= value. P3 E8 q: o" x6 C* u; w6 D+ h6 T
  4. db.collection.find({ "field" : { $lte: value } } );  // less than or equal to : field <= value
复制代码

. O0 X* v$ U* u* \( p
如查询j大于3,小于4:
  1. db.things.find({j : {$lt: 3}});
    : J( z* M( V3 A3 e# i
  2. db.things.find({j : {$gte: 4}});
复制代码

" g5 `! p+ D7 v. N( e3 S+ ]1 J
也可以合并在一条语句内:
  1. db.collection.find({ "field" : { $gt: value1, $lt: value2 } } );    // value1 < field < value
复制代码
: ^' X6 |3 J( e) v9 [1 l+ J& ~
, i5 L2 M/ \3 l3 z/ ^0 s( j
8 w" N  v5 R( n
2) 不等于 $ne
例子:
  1. db.things.find( { x : { $ne : 3 } } );
复制代码
9 Y8 I5 ?. I- o% n* i. o. N" A
3) in 和 not in ($in $nin)7 h2 F* M1 J1 U4 Y) D
- E1 V2 C3 q: l4 }; d" N6 f: ^, Y3 v/ I
语法:
  1. db.collection.find( { "field" : { $in : array } } );
复制代码

' w) R2 T# X: W
例子:
  1. db.things.find({j:{$in: [2,4,6]}});7 Y& w* f4 z  W. n- b( Z% ?; u
  2. db.things.find({j:{$nin: [2,4,6]}});
复制代码

  S7 y, G. N/ B
/ T' t9 x$ W3 F" u! H" O
4) 取模运算$mod
' D4 k8 I$ a) ^5 |5 q& S( V) }
. k2 Z! _& ?5 y! I如下面的运算:
  1. db.things.find( "this.a % 10 == 1")
复制代码

0 N8 Z) X2 S$ b7 F0 M# c6 H" T9 E
可用$mod代替:
  1. db.things.find( { a : { $mod : [ 10 , 1 ] } } )
复制代码
8 R9 O$ z$ K' i  ~: o$ t6 e
$ z+ U% T/ C  L+ W9 f3 a
5)  $all
# Y; W8 g) U6 g; ^  `% x
  C+ v& H6 W; ~2 Y9 }$all和$in类似,但是他需要匹配条件内所有的值:
  z3 G: h3 x$ X: h6 [* q
7 U7 ?2 d( V, g如有一个对象:' L; S. X7 r. v0 s5 n
  1. { a: [ 1, 2, 3 ] }
复制代码
; s+ X: D) u/ M$ u
下面这个条件是可以匹配的:
  1. db.things.find( { a: { $all: [ 2, 3 ] } } );
复制代码
9 H7 A0 n% r) f8 f; r4 ]
但是下面这个条件就不行了:
  1. db.things.find( { a: { $all: [ 2, 3, 4 ] } } );
复制代码
2 d; t/ }5 ^- F! o$ m

* @8 A' J6 I8 [* [1 h6)  $size
6 b4 w/ [1 q0 s) a, g$ V' q. e. B% N% B( w
$size是匹配数组内的元素数量的,如有一个对象:{a:["foo"]},他只有一个元素:: a9 E# S0 q$ j" V) m! K) |

, r/ Q) [5 _. t: I! A下面的语句就可以匹配:
  1. db.things.find( { a : { $size: 1 } } );
复制代码

% c* A/ h# {2 c
官网上说不能用来匹配一个范围内的元素,如果想找$size<5之类的,他们建议创建一个字段来保存元素的数量。
You cannot use $size to find a range of sizes (for example: arrays with more than 1 element). If you need to query for a range, create an extra size field that you increment when you add elements.

$ Z: t' P6 n( e" ?9 I) ~
7)$exists
$exists用来判断一个元素是否存在:
如:
  1. db.things.find( { a : { $exists : true } } ); // 如果存在元素a,就返回$ o% {0 \  H0 f! ^' Z, y
  2. db.things.find( { a : { $exists : false } } ); // 如果不存在元素a,就返回
复制代码

" f' S" d9 r. W6 c  Q3 [7 `
8)  $type
$type 基于 bson type来匹配一个元素的类型,像是按照类型ID来匹配,不过我没找到bson类型和id对照表。
  1. db.things.find( { a : { $type : 2 } } ); // matches if a is a string
    # ^, K9 H3 s! ]  \; L
  2. db.things.find( { a : { $type : 16 } } ); // matches if a is an int
复制代码
# ~- R' U2 Z4 ~9 p
9)正则表达式
5 _- E4 {5 \% j/ C8 o/ v  e% n" a6 Z" r# }, S( g
mongo支持正则表达式,如:
  1. db.customers.find( { name : /acme.*corp/i } ); // 后面的i的意思是区分大小写
复制代码
; z3 O' c- w/ y& @9 \9 B) W( X
10)  查询数据内的值9 n% @5 _: W- L; C+ F

; f7 J$ ?: x0 `/ s* I& D  ]下面的查询是查询colors内red的记录,如果colors元素是一个数据,数据库将遍历这个数组的元素来查询。
  1. db.things.find( { colors : "red" } );
复制代码
% H- a; ?& x% `6 r
11) $elemMatch
, F! @; J- s4 J3 R4 a9 C" k
* {. ]/ Z" M5 Q4 l" s& ^如果对象有一个元素是数组,那么$elemMatch可以匹配内数组内的元素:
  1. > t.find( { x : { $elemMatch : { a : 1, b : { $gt : 1 } } } } )
    . B, {" a, u# U& A2 _7 J5 _
  2. { "_id" : ObjectId("4b5783300334000000000aa9"),  # W* ^+ {" `7 M, s" b: e+ k' F$ \
  3. "x" : [ { "a" : 1, "b" : 3 }, 7, { "b" : 99 }, { "a" : 11 } ]
    & c! L: k, ^+ `* E* ^
  4. }
复制代码

* ?3 v* F" \. q1 S$elemMatch : { a : 1, b : { $gt : 1 } } 所有的条件都要匹配上才行。
注意,上面的语句和下面是不一样的。
> t.find( { "x.a" : 1, "x.b" : { $gt : 1 } } )
2 u7 \5 A: T7 N( s5 {
$elemMatch是匹配{ "a" : 1, "b" : 3 },而后面一句是匹配{ "b" : 99 }, { "a" : 11 } . s: a# L# t2 I! }0 G# V" Z) D9 O
+ d* |' C" Y3 e( l& |
12)  查询嵌入对象的值
  1. db.postings.find( { "author.name" : "joe" } );
复制代码
- a3 V* o/ v, k' g; x; N* w
注意用法是author.name,用一个点就行了。更详细的可以看这个链接: dot notation
举个例子:
  1. > db.blog.save({ title : "My First Post", author: {name : "Jane", id : 1}})
复制代码

% @5 d. v! t1 B  z( q
如果我们要查询 authors name 是Jane的, 我们可以这样:
  1. > db.blog.findOne({"author.name" : "Jane"})
复制代码

4 \! N! q5 m1 e7 g$ G, y0 `$ R
如果不用点,那就需要用下面这句才能匹配:
  1. db.blog.findOne({"author" : {"name" : "Jane", "id" : 1}})
复制代码
4 X- I' G% F! w+ G
下面这句:
  1. db.blog.findOne({"author" : {"name" : "Jane"}})
复制代码
3 N$ z4 h2 [; S/ g' _" Y
是不能匹配的,因为mongodb对于子对象,他是精确匹配。

; @% T9 U* g0 J6 R# N! X" j. y3 t
13) 元操作符 $not 取反
如:
  1. db.customers.find( { name : { $not : /acme.*corp/i } } );/ `- z3 O# i' X
  2. db.things.find( { a : { $not : { $mod : [ 10 , 1 ] } } } );
复制代码
! Z- z6 [' V1 z& u3 t7 t- f
mongodb还有很多函数可以用,如排序,统计等,请参考原文。- d$ J' T& K9 n% Z3 O3 l

3 m8 G4 P& J: U' O& z+ Fmongodb目前没有或(or)操作符,只能用变通的办法代替,可以参考下面的链接:+ D- Z! O! A* d; b- R0 j) H
$ t! U& b5 A  p; m
http://www.mongodb.org/display/DOCS/OR+operations+in+query+expressions
9 n, m8 n& m+ Z9 z
4 f- ~$ D* Z% ~1 o# S& t版本二:
6 R7 V( }! u# M2 U" k, x
shell 环境下的操作:
   1.  超级用户相关:
         1. #进入数据库admin
. ?! k- l  y9 [; z3 d& S9 V. H% ^
  1. use admin
复制代码
; j) @, v& U9 c1 d
         2. #增加或修改用户密码
  1.           db.addUser('name','pwd')
复制代码
) K  b% c  b7 h! h# M2 E
         3. #查看用户列表
  1.           db.system.users.find()
复制代码
" Y) f  ~/ q, B4 y2 p
         4. #用户认证
  1.           db.auth('name','pwd')
复制代码

7 Q. S5 `; l1 b3 G4 J
         5. #删除用户
  1.           db.removeUser('name')
复制代码

6 k1 T- ^) y# j' `
         6. #查看所有用户
  1.           show users
复制代码
& J; f4 q3 K% l7 S8 i2 n0 R
         7. #查看所有数据库
  1.           show dbs
复制代码
" J1 X2 X4 A' f) G# c
         8. #查看所有的collection
  1.           show collections
复制代码

/ e8 |5 j5 x' r1 D* \( D% e. O3 `7 _
         9. #查看各collection的状态
  1.           db.printCollectionStats()
复制代码

6 I% s8 A/ L+ M% _& E: w/ i' M+ `8 V
        10. #查看主从复制状态
  1.           db.printReplicationInfo()
复制代码

* c3 S( g6 m! Y$ c
        11. #修复数据库
  1.           db.repairDatabase()
复制代码
# m: c- J7 q: ^
        12. #设置记录profiling,0=off 1=slow 2=all
  1.           db.setProfilingLevel(1)
复制代码
5 Y3 Y, B; x2 S- f8 s% s
        13. #查看profiling
  1.           show profile
复制代码
  C7 X" j! O5 _2 m
        14. #拷贝数据库
  1.           db.copyDatabase('mail_addr','mail_addr_tmp')
复制代码
* d' }$ b) G) z3 m- g, G
        15. #删除collection
  1.           db.mail_addr.drop()
复制代码
: b9 K' g$ \+ @: u4 p
        16. #删除当前的数据库
  1.           db.dropDatabase()
复制代码
7 F8 x; U7 K: @8 I" t
   2. 增删改
         1. #存储嵌套的对象
  1.              db.foo.save({'name':'ysz','address':{'city':'beijing','post':100096},'phone':[138,139]})
复制代码

( ?2 R4 j5 g& v7 x+ o  L
         2. #存储数组对象
  1.              db.user_addr.save({'Uid':'yushunzhi@sohu.com','Al':['test-1@sohu.com','test-2@sohu.com']})
复制代码

) \6 `' L+ p5 J7 k
         3. #根据query条件修改,如果不存在则插入,允许修改多条记录
  1.             db.foo.update({'yy':5},{'$set':{'xx':2}},upsert=true,multi=true)
复制代码
. ]8 _  B4 y$ _. w9 I
         4. #删除yy=5的记录
  1.             db.foo.remove({'yy':5})
复制代码
. {$ j  E0 d* i0 l" w: b( x6 j5 R
         5. #删除所有的记录
  1.             db.foo.remove()
复制代码

1 Y3 g; J+ a6 E0 V' A# u
   3. 索引
         1. #增加索引:1(ascending),-1(descending)
         2. db.foo.ensureIndex({firstname: 1, lastname: 1}, {unique: true});
         3. #索引子对象
         4. db.user_addr.ensureIndex({'Al.Em': 1})
         5. #查看索引信息
         6. db.foo.getIndexes()
         7. db.foo.getIndexKeys()
         8. #根据索引名删除索引
         9. db.user_addr.dropIndex('Al.Em_1')
   4. 查询
         1. #查找所有
        2. db.foo.find()
        3. #查找一条记录
        4. db.foo.findOne()
        5. #根据条件检索10条记录
        6. db.foo.find({'msg':'Hello 1'}).limit(10)
        7. #sort排序
        8. db.deliver_status.find({'From':'ixigua@sina.com'}).sort({'Dt',-1})
         9. db.deliver_status.find().sort({'Ct':-1}).limit(1)
        10. #count操作
        11. db.user_addr.count()
        12. #distinct操作,查询指定列,去重复
        13. db.foo.distinct('msg')
        14. #”>=”操作
        15. db.foo.find({"timestamp": {"$gte" : 2}})
        16. #子对象的查找
        17. db.foo.find({'address.city':'beijing'})
   5. 管理
         1. #查看collection数据的大小
         2. db.deliver_status.dataSize()
         3. #查看colleciont状态
         4. db.deliver_status.stats()
         5. #查询所有索引的大小
         6. db.deliver_status.totalIndexSize()

" y3 I/ _* U* G" `
6.  高级查询
条件操作符
# q% V/ S. r) ~! x, n; a
  1. $gt : > & A, |# T  k0 j5 C- K; R
  2. $lt : <
    * v# O  |0 E1 X$ J; B4 ^- k6 S; C
  3. $gte: >=   X, u4 M% Q/ @6 y$ d
  4. $lte: <=
    8 T( f3 N3 }- [) z
  5. $ne : !=、<> & u$ f! o" A1 w& v
  6. $in : in 2 T7 d+ b8 W# v. K
  7. $nin: not in % J$ V" B2 _: [3 J3 g+ e/ n
  8. $all: all
    . s0 C( ^$ M8 e5 b. z
  9. $not: 反匹配(1.3.3及以上版本)
复制代码
! _* X' E$ v% V- w* j
+ i. @  W7 [  p6 t
查询 name <> "bruce" and age >= 18 的数据
+ m; ~7 W# x. d( q( L+ Z
  1. db.users.find({name: {$ne: "bruce"}, age: {$gte: 18}});
复制代码

$ T0 v7 n& B1 {  I! c
9 g4 V) ~; D( K) f查询 creation_date > '2010-01-01' and creation_date <= '2010-12-31' 的数据
% [+ h% U  |& e& ~0 _
  1. db.users.find({creation_date:{$gt:new Date(2010,0,1), $lte:new Date(2010,11,31)});
复制代码

" ~% c& r5 B+ C0 n" a/ S/ u- t7 z! Q! s
查询 age in (20,22,24,26) 的数据
% v. D, k* E/ U$ b- d
  1. db.users.find({age: {$in: [20,22,24,26]}});
复制代码

2 Q8 u8 _. n# w0 R# s
  x, e9 i4 z0 Y! a* n- V# E查询 age取模10等于0 的数据
- d1 `; g' p) u4 O: q! _
  1. db.users.find('this.age % 10 == 0');
复制代码
# @5 s; A, B) f: n! P
或者 & H* F0 p, A4 {" E& o
  1. db.users.find({age : {$mod : [10, 0]}});
复制代码

' i1 L8 R0 t1 {' L! o- I
6 j' x- K( `" j1 B) ~1 [匹配所有
+ y) g$ M; l! G  ~' h
  1. db.users.find({favorite_number : {$all : [6, 8]}});
复制代码

8 T" j/ q  p; _' y! L7 k3 P. J6 n. p可以查询出{name: 'David', age: 26, favorite_number: [ 6, 8, 9 ] } 5 ?; i  |3 y; s9 K# [! e
可以不查询出{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] } / U  r  ?, R$ Z: D' Q0 r7 D7 {$ P

$ A' H, @3 l& s$ ?/ q( A7 c5 u查询不匹配name=B*带头的记录
) S/ A5 y3 C! Z  g: |
  1. db.users.find({name: {$not: /^B.*/}});
复制代码
2 I/ h* \/ C( z4 A
查询 age取模10不等于0 的数据 1 o% ?6 b/ ?/ U9 v! m
  1. db.users.find({age : {$not: {$mod : [10, 0]}}});
复制代码

$ a2 |6 e, t7 T, B* A2 O
5 |3 `3 X) d' \1 J. J$ }#返回部分字段
5 J' J" ~, L/ X' ^选择返回age和_id字段(_id字段总是会被返回)
' |& m3 i0 ^5 D0 p" E6 R: W
  1. db.users.find({}, {age:1});
    / V' h* I1 G3 B9 i
  2. db.users.find({}, {age:3});
    , }1 f* K* C. H; J5 z( u
  3. db.users.find({}, {age:true});
    5 F) a) e! }9 _' N- s/ K( o
  4. db.users.find({ name : "bruce" }, {age:1});
复制代码

% n% A% b0 z" l! K- U& k- P0为false, 非0为true
( S5 Y8 a/ Z+ [2 R8 f! B6 T' u3 S) r5 f( {( r
选择返回age、address和_id字段
0 I! m/ F' Y; m' i( A, }. g
  1. db.users.find({ name : "bruce" }, {age:1, address:1});
复制代码

  I& r; w7 q0 V0 m. G/ c; Z9 F+ e" \
排除返回age、address和_id字段 ; Z% O+ ?; o; ]0 ]
  1. db.users.find({}, {age:0, address:false});
    , U& |5 M! {7 i' s
  2. db.users.find({ name : "bruce" }, {age:0, address:false});
复制代码
8 m9 G2 _" u2 H1 `8 g4 l+ }
' L0 \7 y, Y, r! D$ ]) P& T' C
数组元素个数判断 5 D" ]& A7 z/ p" I/ W9 l
对于{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] }记录 6 O& v4 |, w& p0 Q# l$ V2 {
匹配db.users.find({favorite_number: {$size: 3}}); & G/ C# m: T  r: l& ?
不匹配db.users.find({favorite_number: {$size: 2}}); " K( C$ K. e& Q* H0 h% I

2 r& u8 u! X. V, }* `9 k8 C' M- _$exists判断字段是否存在
7 A8 W" I# F8 ~7 h. {" Q: }+ n  B查询所有存在name字段的记录 3 f0 A  t' b" ?5 L
  1. db.users.find({name: {$exists: true}});
复制代码

. S8 r9 u- g+ }2 ]" z: F查询所有不存在phone字段的记录
) R9 `2 M) E: J1 j% {  N
  1. db.users.find({phone: {$exists: false}});
复制代码
! E' w2 U  }4 d; c

& O7 j/ ?  C/ {$type判断字段类型 " m5 Z9 J1 B! Y- l# p' _/ w
查询所有name字段是字符类型的
0 n( [  P6 J5 d- f; g: K
  1. db.users.find({name: {$type: 2}});
    ) T7 b$ E/ J7 L! ?' m
复制代码

1 V% ^# ^" ~2 g) Z8 N5 V0 h8 M查询所有age字段是整型的
& S: m" P4 g" Q& [2 U8 M1 i2 u
  1. db.users.find({age: {$type: 16}}); 8 `% M& n5 \% B% f( w- T6 a# g" V5 K
复制代码

- I3 d6 E. V4 N+ Y$ r, i对于字符字段,可以使用正则表达式 3 L1 E  u. M' c/ J
查询以字母b或者B带头的所有记录
# ^( n2 G( r9 u, t
  1. db.users.find({name: /^b.*/i}); ' v( l# a8 T' V2 H
复制代码
# ^7 `( |& \3 d
$elemMatch(1.3.1及以上版本)
8 B: R! h- h+ a为数组的字段中匹配其中某个元素
' Q2 v* h! J) J# u! x( ~$ ]; }4 @2 t, M
Javascript查询和$where查询
0 I1 s" g4 v: f! N查询 age > 18 的记录,以下查询都一样
4 L$ W, D6 G6 e" E9 R! n2 A
  1. db.users.find({age: {$gt: 18}});
    ' Q: I( s# p, t1 n8 E8 V& T" C
  2. db.users.find({$where: "this.age > 18"});
    8 h$ Y/ V- m5 u( ]* U
  3. db.users.find("this.age > 18");
    ( \3 ?# n1 Q- v- m: p& H) }9 N+ v
  4. f = function() {return this.age > 18} db.users.find(f);
复制代码

9 b( O* W6 \( t1 N( j, P
$ q" t/ \, d/ T1 P" k排序sort()
% g, j( a( V2 @0 Z以年龄升序asc
' X, p" I6 J: N; p$ T" F
  1. db.users.find().sort({age: 1}); & B; h& p& d/ o0 S" A0 H3 l
复制代码
; s  @+ M* I* C# l& _9 U' O: u3 k3 ^
以年龄降序desc " \. o1 h5 K5 M4 {$ Z% c- _6 ~# ^
  1. db.users.find().sort({age: -1}); * f# N3 N0 ^% D' N* j: }. }0 L
复制代码

( E, p4 A. F9 L4 a! y限制返回记录数量limit()
3 O1 B# p$ j9 E3 z. h返回5条记录 4 Z! d; N2 Z6 M, l( X
  1. db.users.find().limit(5);
    * A5 E0 c1 a- n1 y
复制代码

2 }8 Z! J% [0 z4 z8 e" D/ |返回3条记录并打印信息
1 M3 z- m! k$ ], r8 |
  1. db.users.find().limit(3).forEach(function(user) {print('my age is ' + user.age)});
    & Z/ Y* S8 a0 I. M; w
复制代码

" \2 d1 F7 U/ o. {  [/ f' u结果 1 L& u5 G1 [4 ?8 Y$ y
  1. my age is 18
    5 n/ k- u& _) C
  2. my age is 19
    , C& k) |1 V. o2 r
  3. my age is 20
复制代码
! ~1 N0 [; l- F6 d% `' C
- r2 K7 U0 o$ m+ W3 e  q+ ^4 }
限制返回记录的开始点skip()
! M7 n" k& u3 y+ U# u) G# H/ @从第3条记录开始,返回5条记录(limit 3, 5) 4 f# }3 K2 {) N$ l* q1 l
  1. db.users.find().skip(3).limit(5); 9 R3 Y) b( g# u" T* U0 s
复制代码
) Q: V2 r; J  E& f$ p
查询记录条数count() 5 [/ T& a1 e, n. H
db.users.find().count();
; o% m9 n* n( O$ F% r6 b! Ddb.users.find({age:18}).count(); & h6 [$ l7 C9 [/ U
以下返回的不是5,而是user表中所有的记录数量 : B; l  L" [7 L$ s9 e
db.users.find().skip(10).limit(5).count();
5 d) u; y# W( }, c/ F' G如果要返回限制之后的记录数量,要使用count(true)或者count(非0) + U* E, _0 b9 A* K: ?# L  W' ^
  1. db.users.find().skip(10).limit(5).count(true);
复制代码

8 C5 ^& ]. q+ A1 r! a
3 P% E7 K! w+ M; }! C) g  f* C# c分组group() + _) @; ?# v% |3 Y8 ^% [
假设test表只有以下一条数据 - C) G3 C. W  N% _
  1. { domain: "www.mongodb.org" 6 [/ v( r, \, ~9 w. w
  2. , invoked_at: {d:"2009-11-03", t:"17:14:05"}
    1 H/ `. ~; E8 P$ q
  3. , response_time: 0.05 7 M/ e5 C" t7 k6 n" ]' ?3 B
  4. , http_action: "GET /display/DOCS/Aggregation"
    / B" E* z# l( O) x7 d# t! D
  5. }
复制代码

3 }8 y. i0 j7 o$ Y3 W使用group统计test表11月份的数据count:count(*)、total_time:sum(response_time)、avg_time:total_time/count; ) y8 W7 _; U0 p+ k& A
  1. db.test.group(
    2 X4 }/ O0 W6 _, L9 A  ]$ j8 l
  2. { cond: {"invoked_at.d": {$gt: "2009-11", $lt: "2009-12"}}
    ' k, O2 q( ]5 a. a- I
  3. , key: {http_action: true}
    1 ~8 d0 W4 b. \4 G) g
  4. , initial: {count: 0, total_time:0}
    1 H2 }. R( x, ~8 H
  5. , reduce: function(doc, out){ out.count++; out.total_time+=doc.response_time } : |  S+ m* c8 b  `
  6. , finalize: function(out){ out.avg_time = out.total_time / out.count }
    " m! y7 \" P& O4 Y7 C2 W  o0 p
  7. } ); ' w1 @$ p3 o, v
  8. 0 |# f( K; k: [1 s* q
  9. [
    5 `# r+ k$ \& L& H4 U1 v
  10. { ( {2 i: o+ s# n0 a$ b. `
  11. "http_action" : "GET /display/DOCS/Aggregation",
    ; K/ z; l9 h" g1 Q! P+ w
  12. "count" : 1,
    3 ?" ?# X4 ]7 R. ]/ H% ?! C+ w
  13. "total_time" : 0.05, ' g5 m$ I9 `7 o; `' ]/ O+ j
  14. "avg_time" : 0.05
    2 H4 m& M" q: |
  15. } 3 _2 F- u; N: A  n9 C
  16. ]
复制代码

* R: H8 l9 B0 E/ l0 g. V1 c- Y9 L# C- C; Y
) J- d/ w9 ]# ?1 p. p
MongoDB 高级聚合查询
MongoDB版本为:2.0.8
系统为:64位Ubuntu 12.04
先给他家看一下我的表结构[Oh sorry, Mongo叫集合]
如你所见,我尽量的模拟现实生活中的场景。这是一个人的实体,他有基本的manId, manName, 有朋友[myFriends],有喜欢的水果[fruits],而且每种水果都有喜欢的权重。
很不好的是你还看见了有个“_class”字段? 因为我是Java开发者, 我还喜欢用Spring,因此我选用了Spring Data Mongo的类库[也算是框架吧,但是我不这么觉得]。
现在有很多人Spring见的腻了也开始烦了。是的,Spring野心很大,他几乎想要垄断Java方面的任何事情。没办法我从使用Spring后就离不开他,以至于其他框架基本上都不用学。我学了Spring的很多,诸如:Spring Security/Spring Integration/Spring Batch等。。。不发明轮子的他已经提供了编程里的很多场景,我利用那些场景解决了工作中的很多问题,也使我的工作变得很高效。从而我又时间学到它更多。Spring Data Mongo封装了mongodb java driver,提供了和SpringJDBC/Template一致编程风格的MongoTemplate。
不说废话了,我们直接来MongoDB吧。
  • Max 和Min
    - m; N5 `. x/ P6 X" S  E1 |
我和同事在测试Mongo时,索引还写了不到一半,他想查询某个字段的最大值,结果找了半天文档也没找到关于max的函数。我也很纳闷这是常规函数啊怎么不提供? 后来经过翻阅资料确定Mongo确实不提供直接的max和min函数。但是可以通过间接的方式[sort 和 limit]实现这个。
要查询最大值我们只需要把结果集按照降序排列,取第一个值就是了。
如我的例子,我想取得集合中年龄最大的人。
  1. db.person.find({}).sort({"age" : -1}).limit(1)
复制代码
2 z0 q4 w; ~6 B5 w5 t5 D6 i

1 b7 ]8 M1 B6 b  F0 B3 ~! ~$ \+ O
$ U! g$ _" ?2 ]  M1 B
相反如果想要年龄最小的人,只需要把sort中改为{“age”:1}就可以了。
当然我们使用了sort,对于小数量的文档是没问题的。当对于大量数据需要给age建立索引,否则这个操作很耗时。
  • distinct
    " E- c/ v3 n( e0 |4 V# E
MongoDB的destinct命令是获取特定字段中不同值列表的最简单工具。该命令适用于普通字段,数组字段[myFriends]和数组内嵌文档[fruits].
如上面的图片,我认为fruits和myFriends字段是不同的。网上很多资料和例子都没说到这个情景,因为我们也业务是fruits这样的模型,我测试了。对于fruits.fruitId他也是可行的。
如上面的表结构,我想统计所有的喜欢的水果。
  1. db.person.distinct("fruits.fruitId") // 查找对象里引入对象的值,直接加.
复制代码

# l  t4 {7 _- x3 W0 N* u/ y3 K- L; @/ y* ~* l3 L; p' v
他成功执行了。输出如:
  1. [ "aaa", "bbb", "ccc", "www", "xxx", "yyy", "zzz", "rrr" ]
复制代码
! g- U% O* x; D, f

7 D3 n4 }, F4 p
我想统计集合中共有多少个人[按名字吧]
  1. db.person.distinct("manName")
复制代码
* C; k+ b, R8 v0 ^& u
1 J0 r  L8 T' c' y/ w$ S: I- O
我想统计指定个数的人的共同关注的朋友。
  1. db.person.distinct("myFriends", {"manName" : {"$in" : ["ZhenQin", "YangYan"]}})
复制代码
) A. a- L6 r% z1 K* ?, c; U; s
6 \* E7 V' {+ Y) V* {: p* W: H  W
输出如:
  1.         # \. U# F( q/ ^5 O& j
  2. [ "234567", "345678", "456789", "987654", "ni", "wo" ]
复制代码

; p) V, Y  `' i5 I4 f; w- T' F, G9 c1 y" |4 }7 s7 S( ?

& u$ `% w( x& i: r5 w# b  h
那么我使用Java呢? 我只是在演示Mongo的命令,用Spring Data Mongo是怎么操作的?
Spring Schema:
  1. <beans xmlns="http://www.springframework.org/schema/beans"3 ~* S3 W& b% E. N) U- \$ \6 U
  2.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    1 b4 I! M) {, e/ Z, T) `6 I% k
  3.        xmlns:context="http://www.springframework.org/schema/context"! _2 `1 o7 _& a6 \5 j
  4.        xmlns:mongo="http://www.springframework.org/schema/data/mongo"  ^4 u( u( }& }5 w
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans: j9 n+ q& W6 z+ \# ]
  6.           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    / b9 _+ D; C' W4 s- d  G' I$ P
  7.           http://www.springframework.org/schema/context. m/ l0 n1 S5 ~3 t; k. e* u( ~
  8.           http://www.springframework.org/schema/context/spring-context-3.1.xsd
    7 ~  y3 W# C2 k0 z
  9.           http://www.springframework.org/schema/data/mongo9 p& I# s: h, e: A$ N: M5 D3 x
  10.           http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">7 V" d* U! k7 L: `7 o0 ^3 p; X& ~
  11. - ]8 d: Q* }4 ^4 i+ ^# ?
  12.     <context:property-placeholder location="classpath:mongo.properties" />- ?0 ?1 m4 G/ f2 j
  13. & F3 e4 P4 e( n; G6 J$ k
  14.     <!-- Default bean name is 'mongo' -->+ y$ h1 L9 |: Z* D9 y
  15.     <mongo:mongo id="mongo" host="${mongo.host}" port="${mongo.port}" />
    ( T+ O& C( `, W
  16. & S: `! l, N; N& G: F, H% \
  17.     <mongo:db-factory id="mongoDbFactory"
    + d2 T% a% r  Y) t
  18.                   mongo-ref="mongo"6 n5 }3 y# F9 C6 ^+ R
  19.                   dbname="mongotest" />
    " J5 c; @3 p7 B7 f3 w. }

  20. % a9 l4 C) f+ V
  21.     <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">" m. {; h0 b" p) P$ o
  22.         <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>5 t9 F: G+ l4 M2 q
  23.     </bean>
    5 E% W, p4 w) Z2 ^1 @: _# U6 Y9 f6 A
  24. </beans>
复制代码
; {% Z2 B$ e1 I' r# A

% L* |1 p: n8 d% I+ H# o1 e0 f
maxmin的测试
  1. @Test
    + R& Z. x( ^; |, E7 @, h
  2.     public void testMaxAndMinAge() throws Exception {: t+ L% }% K1 {
  3.         Query q = new BasicQuery("{}").with(new Sort(new Sort.Order(Sort.Direction.ASC, "age"))).limit(1);$ T' f! c7 K5 E
  4.         Person result = mongoTemplate.findOne(q, Person.class);8 X# n# `: `9 f/ ]
  5.         log.info(result);& l7 c! E' m* h4 T6 i' d9 H& z5 j

  6. & i( Y7 h" i; V2 s0 c8 g: P& f3 b1 w
  7.         q = new BasicQuery("{}").with(new Sort(new Sort.Order(Sort.Direction.DESC, "age"))).limit(1);
    & d( h7 X1 R3 A* ]( M4 Q; t
  8.         result = mongoTemplate.findOne(q, Person.class);
    0 M1 f3 v/ C' Y: ?
  9.         log.info(result);( J6 Q) N% x0 X: T
  10.     }
复制代码
; l# }1 `- u/ W* J" ?/ i/ D

# D+ c3 n; h4 X' O" Z9 ?
distinct的测试:
  1. @Test
    # ]9 E* ?3 E* f* t, e1 f/ Q  Y* g
  2.     public void testDistinct() throws Exception {8 n, {4 d$ a0 r5 Q+ p4 L
  3.         List result = mongoTemplate.getCollection("person").distinct("myFriends");
    : L4 g2 N  }8 O- O
  4.         for (Object o : result) {
    6 j! h) e; @2 M9 D* k
  5.             log.info(o);' X6 q) l5 R8 C- d# c  S
  6.         }
    1 b) A4 K% w6 ~8 L
  7. : T+ w+ L  [/ T9 l" |
  8.         log.info("==================================================================");
    # ^- w4 Q. K% t8 }& y8 ?
  9.         Query query = Query.query(Criteria.where("manId").is("123456"));
    : N; M; {* W& o5 b
  10.         result = mongoTemplate.getCollection("person").distinct("myFriends", query.getQueryObject());' o6 p# @( `9 ^) B
  11.         for (Object o : result) {: ^) ^5 t8 k  |, k+ S
  12.             log.info(o);
    , w, v# }6 @! ?# W" U0 Y
  13.         }1 p+ w  B- i$ C7 O6 U: X6 ]% \

  14. ' N/ X/ V3 u6 ~* O* v# y% i# @: P6 I7 |
  15.         log.info("==================================================================");( k3 f, y. Z/ Y9 x1 O
  16.         result = mongoTemplate.getCollection("person").distinct("fruits.fruitId");" X) z3 G6 q" b2 \  P# o- s. _4 p
  17.         for (Object o : result) {, [& p5 K. F- m5 G# i
  18.             log.info(o);
    4 s* X4 N* V4 U: _. S% S
  19.         }' M0 Y1 S& y" i( w9 t( S" n
  20.     }
复制代码
# ~# I3 ^# }+ O

( F- z. T: L% ^, ]
输出的结果为:
  1. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] 234567! t0 P+ S+ h2 [3 u
  2. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] 345678
    # R! S( x7 V" o! ?) O; z
  3. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] 456789
    6 i% |. @0 U, p$ r' q. _: g& R
  4. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] 987654
    & }) o, R2 h5 m, \3 u* V- h
  5. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] ni' D* O7 h3 ]. D5 j
  6. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] wo
    9 H" T+ \3 l4 ~! o% C& r
  7. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(72)] 123456
    7 E( \* M" |" F1 W
  8. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(75)] ==================================================================
    6 }: v% F' v7 ?
  9. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(79)] 234567
    7 @/ A8 H% r9 ~6 I/ t; S! L( \
  10. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(79)] 345678
    9 S/ S4 `" O$ z+ U# {) y1 I, B
  11. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(79)] 4567891 `9 }  T/ B( U7 m! ~# e( U
  12. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(79)] 987654
    9 ~7 l5 ~. }# w# y" \" ~6 P  P
  13. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(82)] ==================================================================; j8 L+ m0 N! I' d& _
  14. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] aaa
    / C; \+ z6 p( `& w( Q$ V
  15. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] bbb; l4 Q' [# t6 p8 L
  16. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] ccc8 s5 q6 M% n0 y6 M! \, }6 r0 o) ]
  17. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] www
      k8 \1 c; o- ~2 E8 K" \
  18. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] xxx
    2 n: D$ X9 V7 ?; Q9 c, U5 K: t" I
  19. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] yyy
    5 V2 f1 {& ^' P6 ^
  20. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] zzz
    / y- A5 p$ O0 {- S  L0 h- W
  21. 12-22 14:13:45 [INFO] [t.MongoAdvaceQueryTest(85)] rrr9 o5 D& v4 s% a; `$ J8 l* B
  22. 12-22 14:13:45 [INFO] [support.GenericApplicationContext(1020)] Closing org.springframework.context.support.GenericApplicationContext@1e0a91ff: startup date [Sat Dec 22 14:13:44 CST 2012]; root of context hierarchy
复制代码
3 r) J# y- }* y: z4 r8 r, X

0 f- I4 ~' @% y% U( g: ^$ Y
这里我要特别说明一下, 当使用了Spring Data Mongo,如上面的findOne(query, Person.class)它就会把查询的结果集转换成Person类的对象。Spring Data Mongo的很多API中都这样,让传入了一个Bean的class对象。因为distinct的测试是输出list<String>的,我 使用的mongo-java-driver的api。他们都很简单,唯一的是Query这个Spring提供的对象,希望读者注意,他几乎封装了所有条件 查询,sort,limit等信息。

8 \5 y& X; ?) ?# h8 @, p) w+ `" A/ O' |3 |8 H7 r* |3 C; k

' H  o6 e3 x! C3 `
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-3-16 18:13 , Processed in 0.072644 second(s), 23 queries .

Copyright © 2001-2026 Powered by cncml! X3.2. Theme By cncml!