查詢概要java
MongoDB 查詢數據的語法格式以下:
db.collection.find(query, projection)
query :可選,使用查詢操做符指定查詢條件
projection :可選,使用投影操做符指定返回的鍵。查詢時返回文檔中全部鍵值, 只需省略該參數便可(默認省略)。須要以易讀的方式來讀取數據,可使用 pretty() 方法;spring
查詢選擇器sql
(1)in語句示例: db.users.find({"username":{"$in":["lison","sean"]}}) (2)not語句示例: db.users.find({"lenght":{"$not":{"$gte":1.77}}}).pretty() 由於not語句 會把不包含查詢語句字段的文檔 也檢索出來 (3)exists語句示例: db.users.find({"lenght":{"$exists":true}}).pretty() 判斷文檔有沒有關心的字段
查詢選擇mongodb
映射
字段選擇:db.users.find({},{'username':1})
字段排除:db.users.find({},{'username':0})
排序
sort():db.orders.find().sort({'orderTime':1,'price':1})1:升序 -1:降序
跳過和限制
skip(n):跳過n條數據
limit(n):限制n條數據
e.g:db.orders.find().sort({'orderTime':-1}).limit(5).skip(5)
查詢惟一值
distinct():查詢指定字段的惟一值,e.g:db.users.distinct(「age」)數據庫
測試數據json
var user1 = { "username" : "lison", "country" : "china", "address" : { "aCode" : "411000", "add" : "長沙" }, "favorites" : { "movies" : ["殺破狼2","戰狼","雷神1"], "cites" : ["長沙","深圳","上海"] }, "age" : 18, "salary":NumberDecimal("18889.09"), "lenght" :1.79, "comments" : [ { "author" : "lison1", "content" : "lison評論1", "commentTime" : ISODate("2017-01-06T04:26:18.354Z") }, { "author" : "lison2", "content" : "lison評論2", "commentTime" : ISODate("2017-02-06T04:26:18.354Z") }, { "author" : "lison3", "content" : "lison評論3", "commentTime" : ISODate("2017-03-06T04:26:18.354Z") }, { "author" : "lison4", "content" : "lison評論4", "commentTime" : ISODate("2017-04-06T04:26:18.354Z") }, { "author" : "lison5", "content" : "lison是蒼老師的小迷弟", "commentTime" : ISODate("2017-05-06T04:26:18.354Z") }, { "author" : "lison6", "content" : "lison評論6", "commentTime" : ISODate("2017-06-06T04:26:18.354Z") }, { "author" : "lison7", "content" : "lison評論7", "commentTime" : ISODate("2017-07-06T04:26:18.354Z") }, { "author" : "lison8", "content" : "lison評論8", "commentTime" : ISODate("2017-08-06T04:26:18.354Z") }, { "author" : "lison9", "content" : "lison評論9", "commentTime" : ISODate("2017-09-06T04:26:18.354Z") } ] };
字符串數組選擇查詢數組
1.數組單元素查詢
db.users.find({"favorites.movies":"蜘蛛俠"})
查詢數組中包含「蜘蛛俠」
2.數組精確查找
db.users.find({"favorites.movies":[ "殺破狼2", "戰狼", "雷神1" ]},{"favorites.movies":1})
查詢數組等於[ 「殺破狼2」, 「戰狼」, 「雷神1」 ]的文檔,嚴格按照順序;
3.數組多元素查詢
db.users.find({"favorites.movies":{"$all":[ "雷神1", "戰狼" ]}},{"favorites.movies":1})
查詢數組包含[「雷神1」, 「戰狼」 ]的文檔,跟順序無關
4.索引查詢
db.users.find({"favorites.movies.0":"殺破狼2"},{"favorites.movies":1})
查詢數組中第一個爲「殺破狼2」的文檔
5.返回數組子集
db.users.find({},{"favorites.movies":{"$slice":[1,2]},"favorites":1})
$slice能夠取兩個元素數組,分別表示從幾條開始和顯示的條數;安全
對象數組選擇查詢多線程
1. 單元素查詢
db.users.find({"comments":{"author" : "lison6","content" : "lison評論6"}})
備註:對象數組精確查找
2.查找lison1 或者 lison12評論過的user ($in查找符)
db.users.find({"comments.author":{"$in":["lison1","lison12"]}}).pretty()
備註:跟數量無關,跟順序無關;
3.查找lison1 和 lison12都評論過的user
db.users.find({"comments.author":{"$all":["lison12","lison1"]}}).pretty()
備註:跟數量有關,跟順序無關;
4.查找lison5評語爲「lison是蒼老師的小迷弟」的user($elemMatch查找符)
db.users.find({"comments":{"$elemMatch":{"author" : "lison5","content" : "lison是蒼老師的小迷弟"}}}) .pretty()
備註:數組中對象數據要符合查詢對象裏面全部的字段,$全元素匹配,和順序無關;app
查詢練習
需求描述
查看一我的的信息,打開頁面只顯示三條評論
點擊評論的下一頁按鈕,新加載三條評論
默認按照評論時間降序
解決方案描述
1. 新增評論時,使用$sort運算符進行排序,插入評論後,再按照評論時間降序排序;
2. 查看人員時加載最新的三條評論;
3. 點擊評論的下一頁按鈕,新加載三條評論
4. 若是有多種排序需求怎麼處理?
實訓腳本: (1)新增評論時,使用$sort運算符進行排序,插入評論後,再按照評論時間降序排序; db.users.updateOne({"username":"lison",}, { "$push": { "comments": { $each: [ { "author" : "james", "content" : "lison是個好老師!", "commentTime" : ISODate("2018-01-06T04:26:18.354Z") } ], $sort: {"commentTime":-1} } } } ); 注意:$sort操做符必須和$each配合使用 (2)查看人員時加載最新的三條評論; db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty() (3)點擊評論的下一頁按鈕,新加載三條評論 原方法:db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$elemMatch":""}).pretty(); 新的方法:db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$id":1}).pretty(); (4)若是有多種排序需求怎麼處理?使用聚合 db.users.aggregate([{"$match":{"username":"lison"}}, {"$unwind":"$comments"}, {$sort:{"comments.commentTime":-1}}, {"$project":{"comments":1}}, {"$skip":6}, {"$limit":3}]) ---------------------------------------------------------------- 聚合訓練: 查詢2015年4月3號以前,每一個用戶每月消費了多少錢,並按用戶名進行排序: db.ordersTest.aggregate([ {"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}}, {"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}}, {"$sort":{"_id":1}} ])
原生Document方式
@Configuration public class AppConfig { /* * Use the standard Mongo driver API to create a com.mongodb.MongoClient instance. */ @Bean public MongoClient mongoClient() { // CodecRegistry registry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(), // CodecRegistries.fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoClientOptions mco = MongoClientOptions.builder() .writeConcern(WriteConcern.ACKNOWLEDGED) .connectionsPerHost(100) .threadsAllowedToBlockForConnectionMultiplier(5) .maxWaitTime(120000).connectTimeout(10000).build(); MongoClient client = new MongoClient(new ServerAddress("120.78.154.33", 27022), mco); return client; } }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class JavaDriverTest { private static final Logger logger = LoggerFactory.getLogger(JavaDriverTest.class); private MongoDatabase db; private MongoCollection<Document> collection; @Resource private MongoClient client; @Before public void init(){ db = client.getDatabase("lison"); collection=db.getCollection("users"); } @Test //測試elemMatch操做符,數組中對象數據要符合查詢對象裏面全部的字段 //查找lison5評語爲「lison是蒼老師的小迷弟」的人 //db.users.find({"comments":{"$elemMatch":{"author" : "lison5","content" : "lison是蒼老師的小迷弟"}}}) .pretty() public void testElemMatch(){ //定義數據的處理類 final List<Document> ret = new ArrayList<>(); Block<Document> printBlock = getBlock(ret); // Document filter = new Document().append("author","lison5") .append("content","lison是蒼老師的小迷弟"); Bson elemMatch = Filters.elemMatch("comments",filter ); FindIterable<Document> find = collection.find(elemMatch); printOperation(ret, printBlock, find); } /** * db.users.updateOne({"username":"lison",}, {"$push": { "comments": { $each: [{ "author" : "james", "content" : "lison是個好老師!", "commentTime" : ISODate("2018-01-06T04:26:18.354Z") } ], $sort: {"commentTime":-1} }}}); */ @Test //新增評論時,使用$sort運算符進行排序,插入評論後,再按照評論時間降序排序 public void demoStep1(){ Bson filter = eq("username", "lison"); Document comment = new Document().append("author","cang") .append("content","lison是個人粉絲") .append("commentTime", new Date()); //$sort: {"commentTime":-1} Document sortDoc = new Document().append("commentTime", -1); PushOptions sortDocument = new PushOptions().sortDocument(sortDoc); // $each Bson pushEach = Updates.pushEach("comments", Arrays.asList(comment), sortDocument); UpdateResult updateOne = collection.updateOne(filter, pushEach); System.out.println(updateOne.getModifiedCount()); } @Test //查看人員時加載最新的三條評論; //db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty() public void demoStep2(){ final List<Document> ret = new ArrayList<>(); Block<Document> printBlock = getBlock(ret); FindIterable<Document> find = collection.find(eq("username", "lison")) .projection(slice("comments", 0, 3)); printOperation(ret, printBlock, find); } @Test //點擊評論的下一頁按鈕,新加載三條評論 //db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$id":1}).pretty(); public void demoStep3(){ final List<Document> ret = new ArrayList<>(); Block<Document> printBlock = getBlock(ret); //{"username":"lison"} Bson filter = eq("username", "lison"); //"$slice":[3,3] Bson slice = slice("comments", 3, 3); //"$id":1 Bson includeID = include("id"); //{"comments":{"$slice":[3,3]},"$id":1}) Bson projection = fields(slice,includeID); FindIterable<Document> find = collection.find(filter) .projection(projection); printOperation(ret, printBlock, find); } @Test /** * db.users.aggregate([{"$match":{"username":"lison"}}, {"$unwind":"$comments"}, {$sort:{"comments.commentTime":-1}}, {"$project":{"comments":1}}, {"$skip":6}, {"$limit":3}]) */ //若是有多種排序需求怎麼處理,使用聚合 public void demoStep4(){ final List<Document> ret = new ArrayList<>(); Block<Document> printBlock = getBlock(ret); List<Bson> aggregates = new ArrayList<>(); aggregates.add(match(eq("username","lison"))); aggregates.add(unwind("$comments")); aggregates.add(sort(orderBy(ascending("comments.commentTime")))); aggregates.add(project(fields(include("comments")))); aggregates.add(skip(0)); aggregates.add(limit(3)); AggregateIterable<Document> aggregate = collection.aggregate(aggregates); printOperation(ret, printBlock, aggregate); } //dbRef測試 //dbref其實就是關聯關係的信息載體,自己並不會去關聯數據 @Test public void dbRefTest(){ final List<Document> ret = new ArrayList<>(); Block<Document> printBlock = getBlock(ret); FindIterable<Document> find = collection.find(eq("username", "lison")); printOperation(ret, printBlock, find); } //--------------------------------------------------------------------------- private void printOperation(List<Document> ret, Block<Document> printBlock, AggregateIterable<Document> aggregate) { aggregate.forEach(printBlock); System.out.println(ret.size()); ret.removeAll(ret); } private void printOperation(final List<Document> ret, Block<Document> printBlock, FindIterable<Document> find) { find.forEach(printBlock); System.out.println(ret.size()); ret.removeAll(ret); } private Block<Document> getBlock(final List<Document> ret) { Block<Document> printBlock = new Block<Document>() { @Override public void apply(Document t) { logger.info("---------------------"); // logger.info(t.toJson()); Object object = t.get("comments"); System.out.println(object); logger.info("---------------------"); ret.add(t); } }; return printBlock; } }
Spring方式
@Document(collection="users") public class User { private ObjectId id; private String username; private String country; private Address address; private Favorites favorites; private int age; private BigDecimal salary; private float lenght; @DBRef private Comments comments; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public Favorites getFavorites() { return favorites; } public void setFavorites(Favorites favorites) { this.favorites = favorites; } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public BigDecimal getSalary() { return salary; } public void setSalary(BigDecimal salary) { this.salary = salary; } public float getLenght() { return lenght; } public void setLenght(float lenght) { this.lenght = lenght; } public Comments getComments() { return comments; } public void setComments(Comments comments) { this.comments = comments; } }
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringTest { private static final Logger logger = LoggerFactory .getLogger(SpringTest.class); @Resource private MongoOperations tempelate; @Test // db.users.find({"comments":{"$elemMatch":{"author" : "lison5","content" : // "lison是蒼老師的小迷弟"}}}) .pretty() public void testElemMatch() { Query query = query(where("comments").elemMatch(where("author").is("lison5").and("content").is("lison是蒼老師的小迷弟"))); List<User> find = tempelate.find(query, User.class); System.out.println(find.size()); } /** * db.users.updateOne({"username":"lison",}, {"$push": { "comments": { $each: [{ "author" : "james", "content" : "lison是個好老師!", "commentTime" : ISODate("2018-01-06T04:26:18.354Z") } ], $sort: {"commentTime":-1} }}}); */ @Test // 新增評論時,使用$sort運算符進行排序,插入評論後,再按照評論時間降序排序 public void demoStep1() { Query query = query(where("username").is("lison")); Comment comment = new Comment(); comment.setAuthor("cang"); comment.setCommentTime(new Date()); comment.setContent("lison是個人粉絲"); Update update = new Update(); PushOperatorBuilder pob = update.push("comments"); pob.each(comment); pob.sort(new Sort(new Sort.Order(Direction.DESC, "commentTime"))); System.out.println("---------------"); WriteResult updateFirst = tempelate.updateFirst(query, update,User.class); System.out.println("---------------"); System.out.println(updateFirst.getN()); } @Test // 查看人員時加載最新的三條評論; // db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty() public void demoStep2() { //{"username":"lison"} Query query = query(where("username").is("lison")); //{"comments":{"$slice":[0,3]} query.fields().include("comments").slice("comments", 0, 3); System.out.println("---------------"); List<User> find = tempelate.find(query, User.class); System.out.println("---------------"); System.out.println(find); } @Test // 點擊評論的下一頁按鈕,新加載三條評論 // db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$id":1}).pretty(); public void demoStep3() { Query query = query(where("username").is("lison")); query.fields().include("comments").slice("comments", 3, 3) .include("id"); System.out.println("---------------"); List<User> find = tempelate.find(query, User.class); System.out.println("---------------"); System.out.println(find); } /** * db.users.aggregate([{"$match":{"username":"lison"}}, {"$unwind":"$comments"}, {$sort:{"comments.commentTime":-1}}, {"$project":{"comments":1}}, {"$skip":6}, {"$limit":3}]) */ // 若是有多種排序需求怎麼處理,使用聚合 @Test public void demoStep4() { Aggregation aggs = newAggregation( match(where("username").is("lison")), unwind("comments"), sort(Direction.ASC, "comments.commentTime"), project("comments"), skip(6), limit(3)); System.out.println("---------------"); AggregationResults<Object> aggregate = tempelate.aggregate(aggs, "users", Object.class); System.out.println("---------------"); List<Object> mappedResults = aggregate.getMappedResults(); System.out.println(mappedResults.size()); } @Test //(1)注意相關的實體bean要加上註解@document,@dbRef //(2)spring對dbRef進行了封裝,發起了兩次查詢請求 public void dbRefTest(){ System.out.println("----------------------------"); List<User> users = tempelate.findAll(User.class); System.out.println("----------------------------"); System.out.println(users); // System.out.println(users.get(0).getComments()); } }
當出現對同一個屬性進行投影時,只會執行最後一個投影操做。
查看一我的的信息,打開頁面只顯示三條評論,實現的語句:
db.users.find({"username":"lison"},{"comments":{"$slice":[0,3]}}).pretty()
點擊評論的下一頁按鈕,新加載三條評論,實現的查詢語句:
不推薦:db.users.find({"username":"lison"},{"comments.lists":{"$slice":[0,3]},"comments":1}).pretty()
推薦:db.users.find({"username":"lison"},{"comments":{"$slice":[3,3]},"$elemMatch":""}).pretty()
PS
1.SPRING 寫入的數據,_class字段修改後是否會影響到實體類的生成驗證
答:修改對代碼沒有影響
2.convert代碼示例
答:spring 1.x版本沒有提供對mongo decemal128數據默認的支持,因此須要自定義轉換器進行轉換;
spring 配置示例
<!-- mongodb鏈接池配置 --> <mongo:mongo-client host="192.168.225.129" port="27022"> <mongo:client-options write-concern="ACKNOWLEDGED" connections-per-host="100" threads-allowed-to-block-for-connection-multiplier="5" max-wait-time="120000" connect-timeout="10000"/> </mongo:mongo-client> <!-- mongodb數據庫工廠配置 --> <mongo:db-factory dbname="test" mongo-ref="mongo" /> <mongo:mapping-converter base-package="com.dongnao.mongodb.entity"> <mongo:custom-converters> <mongo:converter> <bean class="com.dongnao.mongo.convert.BigDecimalToDecimal128Converter"/> </mongo:converter> <mongo:converter> <bean class="com.dongnao.mongo.convert.Decimal128ToBigDecimalConverter"/> </mongo:converter> </mongo:custom-converters> </mongo:mapping-converter> <!-- mongodb模板配置 --> <bean id="anotherMongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" /> <constructor-arg name="mongoConverter" ref="mappingConverter"/> <property name="writeResultChecking" value="EXCEPTION"></property> </bean>
Java客戶端解析
原生java驅動
MongoClient → MongoDatabase →MongoCollection
• MongoClient被設計成線程安全、能夠被多線程共享的。一般訪問數據庫集羣的應用只須要一個實例
• 若是須要使用pojo對象讀寫,須要將PojoCodecProvider注入到client中
查詢和更新的API類
查詢器:com.mongodb.client.model.Filters
投影器:com.mongodb.client.model.Projections
更新器:com.mongodb.client.model.Updates
構造器模式的理解:https://www.jianshu.com/p/e2a2fe3555b9
public class User { private final String firstName; // 必傳參數 private final String lastName; // 必傳參數 private final int age; // 可選參數 private final String phone; // 可選參數 private final String address; // 可選參數 private User(UserBuilder builder) { this.firstName = builder.firstName; this.lastName = builder.lastName; this.age = builder.age; this.phone = builder.phone; this.address = builder.address; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public String getPhone() { return phone; } public String getAddress() { return address; } public static class UserBuilder { private final String firstName; private final String lastName; private int age; private String phone; private String address; public UserBuilder(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public UserBuilder age(int age) { this.age = age; return this; } public UserBuilder phone(String phone) { this.phone = phone; return this; } public UserBuilder address(String address) { this.address = address; return this; } public User build() { return new User(this); } } } 做者:湫水長天 連接:https://www.jianshu.com/p/e2a2fe3555b9 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。
Mongodb鏈接池配置
數據模式設計
nosql在數據模式設計上的優點
讀寫效率高-在IO性能上有先天獨厚的優點;
可擴展能力強,不須要考慮關聯,數據分區分庫,水平擴展就比較簡單;
動態模式,不要求每一個文檔都具備徹底相同的結構。對不少異構數據場景支持很是好;
模型天然-文檔模型最接近於咱們熟悉的對象模型;
mongoDB能不能實現關聯查詢?
先考慮內嵌, 直接按照你的對象模型來設計你的數據模型。若是你的對象模型數量很少,關係不是很複雜,直接一種對象對應一個集合就能夠了
單個bson 文檔最大不能超過16M ;當文檔超過16M的時候,就應該考慮使用引用(DBRef)了,在主表裏存儲一個id值,指向另外一個表中的 id 值。
DBRef語法:{ "$ref" : <value>, "$id" : <value>, "$db" : <value> }
$ref:引用文檔所在的集合的名稱;
$id:所在集合的_id字段值;
$db:可選,集合所在的數據庫實例;
使用dbref腳本示例:
var lison = db.users.findOne({"username":"lison"});
var dbref = lison.comments;
db[dbref.$ref].findOne({"_id":dbref.$id})
聚合的理解
聚合框架就是定義一個管道,管道里的每一步都爲下一步輸出數據數據
經常使用的管道操做
$project:投影,指定輸出文檔中的字段;
$match:用於過濾數據,只輸出符合條件的文檔。$match使用MongoDB的標準查詢操做
$limit:用來限制MongoDB聚合管道返回的文檔數。
$skip:在聚合管道中跳過指定數量的文檔,並返回餘下的文檔。
$unwind:將文檔中的某一個數組類型字段拆分紅多條,每條包含數組中的一個值。
$group:將集合中的文檔分組,可用於統計結果。
$sort:將輸入文檔排序後輸出。
$group操做符
$group:能夠分組的數據執行以下的表達式計算:
$sum:計算總和。
$avg:計算平均值。
$min:根據分組,獲取集合中全部文檔對應值得最小值。
$max:根據分組,獲取集合中全部文檔對應值得最大值。
$push:將指定的表達式的值添加到一個數組中。
$addToSet:將表達式的值添加到一個集合中(無重複值)。
$first:返回每組第一個文檔,若是有排序,按照排序,若是沒有按照默認的存儲的順序的第一個文檔。
$last:返回每組最後一個文檔,若是有排序,按照排序,若是沒有按照默認的存儲的順序的最後個文檔
聚合訓練
查詢2015年4月3號以前,每一個用戶每月消費的總金額,並按用戶名進行排序:
db.ordersTest.aggregate([
{"$match":{ "orderTime" : { "$lt" : new Date("2015-04-03T16:00:00.000Z")}}},
{"$group":{"_id":{"useCode":"$useCode","month":{"$month":"$orderTime"}},"total":{"$sum":"$price"}}},
{"$sort":{"_id":1}}
])