For querying a kDB Store with ROS, there are two approaches, the first one uses the kDB API and the second one uses service calls (as shown in the getting started tutorial). In this tutorial we present both approaches. And the use of the kDB API is more efficient.

Query with the kDB API

The kDB ROS Server broadcast its status on a topic kdb_status with the hostname and port needed to connect directly to the database. It is therefore possible to create a connection object from the information provided in that topic. The kDB ROS module also provides classes to conveniently create such a connection object.

  • To query the database from other Python programs, the easiest is to use ros_kdb.create_connection(). This will listen to the topic kdb_status which contains all the information needed to connect to the database and create a connection that can be used to query the database.

    An example of use of the function is provided below. This example create a connection to a runing ROS kDB server. It then runs a SPARQL query to print the UUID of the server:

    #include <ros_kdb/connection_helper.h>
    #include <knowDBC/Query.h>
    #include <knowDBC/Result.h>
    
    #include <kDB/Repository/RDFEnvironment.h>
    
    // Create a connection helper, this can take a ROS node as an argument, or create one
    ros_kdb::ConnectionHelper ch;
    
    // Get the connection, this will wait for the connection to be ready
    kDB::Repository::Connection connection = ch.getConnection();
    
    // The following creates an SPARQL query, that retuns the UUID of the server.
    knowDBC::Query query = connection.createSPARQLQuery({}, "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }");
    
    // The following executes the query
    knowDBC::Result result = query.execute();
    
    // Print the results in the command line
    std::cout << "Server has uuid: " << result.value(0, 0).printable().expectSuccess() << std::endl;
    

    The following needs to be added to the package CMakeLists.txt:

    find_package(ros_kdb REQUIRED)
    ament_target_dependencies(your_target  ros_kdb)
    
  • To query the database from other Python programs, the easiest is to use ros_kdb.create_connection(). This will listen to the topic kdb_status which contains all the information needed to connect to the database and create a connection that can be used to query the database.

    An example of use of the function is provided below. This example create a connection to a runing ROS kDB server. It then runs a SPARQL query to print the UUID of the server:

    import rclpy
    import ros_kdb
    
    rclpy.init()
    
    # Create a connection, this can take a ROS namespace as argument, or default to "".
    # This will wait for the connection to be ready
    c = ros_kdb.create_connection()
    q = c.createSPARQLQuery()
    
    # The following creates an SPARQL query, that retuns the UUID of the server.
    q.setQuery("SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }")
    
    # The following executes the query
    r = q.execute()
    
    # Print the results in the command line
    print(f"Server has uuid {r.value(0, 0)}")
    
  • In kDB < 4.5, Ruby does not have a convenience class for creating a connection, instead it is necesserary to listen to kdb_status in the program.

    require 'rclrb'
    require 'knowRDF'
    require 'kDB/Repository'
    require 'ros_kdb_interfaces/msg'
    
    # Initialise Rclrb
    Rclrb.init(arguments: [])
    n = Rclrb::Node.new "test_query"
    
    # Create a subscription to `kdb_status`
    n.create_subscription(RosKdbInterfaces::Msg::DatabaseStatus, "kdb_status", 1) do |msg|
      # Create a connection to the database
      connection = KDB::Repository::Connection.create msg.hostname, msg.port
      connection.connect()
    
      # The following creates an SPARQL query, that retuns the UUID of the server.
      query = connection.createSPARQLQuery(
        KDB::Repository::RDFEnvironment.new(),
        "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }")
      
      # The following executes the query
      result = query.execute
    
      # Print the results in the command line
      puts "Server has uuid #{result.value(0, 0)}"
    
      # And exit
      Rclrb.shutdown
    end
    
    # Start the execution
    Rclrb.spin(n)
    

    With kDB >= 4.5, the convenience class was added:

    irb(main):003:0> ; Rclrb::init => #<Proc:0x000055f9ac5f9848 /home/cyrille/ros2_ws/src/rclrb/lib/rclrb/init.rb:47> irb(main):004:0> c = RosKDB.create_connection “/test0/”

    require 'rclrb'
    require 'knowRDF'
    require 'kDB/Repository'
    require 'ros_kdb'
    
    # Initialise Rclrb
    Rclrb.init(arguments: [])
    
    # Create a connection, this can take a ROS namespace as argument, or default to "".
    # This will wait for the connection to be ready
    connection = RosKDB.create_connection
    
    # The following creates an SPARQL query, that retuns the UUID of the server.
    query = connection.createSPARQLQuery(
      KDB::Repository::RDFEnvironment.new(),
      "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }")
    
    # The following executes the query
    result = query.execute
    
    # Print the results in the command line
    puts "Server has uuid #{result.value(0, 0)}"
    

Query with service call

To query remote systems, it is possible to allow the PostgreSQL server to listen to external connection. But if that is not possible or desirable, the only solution might be to use service calls.

  • #include <ros_kdb_interfaces/srv/QueryDatabase.h>
    
    // Create a service call to 'kdb_server/sparql_query' to execute a SPARQL query.
    // It can be replaced by 'kdb_server/sql_query' for SQL.
    rclcpp::Client<ros_kdb_interfaces::srv::QueryDatabase>::SharedPtr client =
        node->create_client<ros_kdb_interfaces::srv::QueryDatabase>("kdb_server/sparql_query");
    
    // Create a request object
    auto request = std::make_shared<ros_kdb_interfaces::srv::QueryDatabase::Request>();
    
    // Request can contain multiple query, all executed in the same transaction.
    // For this example, a single query will be used.
    ros_kdb_interfaces::msg::Query query;
    
    // Select which graphs are available to the query
    query.graphnames.push_back("http://askco.re/graphs#info");
    
    // Assign the text of the query, it will query for the UUID of the server.
    query.query = "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }";
    request->queries.push_back(query);
    
    // Execute the service call
    auto result = client->async_send_request(request);
    
    // Wait for the result
    rclcpp::spin_until_future_complete(node, result);
    
    // Print the result, which is a JSON string
    std::cout << result.get()->results[0] << std::endl;
    
  • import json
    
    import rclpy
    import rclpy.node
    
    from ros_kdb_interfaces.srv import QueryDatabase
    from ros_kdb_interfaces.msg import Query
    
    rclpy.init(args=args)
    node = rclpy.node.Node("test_query")
    
    # Create a service call to 'kdb_server/sparql_query' to execute a SPARQL query.
    # It can be replaced by 'kdb_server/sql_query' for SQL.
    client = node.create_client(QueryDatabase, 'kdb_server/sparql_query')
    
    # Create a request object
    request = QueryDatabase.Request()
    
    # Request can contain multiple query, all executed in the same transaction.
    # For this example, a single query will be used.
    query = Query()
    
    # Select which graphs are available to the query
    query.graphnames = ["http://askco.re/graphs#info"]
    
    # Assign the text of the query, it will query for the UUID of the server.
    query.query = "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }";
    request.queries.append(query);
    
    # Execute the service call
    future = client.call_async(request)
    
    # Wait for the result
    rclpy.spin_until_future_complete(node, future)
    
    # Parse the JSON string
    r = json.loads(future.result().results[0]))
    
    # Print the results in the command line
    print(f"Server has uuid {r['results']['bindings'][0]['uuid']['value']}")
    
    
  • require 'json'
    require 'rclrb'
    
    require 'ros_kdb_interfaces/msg'
    require 'ros_kdb_interfaces/srv'
    
    Rclrb.init(arguments: [])
    node = Rclrb::Node.new "test_query"
    
    # Create a service call to 'kdb_server/sparql_query' to execute a SPARQL query.
    # It can be replaced by 'kdb_server/sql_query' for SQL.
    client = node.create_client RosKdbInterfaces::Srv::QueryDatabase, 'kdb_server/sparql_query'
    
    # Create a request object
    request = RosKdbInterfaces::Srv::QueryDatabase::Request.new
    
    # Request can contain multiple query, all executed in the same transaction.
    # For this example, a single query will be used.
    query = RosKdbInterfaces::Msg::Query.new
    
    # Select which graphs are available to the query
    query.graphnames = ["http://askco.re/graphs#info"]
    
    # Assign the text of the query, it will query for the UUID of the server.
    query.query = "SELECT ?uuid FROM <http://askco.re/graphs#info> WHERE { <http://askco.re/db/info#self> <http://askco.re/db/info#hasUUID> ?uuid }";
    request.queries.append(query);
    
    # Execute the service call
    future = client.call_async request
    
    # Parse the JSON string
    r = JSON.parse future.result.results[0]
    
    # Print the results in the command line
    puts "Server has uuid #{r['results']['bindings'][0]['uuid']['value']}"