/*
* 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.ignite.internal.processors.cache;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import javax.cache.Cache;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder;
import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
/**
*
*/
public class IgniteBinaryObjectQueryArgumentsTest extends GridCommonAbstractTest {
/** */
private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
/** */
private static final int NODES = 3;
/** */
private static final String PRIM_CACHE = "prim-cache";
/** */
private static final String STR_CACHE = "str-cache";
/** */
private static final String ENUM_CACHE = "enum-cache";
/** */
private static final String UUID_CACHE = "uuid-cache";
/** */
private static final String DATE_CACHE = "date-cache";
/** */
private static final String TIMESTAMP_CACHE = "timestamp-cache";
/** */
private static final String BIG_DECIMAL_CACHE = "decimal-cache";
/** */
private static final String OBJECT_CACHE = "obj-cache";
/** */
private static final String FIELD_CACHE = "field-cache";
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
((TcpDiscoverySpi)cfg.getDiscoverySpi()).setIpFinder(IP_FINDER);
cfg.setCacheConfiguration(getCacheConfigurations());
cfg.setMarshaller(null);
return cfg;
}
/**
* @return {@code True} If query is local.
*/
protected boolean isLocal() {
return false;
}
/**
* @param cacheName Cache name.
* @return Cache config.
*/
protected CacheConfiguration getCacheConfiguration(final String cacheName) {
CacheConfiguration ccfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
ccfg.setWriteSynchronizationMode(FULL_SYNC);
QueryEntity person = new QueryEntity();
person.setKeyType(TestKey.class.getName());
person.setValueType(Person.class.getName());
person.addQueryField("name", String.class.getName(), null);
ccfg.setQueryEntities(Collections.singletonList(person));
ccfg.setName(cacheName);
return ccfg;
}
/**
* @return Cache configurations.
*/
private CacheConfiguration[] getCacheConfigurations() {
final ArrayList<CacheConfiguration> ccfgs = new ArrayList<>();
ccfgs.add(getCacheConfiguration(OBJECT_CACHE));
ccfgs.addAll(getCacheConfigurations(STR_CACHE, String.class, Person.class));
ccfgs.addAll(getCacheConfigurations(PRIM_CACHE, Integer.class, Person.class));
ccfgs.addAll(getCacheConfigurations(ENUM_CACHE, EnumKey.class, Person.class));
ccfgs.addAll(getCacheConfigurations(UUID_CACHE, UUID.class, Person.class));
ccfgs.addAll(getCacheConfigurations(DATE_CACHE, Date.class, Person.class));
ccfgs.addAll(getCacheConfigurations(TIMESTAMP_CACHE, Timestamp.class, Person.class));
ccfgs.addAll(getCacheConfigurations(BIG_DECIMAL_CACHE, BigDecimal.class, Person.class));
ccfgs.add(getCacheConfiguration(FIELD_CACHE, Integer.class, SearchValue.class));
return ccfgs.toArray(new CacheConfiguration[ccfgs.size()]);
}
/**
*
* @param cacheName Cache name.
* @param key Key type.
* @param val Value type.
* @return Configurations.
*/
private List<CacheConfiguration> getCacheConfigurations(final String cacheName, final Class<?> key, final Class<?> val) {
final List<CacheConfiguration> res = new ArrayList<>();
res.add(getCacheConfiguration(cacheName, key, val));
res.add(getCacheConfiguration(cacheName + "-val", val, key));
return res;
}
/**
* @param cacheName Cache name.
* @param key Key type.
* @param val Value type
* @return Configuration.
*/
@SuppressWarnings("unchecked")
private CacheConfiguration getCacheConfiguration(final String cacheName, final Class<?> key, final Class<?> val) {
CacheConfiguration cfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
cfg.setName(cacheName);
cfg.setIndexedTypes(key, val);
return cfg;
}
/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();
final int nodes = isLocal() ? 1 : NODES;
startGridsMultiThreaded(nodes);
}
/** {@inheritDoc} */
@Override protected void afterTestsStopped() throws Exception {
stopAllGrids();
super.afterTestsStopped();
}
/**
* @throws Exception If failed.
*/
public void testObjectArgument() throws Exception {
testKeyQuery(OBJECT_CACHE, new TestKey(1), new TestKey(2));
}
/**
* @throws Exception If failed.
*/
public void testPrimitiveObjectArgument() throws Exception {
testKeyValQuery(PRIM_CACHE, 1, 2);
}
/**
* @throws Exception If failed.
*/
public void testStringObjectArgument() throws Exception {
testKeyValQuery(STR_CACHE, "str1", "str2");
}
/**
* @throws Exception If failed.
*/
public void testEnumObjectArgument() throws Exception {
testKeyValQuery(ENUM_CACHE, EnumKey.KEY1, EnumKey.KEY2);
}
/**
* @throws Exception If failed.
*/
public void testUuidObjectArgument() throws Exception {
final UUID uuid1 = UUID.randomUUID();
UUID uuid2 = UUID.randomUUID();
while (uuid1.equals(uuid2))
uuid2 = UUID.randomUUID();
testKeyValQuery(UUID_CACHE, uuid1, uuid2);
}
/**
* @throws Exception If failed.
*/
public void testDateObjectArgument() throws Exception {
testKeyValQuery(DATE_CACHE, new Date(0), new Date(1));
}
/**
* @throws Exception If failed.
*/
public void testTimestampArgument() throws Exception {
testKeyValQuery(TIMESTAMP_CACHE, new Timestamp(0), new Timestamp(1));
}
/**
* @throws Exception If failed.
*/
public void testBigDecimalArgument() throws Exception {
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
final BigDecimal bd1 = new BigDecimal(rnd.nextDouble());
BigDecimal bd2 = new BigDecimal(rnd.nextDouble());
while (bd1.equals(bd2))
bd2 = new BigDecimal(rnd.nextDouble());
testKeyValQuery(BIG_DECIMAL_CACHE, bd1, bd2);
}
/**
* Test simple queries.
*
* @param cacheName Cache name.
* @param key1 Key 1.
* @param key2 Key 2.
* @param <T> Key type.
*/
private <T> void testKeyValQuery(final String cacheName, final T key1, final T key2) {
testKeyQuery(cacheName, key1, key2);
testValQuery(cacheName + "-val", key1, key2);
}
/**
* Test simple query by key.
*
* @param cacheName Cache name.
* @param key1 Key 1.
* @param key2 Key 2.
* @param <T> Key type.
*/
private <T> void testKeyQuery(final String cacheName, final T key1, final T key2) {
final IgniteCache<T, Person> cache = ignite(0).cache(cacheName);
final Person p1 = new Person("p1");
final Person p2 = new Person("p2");
cache.put(key1, p1);
cache.put(key2, p2);
final SqlQuery<T, Person> qry = new SqlQuery<>(Person.class, "where _key=?");
final SqlFieldsQuery fieldsQry = new SqlFieldsQuery("select _key, _val, * from Person where _key=?");
qry.setLocal(isLocal());
fieldsQry.setLocal(isLocal());
qry.setArgs(key1);
fieldsQry.setArgs(key1);
final List<Cache.Entry<T, Person>> res = cache.query(qry).getAll();
final List<List<?>> fieldsRes = cache.query(fieldsQry).getAll();
assertEquals(1, res.size());
assertEquals(1, fieldsRes.size());
assertEquals(p1, res.get(0).getValue());
assertEquals(key1, res.get(0).getKey());
assertTrue(fieldsRes.get(0).size() >= 2);
assertEquals(key1, fieldsRes.get(0).get(0));
assertEquals(p1, fieldsRes.get(0).get(1));
}
/**
* Test simple query by value.
*
* @param cacheName Cache name.
* @param val1 Value 1.
* @param val2 Value 2.
* @param <T> Value type.
*/
private <T> void testValQuery(final String cacheName, final T val1, final T val2) {
final IgniteCache<Person, T> cache = ignite(0).cache(cacheName);
final Class<?> valType = val1.getClass();
final Person p1 = new Person("p1");
final Person p2 = new Person("p2");
cache.put(p1, val1);
cache.put(p2, val2);
final SqlQuery<Person, T> qry = new SqlQuery<>(valType, "where _val=?");
final SqlFieldsQuery fieldsQry = new SqlFieldsQuery("select _key, _val, * from " + valType.getSimpleName() + " where _val=?");
qry.setLocal(isLocal());
fieldsQry.setLocal(isLocal());
qry.setArgs(val1);
fieldsQry.setArgs(val1);
final List<Cache.Entry<Person, T>> res = cache.query(qry).getAll();
final List<List<?>> fieldsRes = cache.query(fieldsQry).getAll();
assertEquals(1, res.size());
assertEquals(1, fieldsRes.size());
assertEquals(p1, res.get(0).getKey());
assertEquals(val1, res.get(0).getValue());
assertTrue(fieldsRes.get(0).size() >= 2);
assertEquals(p1, fieldsRes.get(0).get(0));
assertEquals(val1, fieldsRes.get(0).get(1));
}
/**
* @throws Exception If failed.
*/
public void testFieldSearch() throws Exception {
final IgniteCache<Integer, SearchValue> cache = ignite(0).cache(FIELD_CACHE);
final Map<Integer, SearchValue> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
map.put(i,
new SearchValue(
UUID.randomUUID(),
String.valueOf(i),
new BigDecimal(i * 0.1),
i,
new Date(i),
new Timestamp(i),
new Person(String.valueOf("name-" + i)),
i % 2 == 0 ? EnumKey.KEY1 : EnumKey.KEY2)
);
}
cache.putAll(map);
SqlQuery<Integer, SearchValue> qry = new SqlQuery<>(SearchValue.class,
"where uuid=? and str=? and decimal=? and integer=? and date=? and ts=? and person=? and enumKey=?");
final int k = ThreadLocalRandom.current().nextInt(10);
final SearchValue val = map.get(k);
qry.setLocal(isLocal());
qry.setArgs(val.uuid, val.str, val.decimal, val.integer, val.date, val.ts, val.person, val.enumKey);
final List<Cache.Entry<Integer, SearchValue>> res = cache.query(qry).getAll();
assertEquals(1, res.size());
assertEquals(val.integer, res.get(0).getKey());
assertEquals(val, res.get(0).getValue());
}
/**
*
*/
private static class Person {
/** */
String name;
/**
* @param name Name.
*/
public Person(String name) {
this.name = name;
}
/** {@inheritDoc} */
@Override public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Person person = (Person) o;
return name != null ? name.equals(person.name) : person.name == null;
}
/** {@inheritDoc} */
@Override public int hashCode() {
return name != null ? name.hashCode() : 0;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(Person.class, this);
}
}
/**
*
*/
public static class TestKey {
/** */
private int id;
/**
* @param id Key.
*/
TestKey(int id) {
this.id = id;
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
TestKey other = (TestKey)o;
return id == other.id;
}
/** {@inheritDoc} */
@Override public int hashCode() {
return id;
}
}
/**
*
*/
private enum EnumKey {
/** */
KEY1,
/** */
KEY2
}
/**
*
*/
private static class SearchValue {
/** */
@QuerySqlField
private UUID uuid;
/** */
@QuerySqlField
private String str;
/** */
@QuerySqlField
private BigDecimal decimal;
/** */
@QuerySqlField
private Integer integer;
/** */
@QuerySqlField
private Date date;
/** */
@QuerySqlField
private Timestamp ts;
/** */
@QuerySqlField
private Person person;
/** */
@QuerySqlField
private EnumKey enumKey;
/**
*
* @param uuid UUID.
* @param str String.
* @param decimal Decimal.
* @param integer Integer.
* @param date Date.
* @param ts Timestamp.
* @param person Person.
* @param enumKey Enum.
*/
SearchValue(
final UUID uuid,
final String str,
final BigDecimal decimal,
final Integer integer,
final Date date,
final Timestamp ts,
final Person person,
final EnumKey enumKey
) {
this.uuid = uuid;
this.str = str;
this.decimal = decimal;
this.integer = integer;
this.date = date;
this.ts = ts;
this.person = person;
this.enumKey = enumKey;
}
/** {@inheritDoc} */
@Override public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SearchValue that = (SearchValue) o;
if (uuid != null ? !uuid.equals(that.uuid) : that.uuid != null) return false;
if (str != null ? !str.equals(that.str) : that.str != null) return false;
if (decimal != null ? !decimal.equals(that.decimal) : that.decimal != null) return false;
if (integer != null ? !integer.equals(that.integer) : that.integer != null) return false;
if (date != null ? !date.equals(that.date) : that.date != null) return false;
if (ts != null ? !ts.equals(that.ts) : that.ts != null) return false;
if (person != null ? !person.equals(that.person) : that.person != null) return false;
return enumKey == that.enumKey;
}
/** {@inheritDoc} */
@Override public int hashCode() {
int res = uuid != null ? uuid.hashCode() : 0;
res = 31 * res + (str != null ? str.hashCode() : 0);
res = 31 * res + (decimal != null ? decimal.hashCode() : 0);
res = 31 * res + (integer != null ? integer.hashCode() : 0);
res = 31 * res + (date != null ? date.hashCode() : 0);
res = 31 * res + (ts != null ? ts.hashCode() : 0);
res = 31 * res + (person != null ? person.hashCode() : 0);
res = 31 * res + (enumKey != null ? enumKey.hashCode() : 0);
return res;
}
}
}