CouchDB是什麼?為什麼我們要關注它?

火星人 @ 2014-03-12 , reply:0


  

 CouchDB是眾多稱作NoSQL解決方案中的一員。與眾不同的是,CouchDB是一個面向文檔的資料庫,在它裡面所有文檔域(Field)都是以鍵值對的形式存儲的。域(Field)可以是一個簡單的鍵值對、列表或者是map。 
 CouchDB會為存儲到資料庫中的每一個文檔分配一個文檔級別的唯一標識符(id),同時每次將變動保存到資料庫中時還會分配一個修訂號(rev)。

NoSQL資料庫的出現代表著傳統的關係型資料庫的轉變,它能夠提供很多好處,當然其自身也面臨著挑戰。CouchDB為我們提供了下面的特性:

  • 容易地在多個伺服器實例之間進行資料庫複製
  • 快速地索引和檢索
  • REST風格的文檔插入、更新、檢索和刪除的介面
  • 基於JSON的文檔格式(更容易地在不同語言之間轉換)
  • 為用戶選擇的語言提供多個庫(指一些流行的語言)
  • 通過_changes訂閱數據更新

從NoSQL系統可視化嚮導中可以找到一個非常出色的工具,它能幫你決定哪一個數據存儲適合你。該指南描述了選擇資料庫系統時應該關注的三個方面(NoSQL和關係型資料庫都是如此)。在我們的項目中使用該指南篩選資料庫時會關注下面的特性:

  • 可用性
  • 一致性
  • 分區容忍度

CouchDB側重於AP(可用性和分區容忍度),這正是滿足我們的數據關注點所要尋找的資料庫(更不用說在連續的或者點對點的設備間進行數據複製的能力)。相比之下,MongoDB側重於CP(一致性和分區容忍度),像Neo4J這樣的資料庫則提供了特有的面向圖形的結構。

另一個出色的工具是這篇博客文章,它對Cassandra、MongoDB、CouchDB、Redis、Riak、Hbase和Membase做了比較。

當然,對於一個給定的項目你很可能有多個工具,換言之,這就需要明確需求並找到合適的工具以滿足這些需求。

我們將如何使用CouchDB?

我們將要構建一個簡單的本地事件資料庫,用於存儲一些事件以及這些事件發生的位置。我們將會把它分為兩個文檔,通過它們的文檔id將兩者關聯起來。這兩個文檔是:

  • 事件
  • 位置

(本文稍後會為這兩個文檔創建Java類)

Jcouchdb

我們將使用jcouchdb與CouchDB資料庫交互。這是一個經過良好測試並且易於使用的Java庫,它會自動地將Java對象序列化、反序列化進CouchDB資料庫。選擇jcouchdb的另一個原因是它和CouchDB自身的API非常相似。

Jcouchdb的替代方案有那些?

如果你不喜歡jcouchdb,或者想要嘗試其他的庫,那麼可選項也有很多,如:

  • Ektorp
  • JRelax
  • CouchDB4J
  • DroidCouch

其中有少數已經很久沒有更新了,所以,如果你要做一些測試,請確保預留一些時間解決程序的問題。

開始

從哪兒開始呢?我們將會使用Maven3構建這個示例項目。哪怕不知道Maven也能理解代碼,但是為了構建並運行示例項目你需要安裝它。可以從Maven網站找到Maven3.

指南的這個部分假定你具有一定的Maven3知識,但是如果你不了解Maven,你可以直接使用從我們的庫中下載的pom.xml文件並直接使用它。

我們將會跳過POM創建的初始部分,但是如果需要創建POM文件的細節或者僅僅想要開始編碼可以從我們的github庫中下載它。首先要做的就是指定需要的jcouchdb和Spring組件。

  <properties>      <spring.framework.version>3.1.0.RELEASE</spring.framework.version>      <spring-xml.version>2.0.0.RELEASE</spring-xml.version>      <jcouchdb.version>0.11.0-1</jcouchdb.version>  ...  </properties>  

在文件頂部指定版本信息的一個原因是,這樣可以很容易地一次性將一個庫(或者一組庫,如Spring)快速地更新到新版本。

  <dependencies>      <dependency>          <groupId>com.google.code.jcouchdb</groupId>          <artifactId>jcouchdb</artifactId>          <version>${jcouchdb.version}</version>      </dependency>      <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-context</artifactId>          <version>${spring.framework.version}</version>      </dependency>      <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-aop</artifactId>          <version>${spring.framework.version}</version>      </dependency>      <dependency>          <groupId>org.springframework</groupId>          <artifactId>spring-test</artifactId>          <version>${spring.framework.version}</version>      </dependency>      <dependency>          <groupId>org.springframework.ws</groupId>          <artifactId>spring-xml</artifactId>          <version>${spring-xml.version}</version>      </dependency>      ...  </dependencies>  

在初始化依賴設置完成之後,我們需要為項目設置剩下的目錄結構。我們將遵循標準的Maven設置:

  -src      -main          -java          -resources          -webapp      -test          -java          -resources  

設置CouchDB

完成了初始化設置之後,接下來就需要設置CouchDB資料庫了。幸運的是,有一些非常好的解決方案可以幫助我們快速地啟動並運行CouchDB。

  • Cloudant
  • Iris Couch

它們都提供了免費的賬號,能夠完美的設置好資料庫,以便我們開始開發工作。 (單擊放大圖片)

圖1.CouchAnt首頁

(單擊放大圖片)

圖2.CouchAnt Futon頁面

(單擊放大圖片)

圖3。Iris Couch註冊頁面

(單擊放大圖片)

圖4. Iris CouchFuton頁面

另一個選擇是在本地機器(或主機)上安裝CouchDB。我們並不會帶你在你的操作系統上安裝它,但是在CouchDB的wiki上有一些非常好的說明。

在賬號創建完成之後(或者在設置並啟動CouchDB之後),將需要創建一個的資料庫。在我們的應用程序中選擇了couchspring作為資料庫名。你可以隨意取名,但是當我們開始配置設置時需要將其修改為對應的名字。

在CloudAnt中,可以在資料庫截圖(圖1)中創建資料庫,而對於Iris Couch來說可以直接在Futon頁面(管理CouchDB實例的用戶界面)中創建。關於管理頁面的更多信息可以在CouchDB wiki中找到。本文並不會過多的使用管理頁面,但是這是一個非常好的操作視圖的工具。

圖5.在管理頁面中創建資料庫的步驟1

圖6.在Futon頁面中創建資料庫的步驟2

配置jcouchdb、Spring和POJOS

在新資料庫設置完成之後,我們需要:

  • 創建基礎POJO對象
  • 提供一個json配置映射,它能夠將CouchDB使用的Java對象和JSON對象自動地進行轉換
  • Spring 配置

首先,讓我們創建一些對象!

帶有自定義註解的POJOs

我們將要為事件系統創建的基礎對象是哪些?

  • Event——存儲來自於外部源(如Eventful.com)或Web界面的事件
  • Place——存儲事件的發生位置。

同時,還會結合使用一些其他的對象(從外部源中抽取數據時做一些額外的數據處理):

  • AppDocument ——由json映射實用程序所使用的用來定義文檔類型識別域的基礎對象
  • Description——用于格式化並過濾事件的描述
  • Location——用於記錄給定位置/地點的經緯度

首先,需要創建基礎類AppDocument

AppDocument.java

  package com.clearboxmedia.couchspring.domain;    import org.jcouchdb.document.BaseDocument;  import org.svenson.JSONProperty;    public class AppDocument extends BaseDocument {      /**       * Returns the simple name of the class as doc type.       *        * The annotation makes it a read-only property and also       * shortens the JSON name a little.       *       * @return document type name       */      @JSONProperty(value = "docType", readOnly = true)      public String getDocumentType()      {          return this.getClass().getSimpleName();      }    }  

該對象繼承了jcouchdb自身的BaseDocument對象,同時它還提供了一種區分不同的文檔類型的方法。CouchDB並沒有提供處理這些內容的默認方式,而是將其留給了開發者,讓他們去實現各自的處理方式。我們選擇使用類名作為識別符,例如:Event對象的docType會輸出Event,而Place對象會輸出Place。

接下來需要創建Event類。

Event.java(為簡單起見,我們省略了一些域和方法)

  package com.clearboxmedia.couchspring.domain;    import java.util.ArrayList;  import java.util.Arrays;  import java.util.Iterator;  import java.util.LinkedHashMap;  import java.util.List;  import java.util.Map;    import javax.xml.bind.annotation.XmlRootElement;  import javax.xml.bind.annotation.XmlElement;    import org.svenson.JSONProperty;    public class Event extends AppDocument {      private String id;      private String title;      private String description;      private String startTime;      private String stopTime;      private String venueId;      private Map

這裡有幾件有趣的事。首先就是我們將會在對象中存儲的是venueId而非venue,為什麼要這樣做呢?

因為CouchDB並不是關係型資料庫,它沒有一種直接的方式去定義兩個不同文檔之間的關係,因此我們在Event對象中存儲venue的id。我們可以在event對象中存儲venue對象,但是將這些對象分開存儲更清晰,尤其對於一個給定的地點可能會有多個事件。因此,我們將會提供一個動態的獲取器,僅在我們需要的時候才會檢索venue對象,而不是存儲關係。我們將會在查詢文檔部分介紹這如何實現。[todo: 動態查詢]

現在,我們來定義Place類。

Place.java

  package com.clearboxmedia.couchspring.domain;  import java.util.LinkedHashMap;  import java.util.List;    public class Place extends AppDocument {      private String id;      private String name;      private String address1;      private String address2;      private String city;      private String state;      private String postalCode;      private String lastUpdated;      private Boolean active;      private Location location;      private String venueType;      private List<String> tags;        public Place() {      }        public String getId() {          return this.id;      }        public void setId(final String id) {          this.id = id;      }        public String getName() {          return this.name;      }        public void setName(final String name) {          this.name = name;      }        public String getAddress1() {          return this.address1;      }        public void setAddress1(final String address1) {          this.address1 = address1;      }        public String getAddress2() {          return this.address2;      }        public void setAddress2(final String address2) {          this.address2 = address2;      }        public String getCity() {          return this.city;      }        public void setCity(final String city) {          this.city = city;      }        public String getState() {          return this.state;      }        public void setState(final String state) {          this.state = state;      }        public Location getLocation() {          return this.location;      }        public void setLocation(final Location location) {          this.location = location;      }        public String getVenueType() {          return this.venueType;      }        public void setVenueType(final String venueType) {          this.venueType = venueType;      }        public String getPostalCode() {          return this.postalCode;      }        public void setPostalCode(final String postalCode) {          this.postalCode = postalCode;      }        public String getLastUpdated() {          return this.lastUpdated;      }        public void setLastUpdated(final String lastUpdated) {          this.lastUpdated = lastUpdated;      }        public Boolean getActive() {          return this.active;      }        public void setActive(final Boolean active) {          this.active = active;      }        public List<String> getTags() {          return this.tags;      }        public void setTags(final List<String> tags) {          this.tags = tags;      }  }  

我們不再詳細介紹其他的輔助對象Description 或者 Location,因為它們實在是太簡單了。如果你對它們感興趣,可以從GitHub倉庫中檢出它們。

配置jcouchdb和JsonConfigFactory

在配置之前,需要創建一些即將使用的類。JsonConfigFactory用於json數據(CouchDB)和Java類之間的映射,CouchDbServerFactory為將要連接的伺服器創建新的實例。

  JsonConfigFactory.java public class JsonConfigFactory {      /**       * Factory method for creating a {@link JSONConfig}       *       * @return {@link JSONConfig} to create       */      JSONConfig createJsonConfig() {          final DateConverter dateConverter = new DateConverter();            final DefaultTypeConverterRepository typeConverterRepository =                                                   new DefaultTypeConverterRepository();          typeConverterRepository.addTypeConverter(dateConverter);          // typeConverterRepository.addTypeConverter(new LatLongConverter());            // we use the new sub type matcher            final ClassNameBasedTypeMapper typeMapper = new ClassNameBasedTypeMapper();          typeMapper.setBasePackage(AppDocument.class.getPackage().getName());          // we only want to have AppDocument instances          typeMapper.setEnforcedBaseType(AppDocument.class);          // we use the docType property of the AppDocument           typeMapper.setDiscriminatorField("docType");                  // we only want to do the expensive look ahead if we're being told to          // deliver AppDocument instances.                  typeMapper.setPathMatcher(new SubtypeMatcher(AppDocument.class));            final JSON generator = new JSON();          generator.setIgnoredProperties(Arrays.asList("metaClass"));          generator.setTypeConverterRepository(typeConverterRepository);          generator.registerTypeConversion(java.util.Date.class, dateConverter);          generator.registerTypeConversion(java.sql.Date.class, dateConverter);          generator.registerTypeConversion(java.sql.Timestamp.class, dateConverter);            final JSONParser parser = new JSONParser();          parser.setTypeMapper(typeMapper);          parser.setTypeConverterRepository(typeConverterRepository);          parser.registerTypeConversion(java.util.Date.class, dateConverter);          parser.registerTypeConversion(java.sql.Date.class, dateConverter);          parser.registerTypeConversion(java.sql.Timestamp.class, dateConverter);            return new JSONConfig(generator, parser);      }  }  

該類會創建一個生成器,它將Java類(Event或Place)轉換成對應的json,而解析器執行相反的過程。在typeMapper(生成器和解析器都會用到它)中有幾個關鍵點需要注意,特別是基礎類型和鑒別器域。

typeMapper.setEnforcedBaseType(AppDocument.class)僅會轉換繼承自AppDocument類的文檔。

typeMapper.setDiscriminatorField("docType")將使用docType域和值來鑒別不同的文檔類型。可以自由地將該域修改為其他的名字,但是這需要在AppDocument類中改變方法和json映射。為了刷新內存,可以參考下面的方法:

  @JSONProperty(value = "docType", readOnly = true)  public String getDocumentType()  {      return this.getClass().getSimpleName();  }  

要注意的最後一個點是typeMapper.setPathMatcher(new SubtypeMatcher(AppDocument.class)),它會自動查找子類型從而確保我們在繼承自AppDocument的對象間轉換。可以為檢索或查詢資料庫的幾個jcouchdb方法調用提供自己的解析器,但是在本教程不打算探討那些內容。

既然有了我們需要的類,是配置spring上下文的時候了。我們將我們的CouchDB特有的配置項分離到couchdb-config.xml中。

couchdb-config.xml

  <beans       xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xmlns:tx="http://www.springframework.org/schema/tx"       xmlns:util="http://www.springframework.org/schema/util"      xmlns:context="http://www.springframework.org/schema/context"      xsi:schemaLocation="http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans-3.1.xsd          http://www.springframework.org/schema/tx           http://www.springframework.org/schema/tx/spring-tx-3.1.xsd          http://www.springframework.org/schema/util           http://www.springframework.org/schema/util/spring-util-3.1.xsd          http://www.springframework.org/schema/context           http://www.springframework.org/schema/context/spring-context-3.1.xsd          http://www.springframework.org/schema/lang          http://www.springframework.org/schema/lang/spring-lang-3.1.xsd">          <context:annotation-config />        <bean id="properties"   class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />        <bean id="jsonConfigFactory"  class="com.clearboxmedia.couchspring.json.JsonConfigFactory"/>        <bean id="jsonConfig" factory-bean="jsonConfigFactory"       factory-method="createJsonConfig"/>        <!-- If my db requires username/password, I will need to set up a Principal -->      <bean id="couchPrincipal"  class="org.apache.http.auth.UsernamePasswordCredentials">        <constructor-arg value="${couchdb.username}" />        <constructor-arg value="${couchdb.password}" />      </bean>        <bean id="serverFactory"   class="com.clearboxmedia.couchspring.couch.CouchDbServerFactory" />        <bean id="couchDbServer" factory-bean="serverFactory"   factory-method="createCouchDbServerInstance">        <constructor-arg value="${couchdb.url}"/>        <constructor-arg name="credentials" ref="couchPrincipal" />      </bean>        <bean id="systemDatabase" class="org.jcouchdb.db.Database">          <constructor-arg ref="couchDbServer"/>          <constructor-arg value="couchspring-dev"/>          <property name="jsonConfig" ref="jsonConfig"/>      </bean>  </beans>  

我們需要做的第一件事就是使用設置註解,它設置了spring上下文的註解。接下來的兩個部分設置了jsonConfigFactory,使之做好了在伺服器實例中使用的準備。最後,創建了用於創建couchDbServer實例的serverFactory,該實例隨後和jsonConfig以及想要連接的資料庫名稱一起傳入了jcouchd database實例。所有屬性( username,password 和url)現在都是通過命令行傳入的,但是可以簡單到僅提供一個指定的屬性文件。

現在,我們已經配置好一切,是時候編寫一些測試了。

創建、保存、檢索、更新和刪除

在深入探討視圖創建之前,先從一些基礎測試開始,如:創建、更新、檢索和刪除。因為,在每一個測試中我們都會對它們做一些事情。下面是CouchSaveTest類的定義,對於其他的測試來說也是如此。

  CouchSaveTest.java (header)  @RunWith(SpringJUnit4ClassRunner.class)  @ContextConfiguration("/root-context.xml")  public class CouchSaveTest {        @Autowired      protected Database database;      ...  }  

第一個註解@RunWith告訴Maven使用SpringJUnit4ClassRunner運行該測試(不是標準的JUnit類運行器)。這樣下一個註解@ContextConfiguration("/root-context.xml")才能為這個測試啟動一個Spring上下文。該上下文會載入所有的CouchDB 實體、POJOs和它們的JSON註釋以及會自動將視圖更新到CouchDB伺服器的CouchDBUpdater。在下面的Views部分我們將會介紹最後一個註解。

最後,我們告訴Spring將資料庫自動裝配到該測試類中,使我們能夠使用它。

Document creation **文檔創建

無論在哪種類型的資料庫存儲系統中,創建新紀錄的能力(本例中是文檔)都是首要步驟之一。 使用jcouchdb的API如何實現這些呢?

  CouchSaveTest.java (testEventSave())  @Test  public void testEventSave() {      Event document = new Event();      document.setTitle("Test");      assertTrue(document.getId() == null);        database.createDocument(document);      assertTrue(document.getId() != null);  }  

這裡,我們創建了一個新的Event對象,然後將其作為參數調用了database.createDocument()方法。之後JsonConfigFactory會將我們的域映射到CouchDB文檔。[插入屏幕截圖]

文檔的檢索和更新

  CouchSaveTest.java (testEventSave_Update())  @Test  public void testEventSave_Update() {      Event document = database.getDocument(Event.class, "2875977125");      assertTrue(document != null);        document.setDescription("Testing out save");        database.createOrUpdateDocument(document);        Event newdocument = database.getDocument(Event.class, "2875977125");      assertTrue(document != null);      assertTrue(document.getDescription().equals("Testing out save"));  }  

該方法實際上測試了兩件事情,首先通過調用Event document = database.getDocument(Event.class, "2875977125");檢索文檔,檢索時傳入了文檔的id——“2875977125”。其次還測試了更新方法database.createOrUpdateDocument(document);,它的作用正如其名,或創建一個新的文檔,或更新一個已有的文檔(意味著如果在資料庫中能夠找到匹配該id的文檔時就會更新它)。

  CouchSaveTest.java (testEventSave_Exists2())  @Test(expected = IllegalStateException.class)  public void testEventSave_Exists2() {      Event document = database.getDocument(Event.class, "2875977125");      assertTrue(document != null);        database.createDocument(document);      assertFalse(document.getId().equals("2875977125"));  }  

如果我們試圖創建一個已經存在的文檔,最後一個測試會拋出一個異常(注意,我們沒有使用createOrUpdateDocument()方法)。

文檔刪除

刪除文檔和創建、更新文檔一樣簡單。

  CouchDeleteTest.java (testEventDelete())  @Test  public void testEventDelete() {      Event document = database.getDocument(Event.class, "3083848875");      assertTrue(document != null);      database.delete(document);        try {          document = database.getDocument(Event.class, "3083848875");      }      catch (Exception e) {          assertTrue(e instanceof org.jcouchdb.exception.NotFoundException);      }  }    @Test(expected = org.jcouchdb.exception.NotFoundException.class)  public void testEventDelete_NotExists() {      Event document = database.getDocument(Event.class, "-2");      assertTrue(document != null);      database.delete(document);      document = database.getDocument(Event.class, "-2");      assertTrue(document == null);  }  

這兩個測試調用了delete()方法分別對存在的和不存在的文檔進行了刪除,第二種情況會拋出NotFoundException異常。

查詢文檔

現在已經介紹完基礎的CRUD操作,那麼接下來需要做一些稍微複雜的工作。通過更多的方式查詢資料庫,而不僅僅是通過要查找的文檔的id。在本文中我們僅會探究視圖的冰山一角,因為它們可以非常複雜。關於視圖的更多內容可以在CouchDB wiki以及在線版的CouchDB: 權威嚮導中找到。

視圖介紹

首先,CouchDB視圖究竟是什麼,它如何工作?

視圖是過濾或查詢資料庫中數據的一種方式。視圖通常使用JavaScript編寫,當然使用其他語音也可以編寫視圖,但那是另一話題,我們並不會在此進行介紹。每一個視圖都將文檔內的鍵映射到值。直到文檔存取時才會更新視圖索引,但是如果你願意,你也可以通過外部腳本改變這個行為。當查詢設計文檔中的某一個視圖時該文檔中的所有視圖都會被更新。

設計文檔

在介紹視圖創建之前我們應當討論如何自動上傳應用(並保持視圖最新)。所有的視圖都捆綁到一個設計文檔。本例中我們將有兩個設計文檔:

  1. event
  2. place

org.jcouchdb.util.CouchDBUpdater類會自動地創建這兩個設計文檔,該類是在couchdb-services.xml文件中配置的。

  couchdb-services.xml    <beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xmlns:tx="http://www.springframework.org/schema/tx"        xmlns:util="http://www.springframework.org/schema/util"        xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx-2.5.xsd        http://www.springframework.org/schema/util        http://www.springframework.org/schema/util/spring-util-2.5.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-2.5.xsd">          <context:annotation-config />          <bean id="systemUpdater"          class="org.jcouchdb.util.CouchDBUpdater"          init-method="updateDesignDocuments">              <property name="createDatabase" value="true"/>              <property name="database" ref="systemDatabase"/>              <property name="designDocumentDir" value="designdocs/"/>          </bean>      </beans>  

CouchDBUpdater會監聽designdocs目錄中的變化,同時自動地將這些變化推到配置的CouchDB資料庫。

那麼designdocs目錄實際上包含什麼內容呢?

      -designdocs          -event              -allByDate.map.js              -allByParentId.map.js              -allByVenueId.map.js              -list.map.js          -place              -list.map.js  

實際上每一個目錄都映射到CouchDB中的一個設計文檔。

(點擊放大圖片)

圖7 DesignDocs事件

很好,接下來就讓我們編寫這些視圖。

我們的第一個視圖

下面是一個簡單的視圖,它會查找所有的“event” 類型的文檔。

  function(doc) {      if (doc.docType == 'Event') {          emit(doc.id, doc);      }  }  

該視圖會簡單的返回所有具有docType域並且該域值為Event的文檔的id。讓我們稍微檢查一下它做了什麼。第一行是一個JavaScript函數定義,它僅接受一個doc參數。然後就能夠檢查存儲在文檔內的值(本例中是doc.docType)。最後,調用了emit函數,它有兩個參數key和value,其中value可以為null。在本例中,key是doc.id域,value是整個文檔。

在接下來的幾個視圖示例中,emit函數才是我們實際上用到的查詢資料庫的方法。關於emit,我們需要理解的另外一關鍵點是,它會對返回的文檔按照key值進行排序。

下面是調用list視圖的測試用例。

  CouchViewTest.java (testListingQuery())      @Test      public void testListingQuery() {          String viewName = "event/list";          ViewResult results = database.queryView(viewName, Event.class, null, null);                   assertNotNull(results);          assertEquals(27, results.getRows().size());       }   

通過venue id檢索events

為了方便使用,首先需要創建的視圖之一就是通過關聯的venueId檢索給定事件集合的視圖。為此,需要編寫一個key為venueId、值為document的視圖(儘管jcouchdb的函數並沒有嚴格的需要)。那麼,該視圖是什麼樣的呢?

  function(doc) {      if (doc.type == 'Event') {          emit(doc.venueId, {doc});      }  }  

它和之前編寫的那個簡單視圖非常相似,但是這次從應用程序中調用該視圖進行查詢時需要傳遞一個venueId。

  CouchViewTest.java (testQueryByVenueId())      @Test      public void testQueryByVenueId() {          String viewName = "event/allByVenueId";              ViewAndDocumentsResult  

此處關於如何調用視圖的關鍵區別之一就是使用queryViewAndDocumentsByKeys()方法傳入了viewName、映射的Event類以及要查詢的鍵(這種情況下只會查詢出鍵值為指定venueId的事件)。

通過日期查詢事件

這兩個視圖都比較簡單。而通過日期查詢這種稍微複雜一點功能的如何實現呢?首先,我們需要定義視圖。

  function(doc) {      if (doc.docType == 'Event') {          var startDate = new Date(doc.startTime);          var startYear = startDate.getFullYear();          var startMonth = startDate.getMonth();          var startDay = startDate.getDate();          emit([              startYear,              startMonth,              startDay          ]);      }  }  

現在,我們如何調用這個函數呢?

[todo: 在java中調用視圖的代碼示例]

從事件中檢索venue的動態查詢

  CouchViewTest.java (testQueryByDate())      @Test      public void testQueryByDate() {          String viewName = "event/allByDate";          Options opts = new Options();          opts.startKey(Arrays.asList(2013, 7, 11));          opts.endKey(Arrays.asList(2013, 7, 11));            ViewAndDocumentsResult  

此處有一個名為Options的新對象,通過它可以指定想要傳入視圖的查詢選項。在本例中,我們提供了一個startKey和一個endKey去檢索對象集合。需要注意的一件事情就是,你要返回或匹配的數據需與傳入數據的類型保持一致。在本例中處理的是int類型,所以傳入的鍵必須是int類型的域。當然順序也是鍵,我們依次傳入了年、日、月從而匹配視圖中的年、日、月。

那麼,endKey是什麼呢?實際上,endKey參數允許我們指定查詢的範圍。在本例中我們選擇了相同的日期,但是可以很容易地選擇不同的值從而返回更多或更少的文檔。CouchDB會簡單地依次比較每一個鍵,直到再沒有匹配的數據時才會返回結果文檔集合。





[火星人 via ] CouchDB是什麼?為什麼我們要關注它?已經有248次圍觀

http://www.coctec.com/docs/linux/show-post-74019.html