.. _mdsal-binding-dev-guide: 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. :: FluentFuture> read(LogicalDatastoreType store, InstanceIdentifier 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> 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`_. .. _here: https://github.com/opendaylight/mdsal/blob/master/binding/mdsal-binding-test-model/src/main/yang/mdsal-query.yang Query-like operation ~~~~~~~~~~~~~~~~~~~~ The method **execute** of QueryReadTransaction interface. :: FluentFuture> execute(LogicalDatastoreType store, QueryExpression 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 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 nonNull();** | **ValueMatch isNull();** | **ValueMatch valueEquals(V value);** - ComparableMatchBuilder extends ValueMatchBuilder and adds methods: | **ValueMatch lessThan(V value);** | **ValueMatch lessThanOrEqual(V value);** | **ValueMatch greaterThan(V value);** | **ValueMatch greaterThanOrEqual(V value);** - StringMatchBuilder extends ValueMatchBuilder and adds methods: | **ValueMatch startsWith(String str);** | **ValueMatch endsWith(String str);** | **ValueMatch contains(String str);** | **ValueMatch matchesPattern(Pattern pattern);** After creation of query, we can use it in the method execute of QueryReadTransaction interface: :: FluentFuture> future; try (ReadTransaction rtx = getDataBroker().newReadOnlyTransaction()) { future = ((QueryReadTransaction) rtx).execute(LogicalDatastoreType.CONFIGURATION, query); } QueryResult result = future.get();