Add QueryOperations 82/93382/1
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 25 Oct 2020 14:30:08 +0000 (15:30 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 26 Oct 2020 09:35:59 +0000 (10:35 +0100)
Introduce QueryOperations, which support execution of QueryExpressions.
We implement these in the binding/dom adapter in terms of a read/eval
supported by DOMQueryEvaluator.

This allows users to execute queries no matter what the underlying
storage engine supports. This has the upside of instantiating fewer
Binding objects, as the result set is usually a subset of the overall
tree.

TypedTransactions get these unconditionally, throwing
UnsupportedOperationException if the backend does not provide them
(which the default implementation does).

JIRA: MDSAL-606
Change-Id: If68fbb027aa72b1a4b5eef1437e92c30974a1e12
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit be0308e28d334b32485ec68d7b6593b95db728ae)

13 files changed:
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryOperations.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadTransaction.java [new file with mode: 0644]
binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadWriteTransaction.java [new file with mode: 0644]
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/AbstractForwardedTransaction.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMReadTransactionAdapter.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/BindingDOMReadWriteTransactionAdapter.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderTest.java
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryPerformanceTest.java [new file with mode: 0644]
binding/mdsal-binding-util/src/main/java/org/opendaylight/mdsal/binding/util/TypedReadTransaction.java
binding/mdsal-binding-util/src/main/java/org/opendaylight/mdsal/binding/util/TypedReadTransactionImpl.java
binding/mdsal-binding-util/src/main/java/org/opendaylight/mdsal/binding/util/TypedReadWriteTransactionImpl.java
binding/mdsal-binding-util/src/main/java/org/opendaylight/mdsal/binding/util/TypedTransaction.java
dom/mdsal-dom-spi/src/main/java/org/opendaylight/mdsal/dom/spi/query/DOMQueryEvaluator.java

diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryOperations.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryOperations.java
new file mode 100644 (file)
index 0000000..49c2cab
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api;
+
+import com.google.common.util.concurrent.FluentFuture;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.ReadFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Query-like operations supported by {@link ReadTransaction} and {@link ReadWriteTransaction}. This interface defines
+ * the operations without a tie-in with lifecycle management.
+ */
+public interface QueryOperations {
+    /**
+     * Executes a query on the provided logical data store.
+     *
+     * @param store Logical data store from which read should occur.
+     * @param query Query to execute
+     * @return a FluentFuture containing the result of the query. The Future blocks until the operation is complete.
+     *         Once complete:
+     *         <ul>
+     *           <li>The Future returns the result of the query</li>
+     *           <li>If the query execution fails, the Future will fail with a {@link ReadFailedException} or
+     *               an exception derived from ReadFailedException.
+     *            </li>
+     *         </ul>
+     * @throws NullPointerException if any of the arguments is null
+     * @throws IllegalArgumentException if the query is not supported
+     */
+    <T extends @NonNull DataObject> @NonNull FluentFuture<QueryResult<T>> execute(@NonNull LogicalDatastoreType store,
+        @NonNull QueryExpression<T> query);
+}
diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadTransaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadTransaction.java
new file mode 100644 (file)
index 0000000..0b8cf41
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api;
+
+/**
+ * A {@link ReadTransaction} which also supports {@link QueryOperations}.
+ */
+public interface QueryReadTransaction extends ReadTransaction, QueryOperations {
+
+}
diff --git a/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadWriteTransaction.java b/binding/mdsal-binding-api/src/main/java/org/opendaylight/mdsal/binding/api/QueryReadWriteTransaction.java
new file mode 100644 (file)
index 0000000..f6c9ca5
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.api;
+
+/**
+ * A {@link ReadWriteTransaction} which also supports {@link QueryOperations}.
+ */
+public interface QueryReadWriteTransaction extends ReadWriteTransaction, QueryOperations {
+
+}
index 28b63ec8b9e16856fb21b1bf423b301d194c2c8f..f25635ddb85be53c7ff46bc0da85cd8eb1c8594c 100644 (file)
@@ -15,9 +15,15 @@ import com.google.common.util.concurrent.FluentFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.Optional;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
+import org.opendaylight.mdsal.binding.dom.adapter.query.DefaultQuery;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadOperations;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
+import org.opendaylight.mdsal.dom.api.query.DOMQuery;
+import org.opendaylight.mdsal.dom.spi.query.DOMQueryEvaluator;
+import org.opendaylight.mdsal.dom.spi.query.EagerDOMQueryResult;
 import org.opendaylight.yangtools.concepts.Delegator;
 import org.opendaylight.yangtools.concepts.Identifiable;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -67,4 +73,18 @@ abstract class AbstractForwardedTransaction<T extends DOMDataTreeTransaction> im
         checkArgument(!path.isWildcarded(), "Invalid exists of wildcarded path %s", path);
         return readOps.exists(store, codec.toYangInstanceIdentifierBlocking(path));
     }
+
+    protected final <T extends @NonNull DataObject>  @NonNull FluentFuture<QueryResult<T>> doExecute(
+            final DOMDataTreeReadOperations readOps, final @NonNull LogicalDatastoreType store,
+            final @NonNull QueryExpression<T> query) {
+        checkArgument(query instanceof DefaultQuery, "Unsupported query type %s", query);
+        final DefaultQuery<T> defaultQuery = (DefaultQuery<T>) query;
+
+        final DOMQuery domQuery = defaultQuery.asDOMQuery();
+        return readOps.read(store, domQuery.getRoot())
+            .transform(node -> node.map(
+                data -> DOMQueryEvaluator.evaluateOn(domQuery, data)).orElse(EagerDOMQueryResult.of()),
+                MoreExecutors.directExecutor())
+            .transform(defaultQuery::toQueryResult, MoreExecutors.directExecutor());
+    }
 }
index a088ac00d0168b91e3a7a1d11be72fcd8156550c..68176b5bb42a885d7e272cc207d01f90291736a0 100644 (file)
@@ -9,16 +9,19 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Optional;
-import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.QueryReadTransaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
-final class BindingDOMReadTransactionAdapter extends AbstractForwardedTransaction<DOMDataTreeReadTransaction> implements
-        ReadTransaction {
+final class BindingDOMReadTransactionAdapter extends AbstractForwardedTransaction<DOMDataTreeReadTransaction>
+        implements QueryReadTransaction {
 
-    protected BindingDOMReadTransactionAdapter(final DOMDataTreeReadTransaction delegate,
+    BindingDOMReadTransactionAdapter(final DOMDataTreeReadTransaction delegate,
             final BindingToNormalizedNodeCodec codec) {
         super(delegate, codec);
     }
@@ -34,9 +37,14 @@ final class BindingDOMReadTransactionAdapter extends AbstractForwardedTransactio
         return doExists(getDelegate(), store, path);
     }
 
+    @Override
+    public <T extends @NonNull DataObject> FluentFuture<QueryResult<T>> execute(final LogicalDatastoreType store,
+            final QueryExpression<T> query) {
+        return doExecute(getDelegate(), store, query);
+    }
+
     @Override
     public void close() {
         getDelegate().close();
     }
-
 }
index a8868b6547c85417f219600e22fa8b6e8fcae4de..2889983533800e6d4e42d36fd9a7bfb7fb47a8e0 100644 (file)
@@ -9,14 +9,17 @@ package org.opendaylight.mdsal.binding.dom.adapter;
 
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Optional;
-import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding.api.QueryReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
 class BindingDOMReadWriteTransactionAdapter extends BindingDOMWriteTransactionAdapter<DOMDataTreeReadWriteTransaction>
-        implements ReadWriteTransaction {
+        implements QueryReadWriteTransaction {
 
     BindingDOMReadWriteTransactionAdapter(final DOMDataTreeReadWriteTransaction delegateTx,
             final BindingToNormalizedNodeCodec codec) {
@@ -30,7 +33,13 @@ class BindingDOMReadWriteTransactionAdapter extends BindingDOMWriteTransactionAd
     }
 
     @Override
-    public FluentFuture<Boolean> exists(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
+    public final FluentFuture<Boolean> exists(final LogicalDatastoreType store, final InstanceIdentifier<?> path) {
         return doExists(getDelegate(), store, path);
     }
+
+    @Override
+    public final <T extends @NonNull DataObject> FluentFuture<QueryResult<T>> execute(final LogicalDatastoreType store,
+            final QueryExpression<T> query) {
+        return doExecute(getDelegate(), store, query);
+    }
 }
index 5fe94de761ad01edf060af55c1b96800504ae974..d9b8d78623a67ac301712db2eeeeeed45b070762 100644 (file)
@@ -13,6 +13,7 @@ import com.google.common.base.Stopwatch;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.api.query.QueryExecutor;
@@ -46,9 +47,9 @@ import org.slf4j.LoggerFactory;
 public class QueryBuilderTest {
     private static final Logger LOG = LoggerFactory.getLogger(QueryBuilderTest.class);
     private static BindingNormalizedNodeCodecRegistry CODEC;
-    private static QueryExecutor EXECUTOR;
 
     private final QueryFactory factory = new DefaultQueryFactory(CODEC);
+    private QueryExecutor executor;
 
     @BeforeClass
     public static void beforeClass() {
@@ -58,7 +59,16 @@ public class QueryBuilderTest {
 
         CODEC = new BindingNormalizedNodeCodecRegistry(
             BindingRuntimeContext.create(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), schemaContext));
-        EXECUTOR = SimpleQueryExecutor.builder(CODEC.getCodecContext())
+    }
+
+    @AfterClass
+    public static final void afterClass() {
+        CODEC = null;
+    }
+
+    @Before
+    public void before() {
+        executor = SimpleQueryExecutor.builder(CODEC.getCodecContext())
             .add(new FooBuilder()
                 .setSystem(List.of(
                     new SystemBuilder().setName("first").setAlarms(List.of(
@@ -91,12 +101,6 @@ public class QueryBuilderTest {
             .build();
     }
 
-    @AfterClass
-    public static void afterClass() {
-        CODEC = null;
-        EXECUTOR = null;
-    }
-
     @Test
     public void bar() throws QueryStructureException {
         final Stopwatch sw = Stopwatch.createStarted();
@@ -157,9 +161,9 @@ public class QueryBuilderTest {
         assertEquals(2, items.size());
     }
 
-    private static <T extends @NonNull DataObject> QueryResult<T> execute(final QueryExpression<T> query) {
+    private <T extends @NonNull DataObject> QueryResult<T> execute(final QueryExpression<T> query) {
         final Stopwatch sw = Stopwatch.createStarted();
-        final QueryResult<T> result = EXECUTOR.executeQuery(query);
+        final QueryResult<T> result = executor.executeQuery(query);
         LOG.info("Query executed in {}", sw);
         return result;
     }
diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryPerformanceTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryPerformanceTest.java
new file mode 100644 (file)
index 0000000..2190b3b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.dom.adapter.query;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FluentFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.NonNull;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.api.QueryReadTransaction;
+import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.WriteTransaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryFactory;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
+import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
+import org.opendaylight.mdsal.binding.dom.codec.impl.BindingNormalizedNodeCodecRegistry;
+import org.opendaylight.mdsal.binding.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.generator.util.BindingRuntimeContext;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.Foo;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.FooBuilder;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.first.grp.System;
+import org.opendaylight.yang.gen.v1.mdsal.query.norev.first.grp.SystemBuilder;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class QueryPerformanceTest extends AbstractDataBrokerTest {
+    private static final Logger LOG = LoggerFactory.getLogger(QueryPerformanceTest.class);
+    private static final int SYSTEM_COUNT = 1_000_000;
+    private static Foo FOO;
+
+    private QueryFactory factory;
+
+    @BeforeClass
+    public static void beforeClass() {
+        final Stopwatch sw = Stopwatch.createStarted();
+        final List<System> builder = new ArrayList<>(SYSTEM_COUNT);
+
+        for (int i = 0; i < SYSTEM_COUNT; ++i) {
+            builder.add(new SystemBuilder().setName("name" + i).setAlias("alias" + i).build());
+        }
+
+        FOO = new FooBuilder().setSystem(builder).build();
+        LOG.info("Test data with {} items built in {}", SYSTEM_COUNT, sw);
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        FOO = null;
+    }
+
+    @Override
+    protected void setupWithSchema(final SchemaContext schemaContext) {
+        super.setupWithSchema(schemaContext);
+        factory = new DefaultQueryFactory(new BindingNormalizedNodeCodecRegistry(
+            BindingRuntimeContext.create(GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(), schemaContext)));
+    }
+
+    @Override
+    protected void setupWithDataBroker(final DataBroker dataBroker) {
+        final Stopwatch sw = Stopwatch.createStarted();
+        WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
+        wtx.put(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Foo.class), FOO);
+        assertCommit(wtx.commit());
+        LOG.info("Test data with {} items populated in {}", SYSTEM_COUNT, sw);
+    }
+
+    @Test
+    public void queryLessThanAlarm() throws InterruptedException, ExecutionException {
+        final String needle = "alias" + (SYSTEM_COUNT - 1);
+
+        final Stopwatch sw = Stopwatch.createStarted();
+        final QueryExpression<System> query = factory.querySubtree(InstanceIdentifier.create(Foo.class))
+            .extractChild(System.class)
+                .matching()
+                    .leaf(System::getAlias).valueEquals(needle)
+                .build();
+        LOG.info("Query built in {}", sw);
+
+        sw.reset().start();
+        final FluentFuture<QueryResult<@NonNull System>> future;
+        try (ReadTransaction rtx = getDataBroker().newReadOnlyTransaction()) {
+            assertThat(rtx, instanceOf(QueryReadTransaction.class));
+            future = ((QueryReadTransaction) rtx).execute(LogicalDatastoreType.CONFIGURATION, query);
+        }
+
+        final QueryResult<@NonNull System> result = future.get();
+        LOG.info("Query executed {} in {}", future, sw);
+
+        assertTrue(result.stream().findAny().isPresent());
+        LOG.info("Query result {} in {}", result, sw);
+    }
+
+    @Test
+    public void searchLessThanAlarm() throws InterruptedException, ExecutionException {
+        final String needle = "alias" + (SYSTEM_COUNT - 1);
+
+        final Stopwatch sw = Stopwatch.createStarted();
+        final FluentFuture<Optional<Foo>> future;
+        try (ReadTransaction rtx = getDataBroker().newReadOnlyTransaction()) {
+            future = rtx.read(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Foo.class));
+        }
+
+        final Foo haystack = future.get().orElseThrow();
+        LOG.info("Read executed in {}", sw);
+
+        Object result = null;
+        for (System system : haystack.nonnullSystem()) {
+            if (needle.equals(system.getAlias())) {
+                result = system;
+                break;
+            }
+        }
+
+        LOG.info("Search found {} in {}", result, sw);
+        assertNotNull(result);
+    }
+}
index 25cb823fd3e2fff4fb965ee9d98d5094215d75f3..c6556c6ec7777a681c5fda7aa7d806c0802aa266 100644 (file)
@@ -9,9 +9,13 @@ package org.opendaylight.mdsal.binding.util;
 
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.Transaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.ReadFailedException;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -44,4 +48,21 @@ public interface TypedReadTransaction<D extends Datastore> extends Transaction {
      * @return A future providing access to the result of the check, when it’s available, or any error encountered.
      */
     FluentFuture<Boolean> exists(InstanceIdentifier<?> path);
+
+    /**
+     * Executes a {@link QueryExpression}.
+     *
+     * @param query Query to execute
+     * @return a FluentFuture containing the result of the query. The Future blocks until the operation is complete.
+     *         Once complete:
+     *         <ul>
+     *           <li>The Future returns the result of the query</li>
+     *           <li>If the query execution fails, the Future will fail with a {@link ReadFailedException} or
+     *               an exception derived from ReadFailedException.
+     *            </li>
+     *         </ul>
+     * @throws NullPointerException if any of the arguments is null
+     * @throws IllegalArgumentException if the query is not supported
+     */
+    <T extends @NonNull DataObject> FluentFuture<QueryResult<T>> execute(QueryExpression<T> query);
 }
index 31ee12788e2adffb2a293d6b4af55e26c0a1555e..9a440ddf8841da0dc4242661418a6dc005dfc532 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.mdsal.binding.util;
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Optional;
 import org.opendaylight.mdsal.binding.api.ReadTransaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -33,4 +35,9 @@ final class TypedReadTransactionImpl<D extends Datastore> extends TypedTransacti
     public FluentFuture<Boolean> exists(final InstanceIdentifier<?> path) {
         return delegate().exists(getDatastoreType(), path);
     }
+
+    @Override
+    public <T extends DataObject> FluentFuture<QueryResult<T>> execute(final QueryExpression<T> query) {
+        return doExecute(query);
+    }
 }
index 192785390548908e8040583c158e8c86e00259ee..39bd29414bfedbfe90f0d12c1565c4216d0a55ed 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.mdsal.binding.util;
 import com.google.common.util.concurrent.FluentFuture;
 import java.util.Optional;
 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
@@ -31,7 +33,12 @@ class TypedReadWriteTransactionImpl<D extends Datastore>
     }
 
     @Override
-    public FluentFuture<Boolean> exists(final InstanceIdentifier<?> path) {
+    public final FluentFuture<Boolean> exists(final InstanceIdentifier<?> path) {
         return delegate().exists(getDatastoreType(), path);
     }
+
+    @Override
+    public final <T extends DataObject> FluentFuture<QueryResult<T>> execute(final QueryExpression<T> query) {
+        return doExecute(query);
+    }
 }
index 2da4724819be8c6f4ac87a06e64c1ebf1f111a11..3a0a5d6c70ac98f47211f29f00374a5529656fab 100644 (file)
@@ -7,9 +7,14 @@
  */
 package org.opendaylight.mdsal.binding.util;
 
+import com.google.common.util.concurrent.FluentFuture;
+import org.opendaylight.mdsal.binding.api.QueryOperations;
 import org.opendaylight.mdsal.binding.api.Transaction;
+import org.opendaylight.mdsal.binding.api.query.QueryExpression;
+import org.opendaylight.mdsal.binding.api.query.QueryResult;
 import org.opendaylight.mdsal.binding.spi.ForwardingTransaction;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yangtools.yang.binding.DataObject;
 
 abstract class TypedTransaction<D extends Datastore, X extends Transaction> extends ForwardingTransaction {
     private final LogicalDatastoreType datastoreType;
@@ -28,4 +33,11 @@ abstract class TypedTransaction<D extends Datastore, X extends Transaction> exte
     final LogicalDatastoreType getDatastoreType() {
         return datastoreType;
     }
+
+    final <T extends DataObject> FluentFuture<QueryResult<T>> doExecute(final QueryExpression<T> query) {
+        if (delegate instanceof QueryOperations) {
+            return ((QueryOperations) delegate).execute(datastoreType, query);
+        }
+        throw new UnsupportedOperationException("Query execution requires backend support");
+    }
 }
index 671b8eadd92cc5d95dbf52022bc75337ba9bd9fd..61e8fed5b254bfe4b4f6956b68f78328fecd1cd9 100644 (file)
@@ -73,6 +73,7 @@ public final class DOMQueryEvaluator {
 
     private static DOMQueryResult evalPath(final ArrayDeque<PathArgument> remaining, final NormalizedNode<?, ?> data,
             final DOMQuery query) {
+        // FIXME: this is eager evaluation, we should be doing lazy traversal
         final List<Entry<YangInstanceIdentifier, NormalizedNode<?, ?>>> result = new ArrayList<>();
         evalPath(result, new ArrayDeque<>(query.getRoot().getPathArguments()), remaining, data, query);
         return EagerDOMQueryResult.of(result);