package org.nlpcn.es4sql;
import org.elasticsearch.plugin.nlpcn.ElasticHitsExecutor;
import org.elasticsearch.plugin.nlpcn.ElasticJoinExecutor;
import org.elasticsearch.plugin.nlpcn.MultiRequestExecutorFactory;
import org.elasticsearch.search.SearchHit;
import org.junit.Assert;
import org.junit.Test;
import org.nlpcn.es4sql.exception.SqlParseException;
import org.nlpcn.es4sql.query.SqlElasticRequestBuilder;
import java.io.IOException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.nlpcn.es4sql.TestsConstants.TEST_INDEX;
/**
* Created by Eliran on 21/8/2016.
*/
public class MultiQueryTests {
private static String MINUS_SCROLL_DEFAULT_HINT = " /*! MINUS_SCROLL_FETCH_AND_RESULT_LIMITS(1000,50,100) */ ";
private static String MINUS_TERMS_OPTIMIZATION_HINT = " /*! MINUS_USE_TERMS_OPTIMIZATION(true)*/ ";
@Test
public void unionAllSameRequestOnlyOneRecordTwice() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT firstname FROM %s/account WHERE firstname = 'Amber' limit 1 union all SELECT firstname FROM %s/account WHERE firstname = 'Amber'",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals(2,searchHits.length);
for(SearchHit hit : searchHits) {
Object firstname = hit.sourceAsMap().get("firstname");
Assert.assertEquals("Amber",firstname);
}
}
@Test
public void unionAllOnlyOneRecordEachWithAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT firstname FROM %s/account WHERE firstname = 'Amber' " +
"union all " +
"SELECT dog_name as firstname FROM %s/dog WHERE dog_name = 'rex'",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals(2,searchHits.length);
Set<String> names = new HashSet<>();
for(SearchHit hit : searchHits) {
Object firstname = hit.sourceAsMap().get("firstname");
names.add(firstname.toString());
}
Assert.assertTrue("names should contain Amber",names.contains("Amber"));
Assert.assertTrue("names should contain rex",names.contains("rex"));
}
@Test
public void unionAllOnlyOneRecordEachWithComplexAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT firstname FROM %s/account WHERE firstname = 'Amber' " +
"union all " +
"SELECT name.firstname as firstname FROM %s/gotCharacters WHERE name.firstname = 'Daenerys'",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals(2,searchHits.length);
Set<String> names = new HashSet<>();
for(SearchHit hit : searchHits) {
Object firstname = hit.sourceAsMap().get("firstname");
names.add(firstname.toString());
}
Assert.assertTrue("names should contain Amber",names.contains("Amber"));
Assert.assertTrue("names should contain Daenerys",names.contains("Daenerys"));
}
@Test
public void minusAMinusANoAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinusAMinusANoAlias("");
}
@Test
public void minusAMinusANoAliasWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinusAMinusANoAlias(MINUS_SCROLL_DEFAULT_HINT);
}
@Test
public void minusAMinusANoAliasWithScrollingAndTerms() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinusAMinusANoAlias(MINUS_SCROLL_DEFAULT_HINT + MINUS_TERMS_OPTIMIZATION_HINT);
}
private void innerMinusAMinusANoAlias(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT "+hint+" pk FROM %s/systems WHERE system_name = 'A' " +
"minus " +
"SELECT pk FROM %s/systems WHERE system_name = 'A' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("no hits should be return", 0, searchHits.length);
}
@Test
public void minusAMinusBNoAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_AMinusBNoAlias("");
}
@Test
public void minusAMinusBNoAliasWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_AMinusBNoAlias(MINUS_SCROLL_DEFAULT_HINT);
}
@Test
public void minusAMinusBNoAliasWithScrollingAndTerms () throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_AMinusBNoAlias(MINUS_SCROLL_DEFAULT_HINT + MINUS_TERMS_OPTIMIZATION_HINT);
}
private void innerMinus_AMinusBNoAlias(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT "+hint+" pk FROM %s/systems WHERE system_name = 'A' " +
"minus " +
"SELECT pk FROM %s/systems WHERE system_name = 'B' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("not exactly one hit returned", 1, searchHits.length);
Map<String, Object> sourceAsMap = searchHits[0].sourceAsMap();
Assert.assertEquals("source map not contained exactly one field",1,sourceAsMap.size());
Assert.assertTrue("source map should contain pk",sourceAsMap.containsKey("pk"));
Assert.assertEquals(3, sourceAsMap.get("pk"));
}
@Test
public void minusCMinusDTwoFieldsNoAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusDTwoFieldsNoAlias("");
}
@Test
public void minusCMinusDTwoFieldsNoAliasWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusDTwoFieldsNoAlias(MINUS_SCROLL_DEFAULT_HINT);
}
private void innerMinus_CMinusDTwoFieldsNoAlias(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT " + hint + " pk , letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT pk , letter FROM %s/systems WHERE system_name = 'D' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("not exactly one hit returned", 1, searchHits.length);
Map<String, Object> sourceAsMap = searchHits[0].sourceAsMap();
Assert.assertEquals("source map not contained exactly two fields",2,sourceAsMap.size());
Assert.assertTrue("source map should contain pk",sourceAsMap.containsKey("pk"));
Assert.assertTrue("source map should contain letter",sourceAsMap.containsKey("letter"));
Assert.assertEquals(1,sourceAsMap.get("pk"));
Assert.assertEquals("e",sourceAsMap.get("letter"));
}
@Test
public void minusCMinusDTwoFieldsAliasOnBothSecondTableFields() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT pk , letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT myId as pk , myLetter as letter FROM %s/systems WHERE system_name = 'E' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("not exactly one hit returned",1,searchHits.length);
Map<String, Object> sourceAsMap = searchHits[0].sourceAsMap();
Assert.assertEquals("source map not contained exactly two fields",2,sourceAsMap.size());
Assert.assertTrue("source map should contain pk",sourceAsMap.containsKey("pk"));
Assert.assertTrue("source map should contain letter",sourceAsMap.containsKey("letter"));
Assert.assertEquals(1,sourceAsMap.get("pk"));
Assert.assertEquals("e",sourceAsMap.get("letter"));
}
@Test
public void minusCMinusDTwoFieldsAliasOnBothTables() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusDTwoFieldsAliasOnBothTables("");
}
@Test
public void minusCMinusDTwoFieldsAliasOnBothTablesWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusDTwoFieldsAliasOnBothTables(MINUS_SCROLL_DEFAULT_HINT);
}
private void innerMinus_CMinusDTwoFieldsAliasOnBothTables(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT "+hint+" pk as myId , letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT myId , myLetter as letter FROM %s/systems WHERE system_name = 'E' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("not exactly one hit returned", 1, searchHits.length);
Map<String, Object> sourceAsMap = searchHits[0].sourceAsMap();
Assert.assertEquals("source map not contained exactly two fields",2,sourceAsMap.size());
Assert.assertTrue("source map should contain pk",sourceAsMap.containsKey("myId"));
Assert.assertTrue("source map should contain letter",sourceAsMap.containsKey("letter"));
Assert.assertEquals(1,sourceAsMap.get("myId"));
Assert.assertEquals("e",sourceAsMap.get("letter"));
}
@Test
public void minusCMinusCTwoFields_OneAlias() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT pk as myId , letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT pk as myId , letter FROM %s/systems WHERE system_name = 'C' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("no hits should be returned", 0, searchHits.length);
}
@Test
public void minusCMinusTNoExistsTwoFields() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
String query = String.format("SELECT pk , letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT pk , letter FROM %s/systems WHERE system_name = 'T' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("all hits should be returned",3,searchHits.length);
}
@Test
public void minusCMinusTNoExistsOneField() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusTNoExistsOneField("");
}
@Test
public void minusCMinusTNoExistsOneFieldWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusTNoExistsOneField(MINUS_SCROLL_DEFAULT_HINT);
}
@Test
public void minusCMinusTNoExistsOneFieldWithScrollingAndOptimization() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_CMinusTNoExistsOneField(MINUS_SCROLL_DEFAULT_HINT + MINUS_TERMS_OPTIMIZATION_HINT);
}
private void innerMinus_CMinusTNoExistsOneField(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT "+hint+" letter FROM %s/systems WHERE system_name = 'C' " +
"minus " +
"SELECT letter FROM %s/systems WHERE system_name = 'T' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("all hits should be returned", 3, searchHits.length);
}
@Test
public void minusTMinusCNoExistsFirstQuery() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_TMinusCNoExistsFirstQuery("");
}
@Test
public void minusTMinusCNoExistsFirstQueryWithScrolling() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_TMinusCNoExistsFirstQuery(MINUS_SCROLL_DEFAULT_HINT);
}
@Test
public void minusTMinusCNoExistsFirstQueryWithScrollingAndOptimization() throws SQLFeatureNotSupportedException, IOException, SqlParseException {
innerMinus_TMinusCNoExistsFirstQuery(MINUS_SCROLL_DEFAULT_HINT + MINUS_TERMS_OPTIMIZATION_HINT);
}
private void innerMinus_TMinusCNoExistsFirstQuery(String hint) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
String query = String.format("SELECT "+hint+" letter FROM %s/systems WHERE system_name = 'T' " +
"minus " +
"SELECT letter FROM %s/systems WHERE system_name = 'C' ",TEST_INDEX,TEST_INDEX);
SearchHit[] searchHits = executeAndGetHits(query);
Assert.assertEquals("not hits should be returned", 0, searchHits.length);
}
private SearchHit[] executeAndGetHits(String query) throws SqlParseException, SQLFeatureNotSupportedException, IOException {
SearchDao searchDao = MainTestSuite.getSearchDao();
SqlElasticRequestBuilder explain = searchDao.explain(query).explain();
ElasticHitsExecutor executor = MultiRequestExecutorFactory.createExecutor(searchDao.getClient(),(org.nlpcn.es4sql.query.multi.MultiQueryRequestBuilder) explain);
executor.run();
return executor.getHits().getHits();
}
}