mongodb-java-driver做基本的CRUD--基本查询


title: mongodb-2 mongodb-java-driver做基本的CRUD--基本查询
date: 2017-01-11 16:25:59
tags:
categories: mongodb


本文demo代码地址:https://github.com/lazyguy21/mongoDemo

如何连接mongodb?
http://mongodb.github.io/mongo-java-driver/3.4/driver/tutorials/connect-to-mongodb/
其中MongoClient类比jdbc时的连接池,全局唯一。MongoCollection类似于Connection
不同的是,使用完MongoCollection后并不需要像JDBC,redis的jedis客户端一样手动返回,很方便。

利用Document构建查询语句

mongodb-java-driver为我们提供了2种方式。一种是用Document(类似Map)来构建一个JSON式的对象。怎么个搞法呢?

@Test
public void testQuery() throws Exception {
    Document document = new Document("price", new Document("$gte", 1)
            .append("$lt", 500))
            .append("name", "shirt");
    System.out.println(document.toJson());
    MongoCollection<Document> clothCollection = mongoClient.getDatabase("test").getCollection("Cloth");
    FindIterable<Document> results = clothCollection.find(document);
    results.forEach((Consumer<? super Document>) System.out::println);
}

说实话用json(或者说BSON)的样式作为交互语言,给习惯了sql的我来说本来就十分别扭了,翻译成这个样子之后,不管你怎么看,我觉得简直是崩溃。研究了很久之后,我发现Document提供了2个便利方法。

一个是Document.parse将一个JSON字符串反序列化为Document,

一个是toJson可以把Document序列化为json字符串。

这样tojson方法可以作为一种查看你最后写的BSON语句正确与否的手段。如果记录在日志里面,就可以像打印sql一样留下记录了!

/**
 * 使用mongodb时怎么去判断自己最后写出的语句对不对呢?
 *
 * @throws Exception
 */
@Test
public void testToJson() throws Exception {
    Document document = new Document("price", new Document("$gte", 1)
            .append("$lt", 5))
            .append("name", "shirt");
    String string = document.toJson();
    String s = document.toString();
    System.out.println( s);
    System.out.println(string);

}

输出:

Document{{price=Document{{$gte=1, $lt=5}}, name=shirt}}
{ "price" : { "$gte" : 1, "$lt" : 5 }, "name" : "shirt" }

前者是toString,后者是toJson的结果。可以看见tojson可以直接复制到GUI里面调试。

使用Filters工具类简写

 @Test
    public void testQuery2() throws Exception {
        MongoCollection<Document> clothCollection = mongoClient.getDatabase("test").getCollection("Cloth");
//        此Document等于下面的简写
//        new Document("stars", new Document("$gte", 2)
//                .append("$lt", 5))
//                .append("categories", "Bakery")
        clothCollection.find(and(gte("stars", 2), lt("stars", 5), eq("categories", "Bakery")))
                .forEach((Consumer<? super Document>) System.out::println);

    }

很容易理解的工具类,不过这样写无法利用toJson方法,因为生成的不是Document。

限制Document返回的Field

@Test
public void testKey() throws Exception {
    FindIterable<Document> results = clothCollection.find().projection(Projections.include("name"));
    results.forEach((Consumer<? super Document>) System.out::println);
}

输出:

Document{{_id=586f4c462148d00ffe8d9787, name=shirt}}

如上,返回的仅仅只有_id 和name。

这段实际在shell中就是:db.Cloth.find(null,{"name":1}); 1表示include,0表示exclude,其中_id在没有指明的时候默认回传。

通过主键查询ID

@Test
    public void queryDate() throws Exception {
//        FindIterable<Document> results = clothCollection.find(Filters.eq("_id", "58759260bc497244060ba7c0"));
        FindIterable<Document> results = clothCollection.find(new Document("_id", new ObjectId("58759260bc497244060ba7c0")));
        
        results.forEach((Consumer<? super Document>) document -> {
            System.out.println(document);
        });

    }

注意Mongodb的主键类型是ObjectID,需要自己去构造。

分页

@Test
public void testPagination() throws Exception {
    int pageNum=1;
    int pageSize=3;
    FindIterable<Document> results = clothCollection.find().skip((pageNum-1)*pageSize).limit(pageSize);
    long count = clothCollection.count();
    System.out.println("count :"+count);
    results.forEach((Consumer<? super Document>) document -> {
        System.out.println(document);
    });

}

mongodb的分skip和count都是非常耗时的操作,数据量达到几百万的时候,最好别进行count。

分页最好只显示前面几十页即可。

或者使用上一页的id作为条件,排序查询代替分页(这样做的弊端是翻页的时候不能跳页,好处是可以一直翻下去,而且不会变慢,其实感觉这样的需求并不强)。

所以一般常见的做法就是别count,分页只拿前面部分。

OR 或查询

@Test
    public void testOR() throws Exception {
//        Document document = new Document("$or", new Object[]{new Document("label", "adfad"), new Document("name", "T")});
//        Document[] documentsParam = new Document[]{new Document("label", "adfad"), new Document("name", "T")};
        ArrayList<Document> documentsParam = Lists.newArrayList(new Document("label", "adfad"), new Document("name", "T"));
        Document document = new Document("$or", documentsParam);
        FindIterable<Document> documents = clothCollection.find(document);
        documents.forEach((Consumer<? super Document>) document1 -> {
            System.out.println(document1);
        });

    }

上面的代码查询的是name=T或者label=adfad的文档。

shell就是:find({ "$or" : [{ "label" : "adfad" }, { "name" : "T" }] })

值得一提的是驱动能认识ArrayList,却没有Array的解析器……

IN查询

@Test
public void testIN() throws Exception {
    Bson queryParam = Filters.in("name", "T", "shirt");
    FindIterable<Document> documents = clothCollection.find(queryParam);
    documents.forEach((Consumer<? super Document>) document1 -> System.out.println(document1));
}

in也可以在单filed的时候或
shell :find{"name",{"$in" : [ "shirt","T" ]}}

上面代码用的Filters辅助类,参数既可以接受可变参数,也可以是List

NULL查询

@Test
public void testNULL() throws Exception {
    FindIterable<Document> results = clothCollection.find(new Document("name", null));
    results.forEach((Consumer<? super Document>) document1 -> System.out.println(document1));
}

输出:

Document{{_id=58735ed5bc49722dd7b74f91, name=null}}
Document{{_id=58759260bc497244060ba7c0, date=Wed Jan 11 10:03:12 CST 2017}}

可以看到单纯的传 name:null这样的条件,将会匹配到name的值本身为null的Document,也会匹配到根本就没有name这个field的Document!!

匹配有name字段并且name字段值为null的Document

@Test
public void testNULL2() throws Exception {
    ArrayList<Object> objects = Lists.newArrayList();
    objects.add(null);
    FindIterable<Document> documents = clothCollection.find(Filters.and(Filters.in("name",objects), Filters.exists("name")));
    documents.forEach((Consumer<? super Document>) document1 -> System.out.println(document1));
}

shell即为:{name:{"$in":[null],"$exits":true}}

没有$eq 这种操作符……只能用$in来代替

比较尴尬的是,null对于java来说也是很特殊的字段,注意上面代码中,我是怎么传null到list里面的……

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容