Patent Publication Number: US-10776352-B2

Title: Generic query language for data stores

Description:
BACKGROUND 
     There is a tendency in data store industry to have simplified queries. NoSQL data stores has brought new capabilities to store, scale and perform well on queries. Also, NoSQL has brought new query languages. For example, a document-based data store may have its own query language; a distributed cache may have its own declared N1SQL language; etc. Thus, data store vendors generally try to bring new query mechanisms that are as simple as possible. These new query mechanisms would allow users to create complex queries by using simple query syntax. Generally, data store implementations may come with its own query language to be used on the client side. However, the query language is specific to the implementation of the data store and the client-side applications. 
    
    
     
       BRIEF DESCRIPTION OF THE DRAWINGS 
       The following detailed description references the drawings, wherein: 
         FIG. 1  is a block diagram of an example architecture for using a generic query language for data stores; 
         FIGS. 2A-2D  are block diagrams illustrating example queries generated using the generic query language for data stores; 
         FIGS. 3A-3F  are block diagrams illustrating more example queries generated using the generic query language for data stores; 
         FIG. 4  is a flowchart of an example query process to use the generic query language for data stores; 
         FIG. 5  is a flowchart of an example query process to use the generic query language for data stores; 
         FIG. 6  is a flowchart of an example query process to use the generic query language for data stores; and 
         FIG. 7  is a block diagram of an example network device to generate queries using the generic query language for data stores. 
     
    
    
     DETAILED DESCRIPTION 
     Data store implementations may come with its own query language to be used by the clients of the data store. Those query languages often are limited to the implementation of the data store. For example, the use of specific query language may be allowed from a limited client base, e.g., JAVA-based clients. As another example, some data stores may allow queries based on a Representational State Transfer (REST) Application Programming Interface (API) by putting query string in Uniform Resource Identifier (URI) query parameter or a JavaScript Object Notation (JSON) body. This capability adds many opportunities for data stores to be useful in such a trending design pattern as a micro-service architecture, where communication is based on the REST API. 
     The generic URI query language described herein can provide a simple way to create complex queries by being independent from the underlying data store and storing mechanisms. Specifically, this generic query language can provide an easy way of representing a full set of variations of queries, especially in many-to-many and one-to-many relationships. The generic query language would need support for a basic set of operations by the data stores. For example, the underlying database needs to provide a set of basic query operations (e.g., “equal,” “not equal,” “greater than,” “less than,” etc.) and a set of query aggregation operations (e.g., “or,” “and,” “not,” etc.). Moreover, the generic query language may be extended if the underlying data store has additional query capabilities. 
     According to the present disclosure, the mechanism to store many-to-many relationships in the data stores could vary depending on a plurality of data store characteristics. For example, for Structured Query Language (SQL) based data stores, associative tables can be used to store the many-to-many relationships. On the other hand, for NoSQL data stores, other options may be used. 
     At least two approaches can be used to store many-to-many relationships for key-value (object) data stores. These approaches can apply to cases where mostly READ or WRITE operations are frequent but update operation. One approach in SQL-based data stores, when on one side of a many-to-many relationship, is using a simple type represented as a string and data can be stored in de-normalized form. However, by storing with associative tables, query performance can be higher. Nevertheless, the generic query language described herein is not coupled with the design or implementation of the underlying storing mechanism. 
     Architecture 
       FIG. 1  is a block diagram of an example architecture for using a generic query language for data stores. Specifically,  FIG. 1  includes a Software-Defined Networking (SDN) Controller  180  that can be connected to a plurality of clients (e.g., Client  110 ) and/or servers (e.g., Server  120 ). SDN Controller  180  generally refers to an application in software-defined networking (SDN) that manages flow control to allow intelligent networking. SDN controller  180  may be based on protocols, such as OpenFlow, that allow servers to communicate to network switches on where to send packets. 
     In the example illustrated in  FIG. 1 , SDN Controller  180  may include a data store  160  and a network application (e.g., SDN Application)  140 . Data store  160  generally refers to a repository to persistently store and manage collections of data, e.g., a database, a file, etc. A database can be a series of bytes that is managed by a database management system (DBMS). A file can be a series of bytes that is managed by a file system. In addition, data store  160  may be used by cloud storage systems for abstracting collections of data inside their respective applications. Therefore, data store  160  can refer to a broad class of storage systems including, but not limited to, simple files (e.g., a spreadsheet), file systems, email storage systems (both server and client systems), relational databases (e.g., SQL databases), non-relational databases (e.g., NoSQL), object-oriented databases, key-value databases, distributed data storages, directory services, virtual machines, etc. 
     Regardless of the implementations of data store  160 , generic query  150  can be generated using a generic query language as defined in the present disclosure as long as data store  160  provides operational support  170  compatible with the generic query language, such as, the support for the operations of OR, AND, NOT, EQUAL, etc. 
     Network Application  140  may provide a REST API  130  to the plurality of clients and/or servers, such that the plurality of clients and/or servers can generate a generic query  150  to data store  160 . 
     Note that, even though data store  160  is illustrated as a part of SDN Controller  180  in  FIG. 1 , data store  160  may reside anywhere in the network so long as it has connectivity to the plurality of clients and/or servers. Also, network application  140  can be implemented without a SDN framework. 
     In the following sections, first, a generic query language is defined herein. Then, a number of example queries are provided for illustration purposes only. Finally, possible ways of storing in a key-value data store are introduced. 
     Language Description 
     The example generic query language according to the present disclosure allows grouping by using OR/AND operands, using NOT operand and parenthesis to create possible variations of logical queries. Boolean algebra can be functionally complete with three OR, AND, and NOT operands. In other words, other logical operations could be expressed by using the OR, AND, and NOT operations. Although there may be even smaller sets with one or two cardinality, these three operands were chosen because most data stores provide some ways to support them. 
     Moreover, this example generic query language can be easily used as query value in the URI query parameter. Detailed information about query parameter in URI could be found in IETF RFC 3986 (URI Generic Syntax), which are incorporated herein in its entirety. In addition, the example generic query language is context free, which allows the use of Backus-Naur form (BNF) notation to describe its grammar and parses queries by using regular expressions. 
     Table 1 below illustrates the definition of the generic query language. 
     
       
         
           
               
               
               
             
               
                   
                 TABLE 1 
               
               
                   
                   
               
               
                   
                   
                 Example Generic Query Language Description 
               
               
                   
                   
               
             
            
               
                   
               
            
           
           
               
               
               
            
               
                   
                   
                 Query :: = NotOperand (Query) B  
               
               
                   
                   
                 B :: = Operand Query  
               
               
                   
                   
                 Operand :: = or | and  
               
               
                   
                   
                 Query :: = Expression  
               
               
                   
                   
                 B :: = epsilon(empty)  
               
               
                   
                   
                 Expression :: = #AllowedAttributeNames #AllowedOperations  
               
               
                   
                   
                 #Values  
               
               
                   
                   
                 Values :: == SingleValueRemainValues  
               
               
                   
                   
                 RemainValues :: = ValueOperand SingleValue RemainValues  
               
               
                   
                   
                 RemainValues :: = epsilon(empty)  
               
               
                   
                   
                 ValueOperand :: = , | &amp;  
               
               
                   
                   
                 SingleValue :: = NotOperand AvailableValues  
               
               
                   
                   
                 NotOperand :: = ! | epsilon(empty) 
               
               
                   
                   
               
            
           
         
       
     
     As shown in Table 1, a query constructed using the generic query language defined in Table 1 may include multiple queries connected by an operand. Each of the multiple queries can be an expression, which generally takes the form of a first hash tag followed by AllowedAttributeNames, then a second hash tag followed by AllowedOperations, and then a third hash tag followed by a plurality of Values. Therefore, the generic query language herein allows a single query to include multiple expressions. 
     Here, AllowedAttributeNames may include those alphanumerical values that designer will allow in its attribute names. Whitespaces in BNF maybe ignored in real expressions. AllowedOperations may include operations that data store  160  allows, for example, “equal,” “notEqual,” “greaterThan,” “lessThan,” “greaterEqual,” “lessEqual,” “in,” “between,” “like,” “notLike,” “regex,” etc. 
     As used herein, the term “query” generally refers to a precise request for information retrieval with an underlying data store (e.g., database, file, or any other information systems). The query may be constructed using the example generic query language. As used herein, “query language” generally refers to a computer language used to make queries into databases and information systems. The query may also be used as a query string. As used herein, “query string” generally refers to an optional part of a Uniform Resource Identifier (URI) that follows the first question mark in the context of a World Wide Web (WWW). In some examples, the query constructed using the example generic query language may be used as a web search query, which generally refers to a query entered by users into an interface of a search engine to search in a database, a local network, Internet, etc. 
     The example generic query language allows creating queries for many-to-many relationships in a simple syntax. Hence, results that conventionally are joined together from multiple datasets returned by multiple queries can now be obtained in single dataset in response to a single query using the example generic query language. Additionally, the example generic query language allows more restrictions on possible query formats using simple syntax, and thus eliminates complex nested queries. The following examples are given for illustration purposes only to explain the aforementioned advantage of the example generic query language. 
     Example Queries 
       FIGS. 2A-2D  are block diagrams illustrating example queries generated using the generic query language for data stores. The example queries in  FIGS. 2A-2D  and  FIGS. 3A-3D  use employees and their visited states as an example context. Assuming that an employee can visit multiple states. Therefore, the relationship between the employees set and the states set is a many-to-many relationship, because a particular state could be visited by multiple employees and an employee could visit multiple states. 
       FIG. 2A  shows example information stored in a data store. Note that, in the data store, such information could be stored in multiple tables. In some examples such as in key-value stores, such information may be stored in various data structures.  FIG. 2A  includes at least three fields, namely, first name  210  of an employee, last name  220  of the employee, and visited states  230  indicating the states that the employee has visited before. For example, according to the information stored in the data store, employee John Thomas has visited the states of Alabama, Nevada, New Jersey, and Nebraska; employee Kevin Thompson has visited the states of Alaska, New Mexico, and South Dakota; employee John Thom berg has visited the states of Nebraska, South Carolina, and New York; employee Johnathan Smith has visited the states of Alabama, New Hampshire, and Ohio; employee Fred Brown has visited the states of Alabama and Oregon; etc. 
       FIG. 2B  illustrates a simple query that searches for employees whose first name is John. Query  240  as a URI query parameter will look like (#FirstName#Equal#John). The string inside parenthesis (e.g., #FirstName#Equal#John) can be referred to as an expression. Here, FirstName is an example of AllowedAttributeNames, Equal is an example of AllowedOperations, and John is an example of Values. When executing query  240 , the underlying data store may return a result  250  as shown in  FIG. 2B . Result  250  may include employees John Thomas and John Thomberg. 
     Referring now to  FIG. 2C , query  260  searches for employees with first name John or Kevin. Instead of joining two expressions in the form of (#FirstName#equal#John) or (#FirstName#equal#Kevin), the example generic query language herein allows query  260  to be written in the form of a single expression (#FirstName#Equal#John, Kevin). Here, comma (,) in a values represents a logical operand “OR.” 
     With a conventional query including two expressions, the underlying data store will execute the first expression (#FirstName#equal#John) to obtain a first resulting dataset that includes employees whose first name equals to John (e.g., John Thomas, John Thomberg), and then execute the second expression (#FirstName#equal#Kevin) to obtain a second resulting dataset that includes employees whose first name equals to Kevin (e.g., Kevin Thompson). Next, the data store will combine the first resulting dataset and the second resulting dataset to obtain the response to the query (e.g., John Thomas, John Thomberg, Kevin Thompson). It is important to note that the data stores have built-in operational support for OR, EQUAL, etc. However, because of the limitations of existing query languages, the data store had to perform complex logical operations on the resulting datasets in response to the search query. 
     Now with the example generic query language herein, the data store can receive a query including multiple values in a single expression, e.g., (#FirstName#Equal#John, Kevin), and return results  270  in a single dataset (e.g., John Thomas, John Thomberg, Kevin Thompson). 
     Referring now to  FIG. 2D , query  280  searches for employees with first name John or Kevin and last Name starting with Thom. Here, the use of wildcard characters (e.g., %) or LIKE operation is a capability of the underlying data store. For example, an underscore sign (_) may be used to represent a wildcard for a single character; and, a percentage sign (%) may be used to represent a wildcard for multiple characters in an expression. If the underlying data store does not support wildcard characters, then this capability could be excluded from the language. Both the underscore sign and the percentage sign are examples of AllowedOperations that include any operations allowed by the underlying data store. 
     The grammar of the example generic query language allows for creating any kind of logical combinations using OR, AND, NOT, and parenthesis, such as, ((expression1)and(expression2))or(!((expression3)or(expression 4))and(expression5)). 
       FIGS. 3A-3F  are block diagrams illustrating more example queries generated using the generic query language for data stores. Specifically,  FIGS. 3A-3F  describe the use cases of searching for many-to-many relationship. A new operation—the CONTAIN operation—is introduced in these example queries. Although many data stores may not have support for the CONTAIN operation, the CONTAIN operation could be implemented by using other operations and capabilities that are supported in the data store. 
       FIG. 3A  illustrates a query that searches for employees who visited the states of Alabama. Query  310  as a URI query parameter may be constructed as (#visitedStates#Contains#Alabama). With the example generic query language herein, the data store can receive a query including the CONTAIN operation in a single expression, and return results  320  in a single dataset (e.g., John Thomas, Johnathan Smith, Kevin Thompson). 
     Referring now to  FIG. 3B , query  330  searches for employees who have visited the states of Alabama or Nebraska. Query  330  may be constructed as (#VisitedStates#Contains#Alabama,Nebraska). Here, the comma sign (,) in values indicates a logical operand “OR.” With a conventional query including two expressions, the underlying data store will execute the first expression (#visitedStates#Contains#Alabama) to obtain a first resulting dataset that includes employees who has visited the state of Alabama (e.g., John Thomas, Johnathan Smith, Fred Brown), and then execute the second expression (#visitedStates#Contains#Nebraska) to obtain a second resulting dataset that includes employees who has visited the state of Nebraska (e.g., John Thomas, John Thomberg). Next, the data store will combine the first resulting dataset and the second resulting dataset to obtain the response to the query (e.g., John Thomas, Johnathan Smith, Kevin Thompson, John Thomberg). This is another example demonstrating that, because of the limitations of existing query languages, the underlying data store had to perform complex logical operations on the resulting datasets in response to the search query. 
     Now with the example generic query language herein, the data store can receive a query including a CONTAIN operation and multiple values in a single expression, e.g., (#VisitedStates#Contains#Alabama, Nebraska), and return results  340  in a single dataset (e.g., John Thomas, Johnathan Smith, Kevin Thompson, John Thomberg). 
     Referring now to  FIG. 3C , query  350  searches for employees who have visited both Alabama and Nebraska. Query  350  may be constructed as (#VisitedStates#Contains#Alabama&amp;Nebraska). Here, the ampersand sign (&amp;) in values indicates a logical operand “AND.” Again, the underlying data store can receive a query including a CONTAIN operation and multiple values in a single expression, e.g., (#VisitedStates#Contains#Alabama&amp;Nebraska), and return results  360  in a single dataset (e.g., John Thomas, John Thomberg). 
     In  FIG. 3D , query  370  searches for employees who have not visited either Alabama or Nebraska. According to the De Morgan Law from Boolean Algebra, an expression can be rewritten as one without parenthesis. For example, not (a or b) is equivalent to not (a) and not (b). Thus, query  370  can be constructed as (#VisitedStates#Contains#!Alabama&amp;!Nebraska). In this example, the underlying data store can receive a query including one comparison operation (e.g., a CONTAIN operation) and multiple logical operations (e.g., NOT operation, AND operation) that are followed by multiple values in a single expression, e.g., (#VisitedStates#Contains#!Alabama&amp;!Nebraska), and return results  375  in a single dataset (e.g., Kevin Thomberg). 
     Note that, the example generic query language does not allow the use of parenthesis in Values. However, the example generic query language allows the use of multiple logical operations (e.g., “AND,” “OR,” and “NOT” operations) in a single expression. From Boolean Algebra, it is possible to create any logical expressions with this set of operands without parenthesis using the De Morgan Law. 
     Referring now to  FIG. 3E , query  380  searches for employees who have visited at least a state whose name starts with “New” and such employees have not visited any states whose name starts with “South.” Using the example generic query language, query  380  may be constructed as (#VisitedStates#Contains#New %&amp;!South%). Here, the wildcard characters, such as the percentage sign (%), are supported by the capabilities of the underlying data store. In this example, the underlying data store can receive a query including one comparison operation (e.g., a CONTAIN operation), multiple logical operations (e.g., NOT operation, AND operation), along with values including multiple wildcard characters in a single expression, and return results  385  in a single dataset (e.g., John Thomas, Johnathan Smith). 
       FIG. 3F  shows yet another example query using the generic query language. Query  390  searches for employees who have visited exactly a provided list of states. Thus, query  390  may be constructed using the “EQUAL” operation, which is a part of the AllowedOperations supported by the underlying data store. Specifically, query  390  may be constructed as (#VisitedStates#Equal#Alabama&amp;Oregon). In this example, the underlying data store can receive a query including one comparison operation (e.g., an EQUAL operation) followed by multiple values in a single expression, and return results  395  in a single dataset (e.g., Fred Brown). 
     As seen in the above examples, the example generic query language described herein allows for creation of complex queries in a simplified manner to search for many-to-many relationships combined with search in other attributes. The queries constructed using the example generic query language can easily be integrated into a URI as a query parameter. An example URI query parameter may look like this: http://example.com/?(#visitedstates#equal#Alabama&amp;oregon). In the example generic query language, reserved characters, such as, comma (,), hash tag (#), ampersand (&amp;), etc. can be encoded in the URI string. Also, because the percentage sign (%) may be used as an escape character in the URI string, it can be encoded as well if used as a wildcard character. 
     Processes to Use Generic Query Language for Data Stores 
     In discussing  FIGS. 4-6 , references may be made to the components in  FIGS. 1-3  to provide contextual examples. In one implementation, network application  140  described in  FIG. 1  executes operations  410 - 440 ,  510 - 550 , and  610 - 640  to construct a query using the generic query language for data stores. Further, although  FIGS. 4-6  are described as implemented by a computing device, it may be executed on other suitable devices or components. For example,  FIGS. 4-6  may be implemented in the form of executable instructions on a machine-readable storage medium  720  as in  FIG. 7 . 
       FIG. 4  is a flowchart of an example process to use the generic query language for data stores. First, a computing device may construct a plurality of expressions using a generic query language that is independent of implementation of an associated data store (operation  410 ). Here, the associated data store may support a plurality of operands that include at least one comparison operand and multiple logical operands. Then, the computing device can generate a single query including the plurality of expressions to the associated data store, wherein the plurality of expressions are connected using the multiple logical operands (operation  420 ). Note that each expression in the single query may include multiple values separated by the multiple logical operands. This allows execution of a single query to accomplish what has been conventionally obtained through the aggregation of resulting datasets from multiple individual queries. Next, the computing device can transmit the single query to the associated data store (operation  430 ). Finally, the computing device can receive a single set of results corresponding to the single query (operation  440 ). 
       FIG. 5  is a flowchart of an example process to use the generic query language for data stores. First, a computing device may construct a first expression using a generic query language that is independent of implementation of an associated data store (operation  510 ). Similarly, the associated data store here may support a plurality of operands that include at least one comparison operand and multiple logical operands. Then, the computing device can construct a second expression using the generic query language (operation  520 ). Note that each expression in the single query may include multiple values separated by the multiple logical operands. Next, the computing device can generate a single query by connecting the first expression with a second expression using an operand (operation  530 ). This allows execution of a single query to accomplish what has been conventionally obtained through the aggregation of resulting datasets from multiple individual queries. Subsequently, the computing device can submit the single query to the associated data store that supports at least one comparison operand and multiple logical operands (operation  540 ). Finally, the computing device can receive a single set of results corresponding to the single query from the associated data store (operation  550 ). 
       FIG. 6  is a flowchart of an example process to use the generic query language for data stores. First, a computing device may construct a plurality of expressions using a generic query language that is independent of implementation of an associated data store (operation  610 ). Then, the computing device can combine the plurality of expressions into a single query using an operand (operation  620 ). Note that each expression in the single query may include multiple values separated by the multiple logical operands. This allows execution of a single query to accomplish what has been conventionally obtained through the aggregation of resulting datasets from multiple individual queries. Next, the computing device can send the single query to the associated data store that supports a plurality of operands comprising at least one comparison operand and multiple logical operands (operation  630 ). Finally, the computing device can receive a single set of results corresponding to the single query from the associated data store (operation  640 ). 
     In some examples, the one comparison operand supported by the associated data store may include an EQUAL operand, a NOT EQUAL operand, a GREATER THAN operand, a LESS THAN operand, etc. In some examples, the multiple logical operands supported by the associated data store may include an OR operand, an AND operand, and a NOT operand. 
     Furthermore, each of the plurality of expressions can include an attribute name, an operation, and a plurality of values. Each of the attribute name, the operation, and the plurality of values may be preceded by a special character. In some examples, the plurality of values are separated by a plurality of value operands. For example, the plurality of value operands may include a comma indicating an OR operation, or an ampersand indicating an AND operation. 
     In some examples, the associated data store may include a text data file, a spreadsheet file, a file system, an email storage system, a relational database, a non-relational database, an object-oriented database, a distributed data storage, a directory service, or a virtual machine. When the associated data store includes multiple components, each component of the associated data store may support the one comparison operand and the multiple logical operands. 
     In some examples, the single query is used as a uniform resource identifier (URI) query parameter. Specifically, reserved characters can be encoded in the URI string. Such reserved characters may include, but are not limited to, a comma sign (,) indicating the OR operand, a hash tag sign (#) introducing a name of attribute and/or operand or a value, an ampersand sign (&amp;) indicating the AND operand, an exclamation sign (!) indicating the NOT operand, a percentage sign (%) indicating a wildcard for multiple characters, an underline sign (_) indicating a wildcard for a single character, etc. 
     Computing Device to Use Generic Query Language for Data Stores 
       FIG. 7  is a block diagram of an example computing device with at least one processor  710  to execute instructions  730 - 780  within a machine-readable storage medium  720  to construct a query using the generic query language for data stores. As used herein, “computing device” or “network device” generally includes a device that is adapted to transmit and/or receive signaling and to process information within such signaling such as a station (e.g., any data processing equipment such as a computer, cellular phone, personal digital assistant, tablet devices, etc.), an access point, data transfer devices (such as network switches, routers, controllers, etc.) or the like. 
     Although the computing device  700  includes at least one processor  710  and machine-readable storage medium  720 , it may also include other components that would be suitable to one skilled in the art. For example, computing device  700  may include an additional processing component and/or storage. In another implementation, the computing device executes instructions  730 - 780 . Computing device  700  is an electronic device with the at least one processor  710  capable of executing instructions  730 - 780 , and as such implementations of computing device  700  include a mobile device, server, data center, networking device, client device, computer, or other type of electronic device capable of executing instructions  730 - 780 . The instructions  730 - 780  may be implemented as methods, functions, operations, and other processes implemented as machine-readable instructions stored on the storage medium  720 , which may be non-transitory, such as hardware storage devices (e.g., random access memory (RAM), read only memory (ROM), erasable programmable ROM, electrically erasable ROM, hard drives, and flash memory). 
     The at least one processor  710  may fetch, decode, and execute instructions  730 - 780  to construct a query using the generic query language for data stores. Specifically, the at least one processor  710  executes instructions  730 - 780  to: construct a plurality of expressions using a generic query language that is independent of implementation of an associated data store; generate a single query including the plurality of expressions to the associated data store; transmit the single query to the associated data store; construct a first expression using a generic query language that is independent of implementation of an associated data store; construct a second expression using the generic query language; generate a single query by connecting the first expression with a second expression using an operand; submit the single query to the associated data store that supports at least one comparison operand and multiple logical operands; construct a plurality of expressions using a generic query language that is independent of implementation of an associated data store; combine the plurality of expressions into a single query using an operand; send the single query to the associated data store that supports a plurality of operands comprising at least one comparison operand and multiple logical operands; receive a single set of results corresponding to the single query from the associated data store; etc. 
     The machine-readable storage medium  720  includes instructions  730 - 780  for the processor  710  to fetch, decode, and execute. In another example, the machine-readable storage medium  720  may be an electronic, magnetic, optical, memory, storage, flash-drive, or other physical device that contains or stores executable instructions. Thus, the machine-readable storage medium  720  may include, for example, Random Access Memory (RAM), an Electrically Erasable Programmable Read-Only Memory (EEPROM), a storage drive, a memory cache, network storage, a Compact Disc Read Only Memory (CDROM) and the like. As such, the machine-readable storage medium  720  may include an application and/or firmware which can be utilized independently and/or in conjunction with the at least one processor  710  to fetch, decode, and/or execute instructions of the machine-readable storage medium  720 . The application and/or firmware may be stored on the machine-readable storage medium  720  and/or stored on another location of the network device  700 .