blob: 212e142b9831c7c276df21fc6ef5ab27b61b01a7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.cache.query.functional;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.CacheUtils;
import org.apache.geode.cache.query.Query;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.SelectResults;
import org.apache.geode.test.junit.categories.OQLIndexTest;
@Category({OQLIndexTest.class})
public class IndexUsageInNestedQueryWithParamsJUnitTest {
@Before
public void setUp() throws java.lang.Exception {
CacheUtils.startCache();
Region portfolioRegion =
CacheUtils.createRegion("portfolio", PortfolioForParameterizedQueries.class);
Region intBoundRegion = CacheUtils.createRegion("intBound", IntermediateBound.class);
QueryService queryService = CacheUtils.getQueryService();
queryService.defineIndex("portfolioIdIndex", "p.ID", "/portfolio p");
queryService.defineIndex("temporaryOriginalBoundIdIdx", "ib.originalId", "/intBound ib");
queryService.createDefinedIndexes();
PortfolioForParameterizedQueries p = new PortfolioForParameterizedQueries(1L);
p.setDataList(Arrays
.asList(new PortfolioData(Arrays.asList(new Bound(11L), new Bound(12L))),
new PortfolioData(Arrays.asList(new Bound(21L), new Bound(22L)))));
portfolioRegion.put(p.getID(), p);
intBoundRegion.put(91L, new IntermediateBound(91L, 12L, new Bound(91L)));
intBoundRegion.put(92L, new IntermediateBound(92L, 21L, new Bound(92L)));
}
@After
public void tearDown() throws java.lang.Exception {
CacheUtils.closeCache();
}
@Test
public void whenParameterizedQueryExecutedThenItShouldReturnCorrectResultsAndNotThrowException()
throws Exception {
QueryService queryService = CacheUtils.getQueryService();
Query query = queryService.newQuery(
"<TRACE>select l.ID from /portfolio p, p.dataList dl, ($1).getConcatenatedBoundsAndIntermediateBounds(dl) l WHERE p.ID IN SET(1L,2L)");
QueryParameterProvider querySupportService = new QueryParameterProvider(CacheUtils.getCache());
Object[] params = new Object[] {querySupportService.createQueryParameter()};
SelectResults result = (SelectResults) query.execute(params);
assertEquals("The query returned a wrong result set = " + result.asList(), true,
result.containsAll(new ArrayList<>(Arrays.asList(21L, 22L, 92L, 12L, 11L, 91L))));
}
public class PortfolioForParameterizedQueries implements Serializable {
long ID;
Collection<PortfolioData> dataList = new ArrayList<>();
public long getID() {
return ID;
}
public void setID(long ID) {
this.ID = ID;
}
public Collection<PortfolioData> getDataList() {
return dataList;
}
public void setDataList(
Collection<PortfolioData> dataList) {
this.dataList = dataList;
}
public PortfolioForParameterizedQueries(long ID) {
this.ID = ID;
}
@Override
public String toString() {
return "PortfolioParameter{" + "id=" + ID + ", data=" + dataList + "}";
}
}
public class PortfolioData {
List<Bound> bounds = new ArrayList<>();
public List<Bound> getBound() {
return bounds;
}
public void setLimits(List<Bound> bounds) {
this.bounds = bounds;
}
public PortfolioData(List<Bound> bounds) {
this.bounds = bounds;
}
@Override
public String toString() {
return "PortfolioData{bounds=" +
bounds +
'}';
}
}
public class Bound implements Serializable {
long ID;
public Bound(long ID) {
this.ID = ID;
}
public long getID() {
return ID;
}
public void setID(long ID) {
this.ID = ID;
}
@Override
public String toString() {
return "Bound{ID=" + ID + "}";
}
}
public class QueryParameterProvider {
private Cache cache;
public QueryParameterProvider(Cache cache) {
this.cache = cache;
}
public QueryParameter createQueryParameter() {
return new QueryParameter(
"<TRACE>select distinct ib from /intBound ib where ib.originalId in $1");
}
public class QueryParameter {
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final Query query;
private QueryParameter(final String queryString) {
try {
this.query = executorService
.submit(() -> cache.getQueryService().newQuery(queryString)).get();
} catch (InterruptedException e) {
throw new RuntimeException("Thread was unexpectedly interrupted", e);
} catch (ExecutionException e) {
throw new RuntimeException("Could not create query context", e);
}
}
@SuppressWarnings("unchecked")
private Iterable<Bound> getIntermediateBounds(final Set<Long> originalBoundIds) {
final Set<IntermediateBound> infos;
final List<Bound> intermediateBounds = new ArrayList<>();
try {
infos = executorService.submit(
() -> ((SelectResults<IntermediateBound>) query
.execute(new Object[] {originalBoundIds}))
.asSet())
.get();
} catch (InterruptedException e) {
throw new RuntimeException("Thread was unexpectedly interrupted", e);
} catch (ExecutionException e) {
throw new RuntimeException("Could not query for temp limits", e);
}
if (infos != null) {
for (final IntermediateBound info : infos) {
if (info != null) {
final Bound intLimit = info.getBound();
if (intLimit != null) {
intermediateBounds.add(intLimit);
}
}
}
}
return intermediateBounds;
}
public Collection<Bound> getConcatenatedBoundsAndIntermediateBounds(
final PortfolioData portfolioData) {
final Map<Long, Bound> boundMap = getBounds(portfolioData);
return Lists.newArrayList(
Iterables.concat(boundMap.values(), getIntermediateBounds(boundMap.keySet())));
}
private Map<Long, Bound> getBounds(final PortfolioData portfolioData) {
final Map<Long, Bound> boundsMap = new HashMap<Long, Bound>();
if (portfolioData != null) {
for (final Bound bound : portfolioData.bounds) {
if (bound != null) {
boundsMap.put(bound.ID, bound);
}
}
}
return boundsMap;
}
}
}
public class IntermediateBound implements Serializable {
long ID;
long originalId;
Bound bound;
public long getOriginalId() {
return originalId;
}
public long getID() {
return ID;
}
public void setID(long ID) {
this.ID = ID;
}
public Bound getBound() {
return bound;
}
public IntermediateBound setOriginalId(long originalId) {
this.originalId = originalId;
return this;
}
public IntermediateBound setBound(Bound bound) {
this.bound = bound;
return this;
}
public IntermediateBound(long ID, long originalId, Bound bound) {
this.ID = ID;
this.originalId = originalId;
this.bound = bound;
}
@Override
public String toString() {
return "Bound{" +
"id=" + ID +
'}';
}
}
}