dojo/store/Memory 和 SimpleQueryEngine 解讀 (非常簡單的存儲器管理工具 和 非常簡單的查詢引擎)

来源:http://www.cnblogs.com/ProgrammerZhong/archive/2016/09/22/5830114.html
-Advertisement-
Play Games

一、介紹(官方API): The Memory store provides full read and write capabilities for in memory data. The Memory store is very simple to use, just provide an ar ...


一、介紹(官方API):

The Memory store provides full read and write capabilities for in memory data. The Memory store is very simple to use, just provide an array of objects. The Memory store is also a synchronous store, which also simplifies its usage. All the functions directly return results, so you don’t have to use asynchronous callbacks in your code.

大致翻譯:Memory store 提供了對記憶體可讀可寫的管理單元。它非常簡單,易於使用,內部僅提供了一個對象數組。Memory store是一個同步存儲器,自然功能簡單易用了。所有的方法都是直接返回結果,你不需要去編寫非同步的回調函數。

Example:

require(["dojo/store/Memory"], function(Memory){
    var someData = [
        {id:1, name:"One"},
        {id:2, name:"Two"}
    ];
    store = new Memory({data: someData});

    store.get(1) -> Returns the object with an id of 1

    store.query({name:"One"}) // 根據特定的查詢條件,返回符合要求的結果集。

    store.query(function(object){
        return object.id > 1;
    }) // 通過傳遞一個函數來進行查詢,可以實現更複雜的查詢操作

    store.query({name:"One"}, {sort: [{attribute: "id"}]}) // 除了查詢以外,返回的結果集會根據id來排序。

    store.put({id:3, name:"Three"}); //保存{id:3, name:"Three"} 對象,並且放在id:3的位置。
    store.remove(3); // 根據ID來刪除對象
});

 

  官方API的介紹和切合要點,Memory store的最顯著的特點,就是“簡單”。Memory store的功能就像上面的例子中的一樣,簡單暴力,基本齊全。下麵我們根據dojo里的源碼進一步解析:

 二、dojo/store/Memory

構造函數constructor

  constructor: function(options){
        // summary:
        //        Creates a memory object store.
        // options: dojo/store/Memory
        //        This provides any configuration information that will be mixed into the store.
        //        This should generally include the data property to provide the starting set of data.
        for(var i in options){
            this[i] = options[i];
        }
        this.setData(this.data || []);
    },

  嗯,我們通過new欄位來創建memory store到對象時,就會調用到這裡。案例中的options參數為{data:somedata}。實際上是創建了最最簡單的Memory stortt僅僅只用了一個對象數組。所以當然咯,可以有略複雜一點的情況。代碼中this[i]=options[i],可以看出各種配置都體現在Menory對象的屬性里。下麵列一下:

  // data: Array
    //        The array of all the objects in the memory store
    data:null,

    // idProperty: String
    //        Indicates the property to use as the identity property. The values of this
    //        property should be unique.
    idProperty: "id",

    // index: Object
    //        An index of data indices into the data array by id
    index:null,

    // queryEngine: Function
    //        Defines the query engine to use for querying the data store
    queryEngine: SimpleQueryEngine,

  data:是必須的,如果不存在,會被賦予一個[]空數組。

  index:後續會提到這個屬性,它是一個以id("idProperty") 為索引的表,表的數據是一個改id對應的屬性在data數組裡的序號索引。

  idProperty:啊哈,通過這個屬性我們可指定改memory的一個不可重覆的標識符(欄位名),預設是為"id"。

  queryEngine:可以指定memory的搜搜引擎,預設使用的是dojo/store/util/SimpleQueryEngine。

 

setData: function(data){
        // summary:
        //        Sets the given data as the source for this store, and indexes it
        // data: Object[]
        //        An array of objects to use as the source of data.
        if(data.items){
            // just for convenience with the data format IFRS expects
            this.idProperty = data.identifier || this.idProperty;
            data = this.data = data.items;
        }else{
            this.data = data;
        }
        this.index = {};
        for(var i = 0, l = data.length; i < l; i++){
            this.index[data[i][this.idProperty]] = i;
        }
    }
setData

  setData可以外部調用,用來更換該Memory的數據源,比如setData([])可以清空。

  有一意思的是,從setData的函數體里可以發現,除了官方例子的創建memory的格式,setData還為IFRS開了一個方便之門。我不太瞭解IFRS會使用怎麼樣的數據格式,從代碼中可以看出:1、標識符存放在data.identifier;2、數據存在data.items里。不過當然的,data.items仍被要求是一個有序對象數組

  我們知道構造函數中數據的初始化也是通過setData實現的,在setData的最後面,實際也是this.index的初始化。在迴圈中構建利用this.index[data[i][this.idProperty]] = i; 來創建一個以idproperty為索引的散列表。該散列表保存著對應數據在data里的位置,這樣形式的散列表在訪問數據時可是非常方便。

 

 get: function(id){
        // summary:
        //        Retrieves an object by its identity
        // id: Number
        //        The identity to use to lookup the object
        // returns: Object
        //        The object in the store that matches the given id.
        return this.data[this.index[id]];
    },
get

  通過標識符的值,來獲取對應對象。

  this.index[id] 取出其對應數據的數組位序,然後通過序號去對象數組中取對象。

 

 getIdentity: function(object){
        // summary:
        //        Returns an object's identity
        // object: Object
        //        The object to get the identity from
        // returns: Number
        return object[this.idProperty];
    },
getIdentity

  獲取對象的標識符值

  我們通過它,可以用index[getIdentity(object)]來獲取對象在數組中的位置了。

 

put: function(object, options){
        // summary:
        //        Stores an object
        // object: Object
        //        The object to store.
        // options: dojo/store/api/Store.PutDirectives?
        //        Additional metadata for storing the data.  Includes an "id"
        //        property if a specific id is to be used.
        // returns: Number
        var data = this.data,
            index = this.index,
            idProperty = this.idProperty;
        var id = object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
        if(id in index){
            // object exists
            if(options && options.overwrite === false){
                throw new Error("Object already exists");
            }
            // replace the entry in data
            data[index[id]] = object;
        }else{
            // add the new object
            index[id] = data.push(object) - 1;
        }
        return id;
    },
put

  保存對象。

  註意到保存操作是有可選參數的,從源碼看看都有些什麼。

  註意到保存對象id的獲取規則:配置里的“id”欄位>對象里的標識符欄位>一個隨機數字。

  如果id已經存在,根據"overwrite"欄位,為真則覆蓋,假則報錯。   如果id不存在,數據存入對象數組最後一位上。   put方法會返回保存對象的標識符值。  
    add: function(object, options){
        // summary:
        //        Creates an object, throws an error if the object already exists
        // object: Object
        //        The object to store.
        // options: dojo/store/api/Store.PutDirectives?
        //        Additional metadata for storing the data.  Includes an "id"
        //        property if a specific id is to be used.
        // returns: Number
        (options = options || {}).overwrite = false;
        // call put with overwrite being false
        return this.put(object, options);
    },
add

  保存一個新的對象,可以指定保存的id,但不可覆蓋。

  其實和put是本質是一樣的操作,但更保險,參數overwrite被確定為false。

 

remove: function(id){
        // summary:
        //        Deletes an object by its identity
        // id: Number
        //        The identity to use to delete the object
        // returns: Boolean
        //        Returns true if an object was removed, falsy (undefined) if no object matched the id
        var index = this.index;
        var data = this.data;
        if(id in index){
            data.splice(index[id], 1);
            // now we have to reindex
            this.setData(data);
            return true;
        }
    },
remove

  通過標識符值,刪除指定對象。

  函數體很簡單,刪除後通過setData更新index的內容。操作成功會返回true。

 

    query: function(query, options){
        // summary:
        //        Queries the store for objects.
        // query: Object
        //        The query to use for retrieving objects from the store.
        // options: dojo/store/api/Store.QueryOptions?
        //        The optional arguments to apply to the resultset.
        // returns: dojo/store/api/Store.QueryResults
        //        The results of the query, extended with iterative methods.
        //
        // example:
        //        Given the following store:
        //
        //     |    var store = new Memory({
        //     |        data: [
        //     |            {id: 1, name: "one", prime: false },
        //    |            {id: 2, name: "two", even: true, prime: true},
        //    |            {id: 3, name: "three", prime: true},
        //    |            {id: 4, name: "four", even: true, prime: false},
        //    |            {id: 5, name: "five", prime: true}
        //    |        ]
        //    |    });
        //
        //    ...find all items where "prime" is true:
        //
        //    |    var results = store.query({ prime: true });
        //
        //    ...or find all items where "even" is true:
        //
        //    |    var results = store.query({ even: true });
        return QueryResults(this.queryEngine(query, options)(this.data));
    },
query

  強大的query查詢功能。

  參數可以是欄位值,來查詢匹配欄位的結果;也可以是一個函數,返回真假,來進行複雜的判斷。

  實際上是調用了memory store的搜索引擎,這個搜索引擎預設是/util/SimpleQueryEngine。

 

三、SimpleQueryEngine

進一步的,我們去看看SimpleQueryEngine是如何實現搜索的。

首先,模塊返回的是一個形參為(query, options)的工具函數。

return function(query, options){
  ...      
}

函數體主要為兩部分,第一個switch,第二個execute方法。

switch部分:

switch(typeof query){
        default:
            throw new Error("Can not query with a " + typeof query);
        case "object": case "undefined":
            var queryObject = query;
            query = function(object){
                for(var key in queryObject){
                    var required = queryObject[key];
                    if(required && required.test){
                        // an object can provide a test method, which makes it work with regex
                        if(!required.test(object[key], object)){
                            return false;
                        }
                    }else if(required != object[key]){
                        return false;
                    }
                }
                return true;
            };
            break;
        case "string":
            // named query
            if(!this[query]){
                throw new Error("No filter function " + query + " was found in store");
            }
            query = this[query];
            // fall through
        case "function":
            // fall through
    }

有四種格式的查詢條件可以通過,string,function,object,undefined

functiion格式在此直接不處理,往後跑。

string格式為認為是函數體名稱,在當前上下文找該函數體。

當query格式為object和undefined時(其實就是object時):

  則生成查詢函數,查詢函數邏輯為,針對object的每一個屬性:

  1、如果有test方法,則執行test方法;

    (想到什麼了嗎?如果提供的是一個RegExpObject,因持有test方法,則會執行test方法,所以該查詢引擎可以支持正則表達式查詢)

  2、如果沒有test方法,則直接進行比較。

總的來說,如果query的格式是一個object,則更像一個複合條件:

舉個例子:

假如待查詢數據為:
data=[
    {id:1,grade:1,class:1,peopleNum:5},
    {id:2,grade:1,class:2,peopleNum:10},
    {id:3,grade:1,class:3,peopleNum:13},
    {id:4,grade:2,class:1,peopleNum:10},
    {id:5,grade:2,class:2,peopleNum:20},
    {id:6,grade:2,class:3,peopleNum:7}
]
假如:
query
={   test:function(object){     return (object.peopleNum>10} } } //查詢結果為對象peopleNum屬性大於10的結果,即 //[ // {id:3,grade:1,class:3,peopleNum:13}, // {id:5,grade:2,class:2,peopleNum:20} //]

又假如: query={   test:function(object){     rreturn (object.peopleNum>10} }, grade:2, } //查詢結果為對象peopleNum屬性大於10,且grade等於2的結果,即 //[ // {id:5,grade:2,class:2,peopleNum:20} //]

 

繼續往下看,我們看execute部分的內容

 1 function execute(array){
 2         // execute the whole query, first we filter
 3         var results = arrayUtil.filter(array, query);
 4         // next we sort
 5         var sortSet = options && options.sort;
 6         if(sortSet){
 7             results.sort(typeof sortSet == "function" ? sortSet : function(a, b){
 8                 for(var sort, i=0; sort = sortSet[i]; i++){
 9                     var aValue = a[sort.attribute];
10                     var bValue = b[sort.attribute];
11                     // valueOf enables proper comparison of dates
12                     aValue = aValue != null ? aValue.valueOf() : aValue;
13                     bValue = bValue != null ? bValue.valueOf() : bValue;
14                     if (aValue != bValue){
15                         return !!sort.descending == (aValue == null || aValue > bValue) ? -1 : 1;
16                     }
17                 }
18                 return 0;
19             });
20         }
21         // now we paginate
22         if(options && (options.start || options.count)){
23             var total = results.length;
24             results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
25             results.total = total;
26         }
27         return results;
28     }
29     execute.matches = query;
30     return execute;
31 };

看第3行,可以知道主要的查詢功能,還是以來arrayUtil.filter來實現的,而這裡更多的代碼是對option欄位的處理。

第5-20行,是對可選參數"sort"做處理:(當此參數存在時)

  1,若sort為排序函數,則利用數組的sort方法,進行排序。

  2,若sort不為排序函數,參數格式應該為

    [{attribute:string1,descending:bool1},{attribute:string2,descending:bool2}......}

  含義為:以string1(attribute參數)屬性進行排序,bool1的真假代表著順逆序。

  另外,從代碼可以看出:

    1、支持通過attribute屬性的valueof()方法進行比較。

    2,for迴圈支持了多個屬性值進行比較,當前一個屬性相等時,會取下一個sort參數對象進行比較得出結果(string2,bool2),直到比較出前後

次序或sort參數用完了)

第22-26行,是對可選參數"start"和"count"進行處理,很簡單,根據start和count對result結果進行裁剪返回,值得註意的是,total值的特殊處理,使得裁剪後的result.total是原查詢結果的總數,而result.length才是裁剪後的結果總數。

 

dojo/store/Memory 和 SimpleQueryEngine 就暫時研究到這一步驟了。謝謝閱讀~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [1]原理介紹 [2]代碼實現 [3]代碼優化 [4]拖拽衝突 [5]IE相容 ...
  • JavaScript圖表FusionCharts免費線上公開課,由印度原廠技術工程師主講,10月13日發車。 ...
  • 前言: 使用HTML5,通過創建cache manifest文件,可輕鬆創建web應用的離線版本。 HTML5引入了應用程式緩存,這意味著web應用可進行緩存,並可在沒有網路時進行訪問。 應用程式緩存為應用帶來三個優勢: 離線瀏覽--用戶可在離線時使用它們。 速度--已經緩存的資源載入得更快。 減少 ...
  • 序 1. 同源策略 是瀏覽器處於安全考慮,為通信設置了“相同的域、相同的埠、相同的協議”這一限制。這讓我們的ajax請求存在跨域無許可權訪問的問題。 2. 同時我們發現script標簽引入腳本的行為並不受同源策略的限制,但是script引入的文件會被立即執行,如果其內容不符合js語法,則會報錯; 操 ...
  • function IsPC() { var userAgentInfo = navigator.userAgent; var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; var flag ...
  • 在前端開發工作中,會遇到這樣問題:針對同一個dom元素,即希望為它綁定click事件,又想該元素可以允許拖拽的效果。而使用拖拽的效果,我們一般就會用到mousedown,mousemove和mouseup事件。但mousedown、mouseup就會和click事件發生衝突。我們希望在拖拽元素的時候 ...
  • web 項目中總是需要彈出框,來讓用戶進行下一步的操作。 大到彈出另外一個頁面,小到彈出提示、確認等。 經手幾個項目,還是感覺 Layer 用起來比較的輕鬆,你能想到的 Layer 都能幫你做到。 感謝 Layer 作者賢心,Layer 官網地址:http://layer.layui.com/ 1. ...
  • 匿名自執行方法體(function(){})經常用在設計JS插件上面,它定義相關組件的行為,自動初始化相關屬性,而且在頁面中可以直接執行,你不需要手動執行它,它被自動被執行! 在設計你的匿名自執行方法體時,我們有幾點需要註意,在這裡大叔部總結了一下,請看下麵說明 將它的結果賦給一個對象 如果它沒有返 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...