<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Voyage系列2: API介紹

      在本章中,我們將瀏覽 Voyage 的 API。

      3.1 創建一個存儲庫

      在 Voyage 中,所有持久化對象都存儲在一個倉庫(repository)中。所使用的倉庫類型決定了對象的存儲后端。

      要使用 Voyage 的內存層,需要創建 VOMemoryRepository 的一個實例,如下所示:

      repository := VOMemoryRepository new
      

      在本文中,我們將使用 MongoDB 后端。要啟動一個新的 MongoDB 倉庫或連接到一個已有的倉庫,創建 VOMongoRepository 的一個實例,并以主機名和數據庫名為參數。例如,要連接到主機 mongo.db.url 上的數據庫 databaseName,請執行以下代碼:

      repository := VOMongoRepository
          host: 'mongo.db.url'
          database: 'databaseName'.
      

      或者,使用 host:port:database: 消息可以指定要連接的端口。最后,如果需要認證,可以使用 host:database:username:password:host:port:database:username:password: 消息來完成。

      3.2 單例模式和實例模式

      Voyage 可以工作在兩種不同的模式下:

      • 單例模式:在鏡像中有一個唯一的倉庫,它作為一個單例保存所有數據。當您使用這種模式時,可以采用“行為完整”的編程方法,其中實例響應特定的詞匯表(關于詞匯表和用法的更多詳情見下文)。

      • 實例模式:您可以在鏡像中擁有不確定數量的倉庫。當然,這種模式要求您明確指定要使用哪些倉庫。

      默認情況下,Voyage 工作在實例模式:返回的實例必須作為參數傳遞給所有數據庫 API 操作。為了避免總是保留這個實例,一個方便的替代方案是使用單例模式。單例模式消除了在所有數據庫操作中傳遞倉庫作為參數的需求。要使用單例模式,執行以下代碼:

      VOMongoRepository enableSingleton.
      

      Note 只有一個倉庫可以作為單例,因此執行這一行代碼將移除任何其他已存在的單例模式下的倉庫!在本文檔中,我們覆蓋了單例模式下的 Voyage,但使用實例模式也是非常直接的。更多信息請參見 VORepositorypersistence 協議。

      3.3 Voyage API

      以下兩張表展示了 Voyage API 的一個代表性子集。這些方法定義在 ObjectClass 上,但只有當消息的接收者(或其實例)是 Voyage 根對象時,它們才會真正執行工作。有關完整的 Voyage API,請參見這兩個類上的 voyage-model-core-extensions 持久化協議。

      首先,我們展示單例模式:

      save 將一個對象存儲進倉庫中(插入或更新)
      remove 將一個對象從倉庫中刪除
      removeAll 將一個類的所有對象從倉庫中刪除
      selectAll 檢索某一類型的所有對象
      selectOne: 檢索與參數匹配的第一個對象
      selectMany: 檢索與參數匹配的所有對象

      接下來是實例模式,在實例模式中,消息的接收者總是執行操作的倉庫。

      save: 將一個對象存儲進倉庫中(插入或更新)
      remove: 從倉庫中刪除一個對象
      removeAll: 從倉庫中刪除一個類的所有對象
      selectAll: 檢索某一類型的所有對象
      selectOne:where: 檢索與where子句匹配的第一個對象
      selectMany:where: 檢索與where子句匹配的所有對象

      3.4 重置或斷開數據庫連接

      在已部署的應用中,不應該需要關閉或重置與數據庫的連接。此外,當鏡像關閉并稍后重新打開時,Voyage 會重新建立連接。

      然而,在開發過程中,可能需要重置數據庫連接以反映更改。這在更改數據庫的存儲選項時尤為重要(參見第3.12節)。執行重置的操作如下:

      VORepository current reset.
      

      如果需要斷開與數據庫的連接,操作如下:

      VORepository setRepository: nil.
      

      3.5 測試和單例

      當我們想要測試動作是否真的在 Voyage 倉庫中保存或移除一個對象時,我們應該確保運行測試不會影響到可能正在使用的數據庫。這非常重要,因為我們面對的是單例(Singleton),它充當全局變量。我們應該確保測試是在專門為測試設置的倉庫中進行的,并且它們不會影響其他倉庫。

      這里是一個典型的解決方案:在設置過程中,我們存儲當前的倉庫,設置一個新的倉庫,并且這個新的臨時倉庫將用于測試。

      Testcase subclass: #SuperHeroTest
        instanceVariableNames: 'oldRepository'
        classVariableNames: ''
        package: 'MyVoyageTests'
      
      SuperHeroTest >> setUp
        oldRepository := VORepository current.
        VORepository setRepository: VOMemoryRepository new.
      

      在清理階段,我們恢復保存的倉庫,并丟棄新創建的臨時倉庫。

      SuperHeroTest >> tearDown
        VORepository setRepository: oldRepository
      

      3.6 存儲對象

      要存儲對象,對象的類需要被聲明為倉庫的根。所有倉庫根都是進入數據庫的入口點。Voyage 存儲的不僅僅是包含字面量的對象。完整的對象樹也可以使用 Voyage 進行存儲,并且這是透明完成的。換句話說,存儲對象樹時不需要特別處理。然而,當存儲對象圖時,必須注意打破循環。在本節中,我們討論這樣的基本對象存儲;而在第 3.12 節“增強存儲”中,我們將展示如何增強和/或修改對象的持久化方式。

      3.7 基本存儲

      假設我們想要存儲一個關聯(即一對對象)。為此,我們需要聲明 Association 類可以作為我們倉庫的根進行存儲。為了表達這一點,我們定義類方法 isVoyageRoot 以返回 true

      Association class >> isVoyageRoot
        ^ ture
      

      我們還可以通過 voyageCollectionName 類方法定義用于存儲文檔的集合名稱。默認情況下,Voyage 為每個根類創建一個 MongoDB 集合,集合名稱為類的名稱。

      接下來,要存儲一個 association 對象,我們只需要給它發送save消息:

      anAssociation := #answer->42.
      anAssociation save.
      

      這將在數據庫中生成一個包含以下結構的文檔的集合:

      {
        "_id" : ObjectId("a05feb630000000000000000"),
        "#instanceOf" : "Association",
        "#version" : NumberLong("3515916499"),
        "key" : 'answer',
        "value" : 42
      
      }
      

      存儲的數據保留了一些額外信息,以便在加載時能夠正確地重建對象:

      • instanceOf 記錄了存儲實例的類。此信息非常重要,因為集合可以包含 Voyage 根類的子類實例。

      • version 保留了提交的對象版本的標記。此屬性由 Voyage 內部用于在應用程序中刷新緩存數據。沒有版本字段,應用程序將不得不通過頻繁查詢數據庫來刷新對象。

      請注意,Voyage 生成的文檔不會直接通過 Voyage 自身可見,因為 Voyage 的目標是抽象出文檔結構。要查看實際的文檔,您需要直接訪問數據庫。對于 MongoDB,這可以通過 Mongo Browser 實現,它是作為 Voyage 的一部分加載的(World -> Tools -> Mongo Browser)。其他用于 MongoDB 的選項包括使用 mongo 命令行界面或圖形界面工具,如 RoboMongo(跨平臺)或 MongoHub(適用于 Mac)。

      3.8 嵌入對象

      對象可以非常簡單,只是字面量的關聯,也可以更復雜:對象可以包含其他對象,從而形成對象的樹。保存這樣的對象就像向他們發送save消息一樣簡單。例如,假設我們想存儲矩形,并且每個矩形包含兩個點。為了實現這一點,我們指定Rectangle類是一個文檔根,如下所示:

      Rectangle class >> isVoyageRoot
        ^ true
      

      這允許矩形被保存到數據庫中,例如,如下代碼片段所示:

      aRectangle := 42@1 corner: 10@20
      aRectangle save.
      

      這將在數據庫的矩形集合中添加一個具有以下結構的文件:

      {
        "_id" : ObjectId("ef72b5810000000000000000"),
        "#instanceOf" : "Rectangle",
        "#version" : NumberLong("2460645040"),
        "origin" : {
          "#instanceOf" : "Point",
          "x" : 42,
          "y" : 1
        },
        "corner" : {
          "#instanceOf" : "Point",
          "x" : 10,
          "y" : 20
        }
      }
      

      3.9 引用其它的根類

      有時對象是包含其他根對象的樹。例如,您可能希望將用戶和角色作為根對象保存在不同的集合中,并且一個用戶有一個角色的集合。如果嵌入的對象(角色)是根對象,Voyage 會存儲對這些對象的引用而不是將它們包含在文檔中。

      回到我們的矩形示例,假設我們希望將點保存在單獨的集合中。換句話說,現在點將被引用而不是嵌入。

      在我們為 Point 類添加 isVoyageRoot,并保存矩形之后,在矩形集合中,我們得到如下文檔:

      {
        "_id" : ObjectId("7c5e772b0000000000000000"),
        "#instanceOf" : "Rectangle",
        "#version" : 423858205,
        "origin" : {
          "#collection" : "point",
          "#instanceOf" : "Point",
          "_id" : ObjectId("7804c56c0000000000000000")
        },
        "corner" : {
          "#collection" : "point",
          "#instanceOf" : "Point",
          "_id" : ObjectId("2a731f310000000000000000")
        }
      }
      

      此外,在點集合中,我們還會得到以下兩個實體:

      {
        "_id" : ObjectId("7804c56c0000000000000000"),
        "#version" : NumberLong("4212049275"),
        "#instanceOf" : "Point",
        "x" : 42,
        "y" : 1
      }
      
      {
        "_id" : ObjectId("2a731f310000000000000000"),
        "#version" : 821387165,
        "#instanceOf" : "Point",
        "x" : 10,
        "y" : 20
      }
      

      3.10 在圖中打破循環

      當要存儲的對象包含嵌入對象的圖而不是樹時,即當嵌入對象之間的引用存在循環時,必須打破這些循環。如果不這樣做,存儲對象將導致無限循環。最直接有效的解決方案是將引起循環的一個對象聲明為 Voyage 根對象。這在存儲時有效地打破了循環,避免了無限循環。

      例如,在矩形示例中,假設我們在矩形內部有一個標簽,并且該標簽包含一段文本。這段文本也保留了對其所包含的標簽的引用。換句話說,標簽和文本之間存在循環引用。為了持久化矩形,必須打破這種循環。為此,要么將標簽聲明為 Voyage 根對象,要么將文本聲明為 Voyage 根對象。

      打破循環的另一種解決方案是避免聲明新的 Voyage 根對象,可以將對象的一些字段聲明為瞬態(transient),并定義如何在加載時重建圖。這個話題將在后面討論。

      3.11 在 Mongo 中存儲日期的實例

      Mongo 的一個已知問題是它不區分 Date 和 DateAndTime,因此即使你存儲的是 Date 實例,你也會檢索到 DateAndTime 實例。在物化對象時,你需要手動將其轉換回 Date

      3.12 增強存儲

      通過向類添加Magritte描述,可以改變對象的存儲方式。本節首先探討對象存儲格式的配置選項,隨后深入講解更進階的概念——例如屬性的加載與保存,這類技術可用于解決嵌入式對象中的循環引用問題。

      配置存儲

      繼續使用矩形示例,但使用嵌入的點,我們添加以下存儲要求:

      • 我們需要使用一個名為 rectanglesForTest 的不同集合,而不是 rectangle

      • 我們只在該集合中存儲 Rectangle 類的實例,因此 instanceOf 信息是冗余的。

      • origincorner 屬性也只會是 Point,因此他倆的 instanceOf 信息也是冗余的。

      要實現這一點,我們使用帶有特定指令的 Magritte 描述來聲明類的屬性,并描述 origincorner 屬性。

      mongoContainer方法的定義包含兩個要點:首先通過pragma指令<mongoContainer>聲明該類所使用的數據容器;其次返回一個經過特定配置的VOMongoContainer實例——該實例被設置為使用數據庫中的rectanglesForTest集合,且僅存儲Rectangle類型的對象實例。

      需要注意的是,這兩行配置并非必須同時指定。開發人員可以僅聲明使用rectanglesForTest集合,或只限定該集合專用于存儲Rectangle實例,這兩種單獨配置方式同樣有效。

      Rectangle class>>mongoContainer
        <mongoContainer>
      
        ^ VOMongoContainer new
          collectionName: 'rectanglesForTest';
          kind: Rectangle;
          yourself
      

      另外兩個方法通過pragma指令<mongoDescription>進行聲明,并返回經過配置的Mongo描述對象。這些描述對象分別設置了對應的屬性名稱和類型,具體實現如下:

      Rectangle class>>mongoOrigin
        <mongoDescription>
      
        ^ VOMongoToOneDescription new
          attributeName: 'origin';
          kind: Point;
          yourself
      
      Rectangle class>>mongoCorner
        <mongoDescription>
      
        ^ VOMongoToOneDescription new
          attributeName: 'corner';
          kind: Point;
          yourself
      

      在重置資源庫時,需執行以下操作:

      VORepository current reset
      

      此時,保存在rectanglesForTest集合中的矩形對象,其存儲結構將大致呈現如下形式:

      {
        "_id" : ObjectId("ef72b5810000000000000000"),
        "#version" : NumberLong("2460645040"),
        "origin" : {
          "x" : 42,
          "y" : 1
        },
        "corner" : {
          "x" : 10,
          "y" : 20
        }
      }
      

      屬性描述的其他配置選項包括:

      • beEager用于聲明被引用的實例需要立即加載(默認設置為延遲加載)。

      • beLazy 聲明被引用的實例將采用延遲加載。

      • convertNullTo: 在檢索到值為Null(nil)的對象時,將返回傳入代碼塊的執行結果作為替代。

      對于集合類型的屬性,需要返回VOMongoToManyDescription而非VOMongoToOneDescription。上述所有配置選項在此情況下依然適用,其中kind:配置項用于指定該集合所包含值的類型。

      VOMongoToManyDescription 提供了若干額外配置選項:

      • kindCollection: 用于指定屬性中所包含集合的類別。

      • convertNullToEmpty 在檢索到值為 Null(nil)的集合時,會返回一個空集合。

      3.13 屬性的自定義加載與保存

      可以為對象屬性的數據庫讀寫操作編寫特定的轉換邏輯。這種機制可用于解決對象圖中的循環引用問題,而無需聲明額外的Voyage根對象。要聲明此類自定義邏輯,需要定義一個包含Smalltalk代碼塊的MAPluggableAccessor,分別用于從對象讀取屬性值以及向對象寫入屬性值。需要注意的是,這些訪問器的命名可能違反直覺:read:訪問器定義的是將存儲到數據庫中的值,而write:訪問器定義的則是將數據庫檢索值轉換為對象屬性值的邏輯。這是因為對象-文檔映射器在讀取對象存儲到數據庫時使用read:訪問器,在基于數據庫取值將數據寫入內存對象時使用write:訪問器。

      定義自定義訪問器可實現如下場景:例如將Amount對象中包含的Currency對象,以其三字母縮寫形式(如EUR、USD、CLP等)存入數據庫。當加載該數據表示時,需將其轉換回Currency對象(例如通過實例化新的Currency對象)。具體實現方式如下:

      Amount class>>mongoCurrency
        <mongoDescription>
      
        ^ VOMongoToOneDescription new
          attributeName: 'currency';
          accessor: (MAPluggableAccessor
            read: [ :amount | amount currency abbreviation ]
            write: [ :amount :value | amount currency: (Currency
            fromAbbreviation: value) ]);
          yourself
      

      此外,可通過在屬性描述符或容器描述符中添加postLoad:動作,為某個屬性或包含它的對象定義加載后操作。該操作是一個單參數代碼塊,會在對象加載到內存后執行,并以被加載的對象作為參數。

      最后,通過返回VOMongoTransientDescription實例作為屬性描述符,可以將屬性排除在存儲(及檢索)范圍之外。這為待保存的對象圖提供了截斷點,例如當對象包含不應持久化至數據庫的引用數據時。該機制也可用于破除存儲對象圖中的循環引用。但需要注意的是,從數據庫檢索對象圖時,包含這些對象的屬性將被設置為nil。為解決此問題,可在屬性描述符或容器描述符中指定加載后操作,將這些屬性設置為正確的值。

      以下示例聲明了'currencyMetaData'屬性將被排除在存儲范圍之外。

      Amount class>>mongoCurrencyMetaData
        <mongoDescription>
      
        ^VOTransientDescription new
          attributeName: 'currencyMetaData';
          yourself
      

      3.14 關于OID的幾點說明

      MongoDB的對象標識符(OID)是一個充當主鍵的唯一字段。這種12字節的BSON類型數據由以下部分構成:

      • 一個4字節數值,表示自Unix紀元以來經過的秒數,

      • 一個3字節的機器標識符,

      • 一個2字節的進程ID,

      • 以及一個從隨機值開始的3字節計數器。

      被添加到Mongo根集合的對象會獲得一個唯一標識符,即OID實例。當您創建此類對象后,通過向其發送voyageId消息獲取OID時,將返回該標識符。OID實例變量的值是一個與Mongo的ObjectId對應的大型正整數。

      可以創建并使用自定義的OID實現,并將這些對象存入Mongo數據庫。但不建議這樣做,因為您可能無法再通過OID(使用voyageId)查詢這些對象——這是由于Mongo要求特定的格式。若執意使用自定義格式,應通過Mongo控制臺驗證其格式,例如使用如下查詢。若返回錯誤提示“Error: invalid object id: length”,則表示無法通過ID查詢該對象。

      > db.Trips.find({"person._id" : ObjectId("190372")})
      Fri Aug 28 14:21:10.815 Error: invalid object id: length
      

      MongoDB格式的OID還有一個額外優勢:它們按創建日期和時間排序,因此您無需額外操作即可獲得一個帶索引的"creationDateAndTime"屬性(因為OID的_id字段上存在不可刪除的索引)。

      3.15 Voyage中的查詢操作

      Voyage支持通過數據庫查詢選擇性檢索對象實例。使用內存層時,查詢采用標準的Smalltalk代碼塊;使用MongoDB后端時,則通過MongoDB查詢語言執行搜索。為指定這些查詢,MongoDB采用JSON結構,而在Voyage中可通過兩種方式構建查詢:根據復雜程度的不同,MongoDB查詢既可編寫為代碼塊形式,也可編寫為字典形式。本節將首先探討這兩種查詢創建方式,最后闡述如何執行這些查詢。

      3.16 使用代碼塊或mongoQuery進行基礎對象檢索

      查詢數據庫最直接的方式是:使用內存層時采用代碼塊,使用MongoDB后端時采用MongoQuery。由于代碼塊屬于標準Smalltalk用法,本次討論我們將重點聚焦于MongoQuery的使用。

      MongoQuery并非Voyage本身的組成部分,而是Voyage用于與MongoDB交互的MongoTalk層的一部分。MongoTalk由Nicolas Petton開發,提供了訪問MongoDB的所有底層操作。在特定限制下,MongoQuery能夠將常規Pharo代碼塊轉換為符合數據庫預期格式的JSON查詢。本質上,MongoQuery是一種用于創建MongoDB查詢的嵌入式領域特定語言。使用MongoQuery時,查詢語句看起來像是普通的Pharo表達式(但該語言的功能比原生Smalltalk更為受限)。

      使用MongoQuery時,查詢中可使用以下運算符:

      < <= > >= = ~= 常規比較運算符
      & AND運算符
      | OR運算符
      not NOT運算符
      at: 訪問嵌入的文檔
      where: 執行Javascript查詢

      例如,以下查詢將選擇數據庫中所有名稱為John的記錄:

      [ :each | each nam = 'John' ]
      

      稍復雜的查詢示例:查找數據庫中所有名稱是John且訂單數量大于10的記錄。

      [ :each | (each name = 'John') & (each orders > 10) ]
      

      需要注意的是,這種查詢方式僅適用于對象屬性值的查詢,不適用于對其他對象引用的查詢。對于引用查詢,應沿用關系型數據庫的傳統方式通過ID構建查詢(我們接下來會討論)。但遵循MongoDB設計理念的最佳解決方案是:重新審視對象模型,避免使用外鍵形式的關系表達。

      3.17 查詢包含其他根文檔元素的查詢

      在No-SQL數據庫中,無法跨集合查詢(即實現SQL中的JOIN操作)。您有兩種選擇:按前文建議修改數據模型,或編寫應用層代碼模擬JOIN行為。后一種方案可通過以下方式實現:對已通過先前查詢返回的對象發送voyageId消息,然后使用該ID匹配另一個對象。以下示例演示了如何將顏色color與參考顏色refCol進行匹配:

      [ :each | (each at: 'color._id') = refCol voyageId ]
      

      3.18 使用at:消息訪問嵌入式文檔

      由于MongoDB能夠存儲任意復雜度的文檔,常見的情況是一個文檔由多個嵌入式文檔組成,例如:

      {
        "origin" : {
          "x" : 42,
          "y" : 1
        },
        "corner" : {
          "x" : 10,
          "y" : 20
        }
      }
      

      在此情況下,若要通過嵌入式文檔元素搜索對象,需使用at:消息及字段分隔符“.”。例如,要選擇所有原點x值等于42的矩形,查詢語句如下所示。

      [ :each | (each at: 'origin.x') = 42 ]
      

      3.19 使用where:消息執行Javascript比較

      對于超出MongoQuery甚至MongoDB查詢語言能力的查詢需求,MongoDB提供了通過$where操作符直接編寫Javascript查詢的方式。在MongoQuery中也可通過發送where:消息實現:

      以下示例我們使用Javascript表達式重寫之前的查詢:

      [ :each | each where: 'this.origin.x == 42' ]
      

      關于$where操作符更完整的使用文檔請參閱MongoDB的官方文檔。

      3.20 使用JSON查詢

      當MongoQuery無法滿足查詢表達需求時,可改用JSON查詢。JSON查詢是MongoDB查詢的內部表示形式,在Voyage中可直接創建。簡而言之:JSON結構被映射為鍵值對構成的字典。其中鍵為字符串,值可以是基本類型、集合或另一個JSON結構(即嵌套字典)。創建查詢時,我們只需構建符合這些要求的字典即可。

      注意 JSON查詢嚴格限定于使用MongoDB后端的情況。其他后端(例如內存層)不提供對JSON查詢的支持。

      例如,首個使用MongoQuery的示例若以字典形式編寫,其代碼如下:

      { 'name' -> 'John' } asDictionary
      

      字典鍵值對采用AND語義進行組合。篩選名稱值為John且訂單數量大于10的記錄可編寫如下:

      {
        'name' -> 'John'.
        'orders' -> { '$gt' : 10 } asDictionary
      } asDictionary
      

      要構建"大于"條件語句,需要創建新字典并使用MongoDB的$gt查詢選擇器來表達大于關系。有關可用查詢選擇器的完整列表,請參閱MongoDB查詢選擇器官方文檔。

      通過OID查詢對象

      若已知文檔的ObjectId,可通過該值創建OID實例并進行查詢。

      {('_id' -> (OID value: 16r55CDD2B6E9A87A520F000001))} asDictionary.
      

      請注意,以下兩種寫法是等價的:

      OID value: 26555050698940995562836590593. "dec"
      OID value: 16r55CDD2B6E9A87A520F000001. "hex"
      

      注意 如果您擁有位于根集合中的對象實例,可以通過獲取其voyageId并在查詢中使用該ObjectId。

      使用點標記法訪問嵌入式文檔

      在JSON查詢中訪問嵌入式文檔的值時,需使用點標記法。例如,查詢原點x值為42的矩形可表示為:

      {
        'origin.x' -> {'$eq' : 42} asDictionary
      } asDictionary
      

      在查詢中表達OR條件

      要表達OR條件,需要創建一個鍵為'$or'、值為條件表達式的字典。以下示例演示如何選擇名稱是John且訂單數超過10的對象,或者名稱不是John且訂單數不超過10的對象:

      { '$or' :
        {
          {
            'name' -> 'John'.
            'orders' -> { '$gt': 10 } asDictionary
          } asDictionary.
          {
            'name' -> { '$ne': 'John'} asDictionary.
            'orders' -> { '$lte': 10 } asDictionary
          } asDictionary.
        }.
      } asDictionary.
      

      突破MongoQuery的功能限制

      使用JSON查詢可以實現MongoQuery不支持的功能,例如使用正則表達式。以下查詢用于搜索所有fullname.lastName以字母D開頭的文檔:

      {
        'fullname.lastName' -> {
          '$regexp': '^D.*'.
          '$options': 'i'.
        } asDictionary.
      } asDictionary.
      

      正則表達式的選項i表示不區分大小寫。更多選項請參閱$regex操作符的文檔說明。

      此示例僅簡要展示了JSON查詢的強大功能。實際上可以構建更多不同類型的查詢,完整運算符及用法請參閱MongoDB操作符文檔。

      3.21 執行查詢

      Voyage提供了一組執行搜索的方法。為演示這些方法的使用,我們將采用先前介紹的已存儲Point示例。請注意,除非特別說明,本節所有查詢均可使用MongoQuery或JSON查詢兩種方式編寫。

      3.22 基礎對象檢索

      以下方法提供基礎的對象檢索功能。

      • selectAll 獲取對應數據庫集合中的所有文檔。例如,Point selectAll 將返回所有Point對象。

      • selectOne 獲取與查詢條件匹配的單個文檔。該方法對應detect:操作,接收查詢規范作為參數(可以是MongoQuery或JSON查詢)。例如:Point selectOne: [:each | each x = 42] 或等價的 Point selectOne: { 'x' -> 42 } asDictionary

      • selectMany 獲取所有與查詢條件匹配的文檔。該方法對應select:操作,與上述方法類似,接收查詢規范作為參數。

      3.23 限制對象檢索與排序

      查詢數據庫的方法看似與Collection層次結構中的等效方法相似。然而,常規集合能完全在內存中操作,而Voyage集合查詢通常需要定制化以優化內存消耗和/或訪問速度。這是因為每個集合可能包含數百萬個文檔,超出Pharo的內存限制,同時數據庫搜索的性能也遠高于Pharo中的等效代碼。

      查詢的第一項優化在于限制返回的結果數量。系統會從所有匹配的文檔集合中,返回以指定參數為起始索引的子集。這可用于僅獲取查詢結果的前N個匹配項,或以更小的數據塊遍歷查詢結果,如下文簡單分頁示例所示。

      • selectMany:limit: 從數據庫中獲取與查詢條件匹配的對象集合,最多返回指定數量的結果。例如:Point selectMany: [:each | each x = 42] limit: 10

      • selectMany:limit:offset: 從數據庫中獲取與查詢條件匹配的對象集合。返回結果從查詢結果的偏移量位置+1處開始,最多返回限定數量的對象。例如,若上述查詢匹配到25個點,則通過Point selectMany: [:each | each x = 42] limit: 20 offset: 10可返回最后15個點(本例中任何大于15的limit參數均可實現此效果)。

      可實施的第二項定制是對結果進行排序。為此,VOOrder類提供了用于指定升序或降序排列的常量。

      • selectAllSortBy: 獲取所有文檔,并按參數中的規范進行排序(該參數需為JSON查詢)。例如,Point selectAllSortBy: { #x -> VOOrder ascending} asDictionary 會按x值升序返回所有點。

      • selectMany:sortBy: 獲取所有與查詢條件匹配的文檔并對其進行排序。例如,要返回x值為42且按y值降序排列的點:Point selectMany: { 'x' -> 42 } asDictionary sortBy: { #y -> VOOrder descending } asDictionary

      • selectMany:sortBy:limit:offset: 為上述查詢提供限定返回數量和偏移量的參數設置。

      3.24 簡單分頁器示例

      通常,您可能只想顯示集合中特定范圍內的對象,例如前25個,或從第25到第50個,依此類推。這里我們展示一個簡單的分頁器,它通過使用selectMany:limit:offset:方法來實現這一功能。

      首先我們創建一個名為Paginator的類。要實例化它,需要提供一個Voyage根對象(aClass)和一個查詢條件(aCondition)。

      Object subclass: #Paginator
        instanceVariableNames: 'collectionClass where pageCount'
        classVariableNames: ''
        package: 'DemoPaginator'
      
      Paginator class>>on: aClass where: aCondition
        ^ self basicNew
                initializeOn: aClass where: aCondition
      
      Paginator>>initializeOn: aClass where: aCondition
        self initialize.
        collectionClass := aClass.
        where := aCondition
      

      接著我們定義計算邏輯:根據頁面大小和實體總數來獲取總頁數。

      Paginator>>pageSize
      	^ 25
      
      Paginator>>pageCount
      	^ pageCount ifNil: [ pageCount := self calculatePageCount ]
      
      Paginator>>calculatePageCount
      	| count pages |
      	count := self collectionClass count: self where.
      	pages := count / self pageSize.
      	count \\ self pageSize > 0
      		ifTrue: [ pages := pages + 1].
      	^ count
      

      隨后,用于獲取指定頁面元素的查詢按如下方式實現:

      Paginator>>page: aNumber
      	^ self collectionClass
      		selectMany: self where
      		limit: self pageSize
      		offset: (aNumber - 1) * self pageSize
      

      3.25 創建與刪除索引

      MongoDB中有許多實用功能在Voyage中并未提供,但仍可通過Pharo環境進行操作,其中最重要的就是索引管理功能。

      3.26 使用OSProcess創建索引

      目前尚無法通過Voyage直接創建和刪除索引,但可以通過OSProcess來實現這一功能。

      例如,假設存在一個名為myDB的數據庫,其中包含名為Trips的集合。行程數據中包含一個嵌入的收據集合,而收據具有名為description的屬性。以下操作將在description字段上創建索引:

      OSProcess command:
      	'/{pathToMongoDB}/MongoDB/bin/mongo --eval ',
      	'"db.getSiblingDB(''myDB'').Trips.',
      	'createIndex({''receipts.description'':1})"'
      

      要刪除Trips集合上的所有索引,可以按以下方式操作:

      OSProcess command:
      	'/{pathToMongoDB}/MongoDB/bin/mongo --eval ',
      	'"db.getSiblingDB(''myDB'').Trips.dropIndexes()"'
      

      3.27 驗證索引的使用情況

      要確認查詢是否確實使用了索引,可以在mongo控制臺中使用".explain()"方法。例如,如果我們按上述方法在description字段上添加索引,然后運行查詢并添加.explain(),就能看到僅掃描了部分文檔。

      > db.Trips.find({"receipts.description":"a"})
      						 .explain("executionStats")
      {
      	"cursor" : "BtreeCursor receipts.receiptDescription_1",
      	"isMultiKey" : true,
      	"n" : 2,
      	"nscannedObjects" : 2,
      	"nscanned" : 2,
      	"nscannedObjectsAllPlans" : 2,
      	"nscannedAllPlans" : 2,
      
        [...]
      }
      

      移除索引后,系統會掃描所有文檔(此示例中共有246個文檔):

      > db.Trips.find({"receipts.description":"a"}
      					  ..explain("executionStats")
      {
      	"cursor" : "BasicCursor",
      	"isMultiKey" : false,
      	"n" : 2,
      	"nscannedObjects" : 246,
      	"nscanned" : 246,
      	"nscannedObjectsAllPlans" : 246,
      	"nscannedAllPlans" : 246,
      
        [...]
      }
      

      3.28 結論

      本章我們介紹了Voyage——一個持久化編程框架。Voyage的優勢在于其對象文檔映射器與MongoDB后端支持。我們演示了如何存儲和刪除數據庫中的對象,以及如何優化存儲格式。隨后深入探討了數據庫查詢操作,展示了兩種構建查詢的方式并詳述了查詢執行機制。最后,盡管Voyage未直接提供索引構建支持,我們仍展示了在MongoDB數據庫中創建索引的方法。

      posted @ 2025-10-27 09:14  fmcdr  閱讀(4)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 高潮喷水抽搐无码免费| 亚洲三区在线观看无套内射| 67194熟妇在线观看线路| 91亚洲精品一区二区三区| 国产不卡一区二区在线| 巨大黑人极品videos精品| 亚洲国产一成人久久精品| 绥滨县| 亚洲a人片在线观看网址| 香港日本三级亚洲三级| 静乐县| 国产AV永久无码青青草原| 少妇人妻偷人精品一区二| 亚洲国产精品综合一区二区 | 欧美国产日韩久久mv| 一区二区三区av天堂| 日韩精品一区二区三区蜜臀| 亚洲码国产精品高潮在线| 欧美人与动zozo在线播放| 白嫩少妇bbw撒尿视频| 亚洲黄日本午夜一区二区| 亚洲精品日韩久久精品| 免费人成网站免费看视频| 亚洲AVAV天堂AV在线网阿V| 亚洲男女一区二区三区| 鲁丝片一区二区三区免费| 久热re这里精品视频在线6| AV秘 无码一区二| 最新精品国偷自产在线美女足| 亚洲深夜精品在线观看| 久久国产精品日本波多野结衣| 狠狠v日韩v欧美v| 青青青视频免费一区二区| 亚洲男女羞羞无遮挡久久丫| 国产午夜精品亚洲精品国产| 周至县| 亚洲真人无码永久在线| 成人3D动漫一区二区三区 | 无码熟妇人妻av影音先锋| 欧美成人精品三级网站视频| 欧美午夜小视频|