package org.molgenis.data.elasticsearch.request;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.index.query.*;
import org.mockito.ArgumentCaptor;
import org.molgenis.data.DataConverter;
import org.molgenis.data.Entity;
import org.molgenis.data.MolgenisQueryException;
import org.molgenis.data.Query;
import org.molgenis.data.elasticsearch.index.MappingsBuilder;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.AttributeFactory;
import org.molgenis.data.meta.model.EntityType;
import org.molgenis.data.meta.model.EntityTypeFactory;
import org.molgenis.data.support.QueryImpl;
import org.molgenis.test.data.AbstractMolgenisSpringTest;
import org.molgenis.util.MolgenisDateFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.molgenis.data.elasticsearch.index.ElasticsearchIndexCreator.DEFAULT_ANALYZER;
import static org.molgenis.data.meta.AttributeType.*;
import static org.molgenis.data.meta.model.EntityType.AttributeRole.ROLE_ID;
import static org.molgenis.data.meta.model.EntityType.AttributeRole.ROLE_LABEL;
import static org.testng.Assert.assertEquals;
/**
* Copy of standard QueryGeneratorTest but now with queries done on the referenced entity
*/
// FIXME add nillable tests
public class QueryGeneratorReferencesTest extends AbstractMolgenisSpringTest
{
private SearchRequestBuilder searchRequestBuilder;
private EntityType entityType;
private final String refIdAttributeName = "xid";
private final String refBoolAttributeName = "xbool";
private final String refCategoricalAttributeName = "xcategorical";
private final String refCompoundAttributeName = "xcompound";
private final String refCompoundPart0AttributeName = "xcompoundpart0";
private final String refCompoundPart1AttributeName = "xcompoundpart1";
private final String refDateAttributeName = "xdate";
private final String refDateTimeAttributeName = "xdatetime";
private final String refDecimalAttributeName = "xdecimal";
private final String refEmailAttributeName = "xemail";
private final String refEnumAttributeName = "xenum";
private final String refHtmlAttributeName = "xhtml";
private final String refHyperlinkAttributeName = "xhyperlink";
private final String refIntAttributeName = "xint";
private final String refLongAttributeName = "xlong";
private final String refMrefAttributeName = "xmref";
private final String refScriptAttributeName = "xscript";
private final String refStringAttributeName = "xstring";
private final String refTextAttributeName = "xtext";
private final String refXrefAttributeName = "xxref";
private final String idAttributeName = "id";
private final String stringAttributeName = "string";
private final String mrefAttributeName = "mref";
private final String REF_ENTITY_ATT = "mref";
private final String PREFIX = REF_ENTITY_ATT + QueryGenerator.ATTRIBUTE_SEPARATOR;
@Autowired
EntityTypeFactory entityTypeFactory;
@Autowired
AttributeFactory attrFactory;
@BeforeMethod
public void setUp()
{
searchRequestBuilder = mock(SearchRequestBuilder.class);
EntityType refEntityType = entityTypeFactory.create().setName("ref_entity");
refEntityType.addAttribute(attrFactory.create().setName(refIdAttributeName), ROLE_ID);
refEntityType.addAttribute(attrFactory.create().setName(refBoolAttributeName).setDataType(BOOL));
refEntityType.addAttribute(attrFactory.create().setName(refCategoricalAttributeName).setDataType(CATEGORICAL)
.setRefEntity(refEntityType).setNillable(true));
Attribute attrCompound = attrFactory.create().setName(refCompoundAttributeName).setDataType(COMPOUND);
Attribute compoundPart0Attribute = attrFactory.create().setName(refCompoundPart0AttributeName)
.setDataType(STRING).setParent(attrCompound);
Attribute compoundPart1Attribute = attrFactory.create().setName(refCompoundPart1AttributeName)
.setDataType(STRING).setParent(attrCompound);
refEntityType.addAttribute(attrCompound);
refEntityType.addAttribute(compoundPart0Attribute);
refEntityType.addAttribute(compoundPart1Attribute);
refEntityType.addAttribute(attrFactory.create().setName(refDateAttributeName).setDataType(DATE));
refEntityType.addAttribute(attrFactory.create().setName(refDateTimeAttributeName).setDataType(DATE_TIME));
refEntityType.addAttribute(attrFactory.create().setName(refDecimalAttributeName).setDataType(DECIMAL));
refEntityType.addAttribute(attrFactory.create().setName(refEmailAttributeName).setDataType(EMAIL));
refEntityType.addAttribute(attrFactory.create().setName(refEnumAttributeName).setDataType(ENUM)
.setEnumOptions(Arrays.asList("enum0", "enum1", "enum2")));
refEntityType.addAttribute(attrFactory.create().setName(refHtmlAttributeName).setDataType(HTML));
refEntityType.addAttribute(attrFactory.create().setName(refHyperlinkAttributeName).setDataType(HYPERLINK));
refEntityType.addAttribute(attrFactory.create().setName(refIntAttributeName).setDataType(INT));
refEntityType.addAttribute(attrFactory.create().setName(refLongAttributeName).setDataType(LONG));
refEntityType.addAttribute(
attrFactory.create().setName(refMrefAttributeName).setDataType(MREF).setRefEntity(refEntityType)
.setNillable(true));
refEntityType.addAttribute(attrFactory.create().setName(refScriptAttributeName).setDataType(SCRIPT));
refEntityType.addAttribute(attrFactory.create().setName(refStringAttributeName).setDataType(STRING));
refEntityType.addAttribute(attrFactory.create().setName(refTextAttributeName).setDataType(TEXT));
refEntityType.addAttribute(
attrFactory.create().setName(refXrefAttributeName).setDataType(XREF).setRefEntity(refEntityType)
.setNillable(true));
EntityType emd = entityTypeFactory.create().setName("entity");
emd.addAttribute(attrFactory.create().setName(idAttributeName), ROLE_ID);
emd.addAttribute(attrFactory.create().setName(stringAttributeName).setUnique(true), ROLE_LABEL);
emd.addAttribute(attrFactory.create().setName(mrefAttributeName).setDataType(MREF).setNillable(true)
.setRefEntity(refEntityType));
this.entityType = emd;
}
@Test
public void generateOneQueryRuleGreaterDate() throws ParseException
{
String date = "2015-05-22";
Date value = MolgenisDateFormat.getDateFormat().parse(date);
Query<Entity> q = new QueryImpl<Entity>().gt(PREFIX + refDateAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refDateAttributeName).gt(date)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleGreaterDateTime() throws ParseException
{
Date value = MolgenisDateFormat.getDateFormat().parse("2015-05-22T11:12:13+0500");
Query<Entity> q = new QueryImpl<Entity>().gt(PREFIX + refDateTimeAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refDateTimeAttributeName)
.gt(DataConverter.toString(value))));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleGreaterDecimal()
{
Double value = Double.valueOf(1.23);
Query<Entity> q = new QueryImpl<Entity>().gt(PREFIX + refDecimalAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refDecimalAttributeName).gt(value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleGreaterInt()
{
Integer value = Integer.valueOf(1);
Query<Entity> q = new QueryImpl<Entity>().gt(PREFIX + refIntAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refIntAttributeName).gt(value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleGreaterLong()
{
Long value = Long.valueOf(1l);
Query<Entity> q = new QueryImpl<Entity>().gt(PREFIX + refLongAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refLongAttributeName).gt(value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleGreaterEqualDate() throws ParseException
{
String date = "2015-05-22";
Date value = MolgenisDateFormat.getDateFormat().parse(date);
Query<Entity> q = new QueryImpl<Entity>().ge(PREFIX + refDateAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refDateAttributeName).gte(date)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleLesserEqualDecimal()
{
Double value = Double.valueOf(1.23);
Query<Entity> q = new QueryImpl<>().le(PREFIX + refDecimalAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refDecimalAttributeName).lte(value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleLesserInt()
{
Integer value = Integer.valueOf(1);
Query<Entity> q = new QueryImpl<>().lt(PREFIX + refIntAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refIntAttributeName).lt(value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void generateOneQueryRuleInCategorical_Ids()
{
Iterable<String> values = Arrays.asList("id0", "id1", "id2");
Query<Entity> q = new QueryImpl<>().in(PREFIX + refCategoricalAttributeName, values);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
}
@Test
public void generateOneQueryRuleLikeCompoundPartString()
{
String value = "value";
Query<Entity> q = new QueryImpl<Entity>().like(PREFIX + refCompoundPart0AttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.nestedQuery(REF_ENTITY_ATT, QueryBuilders
.matchQuery(PREFIX + refCompoundPart0AttributeName + '.' + MappingsBuilder.FIELD_NGRAM_ANALYZED, value)
.analyzer(DEFAULT_ANALYZER));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleEqualsBool()
{
Boolean value = Boolean.TRUE;
Query<Entity> q = new QueryImpl<Entity>().eq(PREFIX + refBoolAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.termFilter(PREFIX + refBoolAttributeName, value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleEqualsString()
{
String value = "value";
Query<Entity> q = new QueryImpl<Entity>().eq(PREFIX + refStringAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders
.termFilter(PREFIX + refStringAttributeName + '.' + MappingsBuilder.FIELD_NOT_ANALYZED,
value)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void generateOneQueryRuleEqualsCategorical()
{
String value = "id";
Query<Entity> q = new QueryImpl<Entity>().eq(PREFIX + refCategoricalAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
}
@Test
public void generateOneQueryRuleNotEqualsEqualsBool()
{
Boolean value = Boolean.TRUE;
Query<Entity> q = new QueryImpl<Entity>().not().eq(PREFIX + refBoolAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders
.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.nestedFilter(REF_ENTITY_ATT,
FilterBuilders.termFilter(PREFIX + refBoolAttributeName, value))));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void generateOneQueryRuleNotEqualsCategorical()
{
String value = "id";
Query<Entity> q = new QueryImpl<Entity>().not().eq(PREFIX + refCategoricalAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
}
@Test(expectedExceptions = MolgenisQueryException.class)
public void generateOneQueryRuleNotEqualsCompound()
{
Object value = "value";
Query<Entity> q = new QueryImpl<Entity>().not().eq(PREFIX + refCompoundAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
}
@Test
public void generateOneQueryRuleNotEqualsCompoundPartString()
{
String value = "value";
Query<Entity> q = new QueryImpl<Entity>().not().eq(PREFIX + refCompoundPart0AttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders
.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.nestedFilter(REF_ENTITY_ATT, FilterBuilders
.termFilter(PREFIX + refCompoundPart0AttributeName + '.' + MappingsBuilder.FIELD_NOT_ANALYZED,
value))));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateOneQueryRuleRangeInt()
{
Integer low = Integer.valueOf(3);
Integer high = Integer.valueOf(9);
Query<Entity> q = new QueryImpl<Entity>().rng(PREFIX + refIntAttributeName, low, high);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.rangeFilter(PREFIX + refIntAttributeName).gte(3).lte(9)));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void generateOneQueryRuleSearchOneFieldCategorical()
{
String value = "text";
Query<Entity> q = new QueryImpl<Entity>().search(PREFIX + refCategoricalAttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
}
@Test
public void generateOneQueryRuleSearchOneFieldCompoundPartString()
{
String value = "value";
Query<Entity> q = new QueryImpl<Entity>().search(PREFIX + refCompoundPart0AttributeName, value);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
QueryBuilder expectedQuery = QueryBuilders
.nestedQuery(REF_ENTITY_ATT, QueryBuilders.matchQuery(PREFIX + refCompoundPart0AttributeName, value));
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateMultipleQueryRule()
{
// query: ref.a or (b and ref.c)
Boolean booleanValue = Boolean.TRUE;
String stringValue = "str";
Integer intValue = 1;
Query<Entity> q = new QueryImpl<Entity>().eq(PREFIX + refBoolAttributeName, booleanValue).or().nest()
.eq(stringAttributeName, stringValue).and().eq(PREFIX + refIntAttributeName, intValue).unnest();
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
FilteredQueryBuilder booleanQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.termFilter(PREFIX + refBoolAttributeName, booleanValue)));
QueryBuilder stringQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
FilterBuilders.termFilter(stringAttributeName + '.' + MappingsBuilder.FIELD_NOT_ANALYZED, stringValue));
QueryBuilder intQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.termFilter(PREFIX + refIntAttributeName, intValue)));
BoolQueryBuilder stringIntQuery = QueryBuilders.boolQuery().must(stringQuery).must(intQuery);
QueryBuilder expectedQuery = QueryBuilders.boolQuery().should(booleanQuery).should(stringIntQuery)
.minimumNumberShouldMatch(1);
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
@Test
public void generateMultipleQueryRuleMultipleNotClauses()
{
// query: ref.a and not b and not ref.c
Boolean booleanValue = Boolean.TRUE;
String stringValue = "str";
Integer intValue = 1;
Query<Entity> q = new QueryImpl<Entity>().eq(PREFIX + refBoolAttributeName, booleanValue).and().not()
.eq(stringAttributeName, stringValue).and().not().eq(PREFIX + refIntAttributeName, intValue);
new QueryGenerator().generate(searchRequestBuilder, q, entityType);
ArgumentCaptor<QueryBuilder> captor = ArgumentCaptor.forClass(QueryBuilder.class);
verify(searchRequestBuilder).setQuery(captor.capture());
FilteredQueryBuilder booleanQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.termFilter(PREFIX + refBoolAttributeName, booleanValue)));
QueryBuilder stringQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(),
FilterBuilders.termFilter(stringAttributeName + '.' + MappingsBuilder.FIELD_NOT_ANALYZED, stringValue));
QueryBuilder intQuery = QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders
.nestedFilter(REF_ENTITY_ATT, FilterBuilders.termFilter(PREFIX + refIntAttributeName, intValue)));
QueryBuilder expectedQuery = QueryBuilders.boolQuery().must(booleanQuery).mustNot(stringQuery)
.mustNot(intQuery);
assertQueryBuilderEquals(captor.getValue(), expectedQuery);
}
private void assertQueryBuilderEquals(QueryBuilder actual, QueryBuilder expected)
{
// QueryBuilder classes do not implement equals
assertEquals(actual.toString().replaceAll("\\s", ""), expected.toString().replaceAll("\\s", ""));
}
}