MD-SAL Binding Query Language Developer Guide

Note

Reading this section is likely useful as it contains an overview of MD-SAL Binding query language in OpenDaylight and a how-to use it for retrieving data from data storage.

Retrieving data from storage

MD-SAL has two ways (operations) of retrieving data from storage: read-like and query-like operations.

Read-like operation

The method read of ReadTransaction interface.

<T extends DataObject> FluentFuture<Optional<T>> read(LogicalDatastoreType store, InstanceIdentifier<T> path);

The method reads data from the provided logical data store located at the provided path. If the target is a subtree, then the whole subtree is read (and will be accessible from the returned DataObject). So we are getting DataObject which we need to process in code for getting relevant data:

FluentFuture<Optional<Foo>> future;
try (ReadTransaction rtx = getDataBroker().newReadOnlyTransaction()) {
    future = rtx.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Foo.class));
}
Foo haystack = future.get().orElseThrow();
Object result = null;
for (System system : haystack.nonnullSystem().values()) {
    if (needle.equals(system.getAlias())) {
        result = system;
        break;
    }
}

Note

The structure of the Foo container is here.

Query-like operation

The method execute of QueryReadTransaction interface.

<T extends DataObject> FluentFuture<QueryResult<T>> execute(LogicalDatastoreType store, QueryExpression<T> query);

The method executes a query on the provided logical data store for getting relevant data. So we are getting result which we need for future business logic processing. Before running the method execute we need to prepare a query with the match predicates. For example, we want to find in Foo container the System with alias target-needle:

String needle = "target-needle";
QueryExpression<System> query = factory.querySubtree(InstanceIdentifier.create(Foo.class))
    .extractChild(System.class)
        .matching()
            .leaf(System::getAlias).valueEquals(needle)
        .build();

The method querySubtree creates a new DescendantQueryBuilder for a specified root path. It’s intermediate query builder stage, which allows the specification of the query result type to be built up via extractChild(Class) and extractChild(Class, Class) methods. They used to specify which object type to select from the root path. Once completed, use either build() to create a simple query, or matching() to transition to specify predicates. There is a bunch of overloaded methods leaf which based on the type of arguments returns specific match builders:

  • ValueMatchBuilder methods:

ValueMatch<T> nonNull();
ValueMatch<T> isNull();
ValueMatch<T> valueEquals(V value);
  • ComparableMatchBuilder extends ValueMatchBuilder and adds methods:

ValueMatch<T> lessThan(V value);
ValueMatch<T> lessThanOrEqual(V value);
ValueMatch<T> greaterThan(V value);
ValueMatch<T> greaterThanOrEqual(V value);
  • StringMatchBuilder extends ValueMatchBuilder and adds methods:

ValueMatch<T> startsWith(String str);
ValueMatch<T> endsWith(String str);
ValueMatch<T> contains(String str);
ValueMatch<T> matchesPattern(Pattern pattern);

After creation of query, we can use it in the method execute of QueryReadTransaction interface:

FluentFuture<QueryResult<System>> future;
try (ReadTransaction rtx = getDataBroker().newReadOnlyTransaction()) {
    future = ((QueryReadTransaction) rtx).execute(LogicalDatastoreType.CONFIGURATION, query);
}
QueryResult<System> result = future.get();