/*
* Copyright (c) 2017 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.magma.js.methods;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.obiba.magma.Datasource;
import org.obiba.magma.DatasourceFactory;
import org.obiba.magma.MagmaEngine;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueTable;
import org.obiba.magma.ValueTableWriter;
import org.obiba.magma.Variable;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.VariableValueSource;
import org.obiba.magma.VectorSource;
import org.obiba.magma.datasource.generated.GeneratedValueTable;
import org.obiba.magma.test.EmbeddedMongoProcessWrapper;
import org.obiba.magma.datasource.mongodb.MongoDBDatasourceFactory;
import org.obiba.magma.js.AbstractJsTest;
import org.obiba.magma.js.views.VariablesClause;
import org.obiba.magma.support.DatasourceCopier;
import org.obiba.magma.type.IntegerType;
import org.obiba.magma.views.DefaultViewManagerImpl;
import org.obiba.magma.views.MemoryViewPersistenceStrategy;
import org.obiba.magma.views.View;
import org.obiba.magma.views.ViewManager;
import org.obiba.magma.xstream.MagmaXStreamExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.obiba.magma.Variable.Builder.newVariable;
@SuppressWarnings({ "OverlyLongMethod", "PMD.NcssMethodCount", "OverlyCoupledClass" })
public class GlobalMethodsMongoDbTest extends AbstractJsTest {
private static final Logger log = LoggerFactory.getLogger(GlobalMethodsMongoDbTest.class);
private static final String MONGO_DB_TEST = "magma-test";
private static final String DATASOURCE = "ds";
private static final String TABLE = "table";
private static final String TABLE2 = "table2";
private static final String VARIABLE_AGE = "age";
private static final String VARIABLE_WEIGHT = "weight";
private static final String VARIABLE_HEIGHT = "height";
private static final int NB_ENTITIES = 150;
private static final int NB_ENTITIES2 = 200;
private ViewManager viewManager;
private EmbeddedMongoProcessWrapper mongo;
private String mongoDbUrl;
@Before
@Override
public void before() {
super.before();
// run test only if MongoDB is running
Assume.assumeTrue(setupMongoDB());
viewManager = new DefaultViewManagerImpl(new MemoryViewPersistenceStrategy());
}
private boolean setupMongoDB() {
try {
mongo = new EmbeddedMongoProcessWrapper();
mongo.start();
mongoDbUrl = "mongodb://" + mongo.getServerSocketAddress() + '/' + MONGO_DB_TEST;
return true;
} catch(Exception e) {
return false;
}
}
@After
@Override
public void after() {
super.after();
mongo.stop();
}
@Override
protected MagmaEngine newEngine() {
MagmaEngine magmaEngine = super.newEngine();
magmaEngine.extend(new MagmaXStreamExtension());
return magmaEngine;
}
private Datasource getTestDatasource() throws IOException {
DatasourceFactory factory = new MongoDBDatasourceFactory(DATASOURCE, mongoDbUrl);
Datasource datasource = factory.create();
List<Variable> variables = Lists.newArrayList( //
newVariable(VARIABLE_AGE, IntegerType.get(), PARTICIPANT).addAttribute("min", "25").addAttribute("max", "90")
.build(), //
newVariable(VARIABLE_WEIGHT, IntegerType.get(), PARTICIPANT).unit("kg").addAttribute("min", "50")
.addAttribute("max", "120").build(), //
newVariable(VARIABLE_HEIGHT, IntegerType.get(), PARTICIPANT).unit("cm").addAttribute("min", "150")
.addAttribute("max", "200").build());
ValueTable generatedValueTable = new GeneratedValueTable(datasource, variables, NB_ENTITIES);
ValueTable generatedValueTable2 = new GeneratedValueTable(datasource, variables, NB_ENTITIES2);
Datasource viewAwareDatasource = viewManager.decorate(datasource);
MagmaEngine.get().addDatasource(viewAwareDatasource);
DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, TABLE, datasource);
DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable2, TABLE2, datasource);
return viewAwareDatasource;
}
@Test
public void test_$_value_set() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable kgWeight = table.getVariable(VARIABLE_WEIGHT);
Variable lbsWeight = createIntVariable("weight_in_lbs", "$('ds.table:weight') * 2.2");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(lbsWeight);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
Map<String,Long> tableValues = Maps.newHashMap();
for(ValueSet valueSet : table.getValueSets()) {
tableValues.put(valueSet.getVariableEntity().getIdentifier(), (Long) table.getValue(kgWeight, valueSet).getValue());
}
Map<String, Long> viewValues = Maps.newHashMap();
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
viewValues.put(valueSet.getVariableEntity().getIdentifier(), (Long) view.getValue(lbsWeight, valueSet).getValue());
}
assertThat(tableValues.size()).isEqualTo(viewValues.size()).isEqualTo(NB_ENTITIES);
for(String id : tableValues.keySet()) {
Long kg = tableValues.get(id);
Long lbs = viewValues.get(id);
assertThat(lbs).isEqualTo((long) (kg * 2.2));
}
}
@Test
public void test_$_join_value_set() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
ValueTable table2 = datasource.getValueTable(TABLE2);
Variable weight = createIntVariable("weight", "$('weight')", true);
Variable weight1 = createIntVariable("weight1", "$('ds.table:weight')");
Variable weight2 = createIntVariable("weight2", "$('ds.table2:weight')");
View viewTemplate = View.Builder.newView("view", table, table2).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(weight);
variableWriter.writeVariable(weight1);
variableWriter.writeVariable(weight2);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
Set<VariableEntity> allEntities = Sets.newLinkedHashSet();
allEntities.addAll(table.getVariableEntities());
allEntities.addAll(table2.getVariableEntities());
assertThat(view.getValueSetCount()).isEqualTo(allEntities.size());
for(ValueSet vs : view.getValueSets()) {
Value w = view.getValue(weight, vs);
Value w1 = view.getValue(weight1, vs);
Value w2 = view.getValue(weight2, vs);
assertThat(w.isSequence()).isTrue();
assertThat(w.asSequence().get(0)).isEqualTo(w1);
assertThat(w.asSequence().get(1)).isEqualTo(w2);
}
}
@Test
public void test_$_vector() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable lbsWeight = createIntVariable("weight_in_lbs", "$('ds.table:weight') * 2.2");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(lbsWeight);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
VectorSource tableVectorSource = table.getVariableValueSource("weight").asVectorSource();
assertThat(tableVectorSource).isNotNull();
List<Value> tableValues = Lists
.newArrayList(tableVectorSource.getValues(new TreeSet<>(table.getVariableEntities())));
View view = viewManager.getView(DATASOURCE, "view");
VectorSource viewVectorSource = view.getVariableValueSource("weight_in_lbs").asVectorSource();
assertThat(viewVectorSource).isNotNull();
int i = 0;
for(Value viewValue : viewVectorSource.getValues(new TreeSet<>(view.getVariableEntities()))) {
Long kg = (Long) tableValues.get(i++).getValue();
Long lbs = (Long) viewValue.getValue();
assertThat(lbs).isEqualTo((long) (kg * 2.2));
}
}
@Test
public void test_$_join_vector() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
ValueTable table2 = datasource.getValueTable(TABLE2);
Variable weight = createIntVariable("weight", "$('weight')", true);
Variable weight1 = createIntVariable("weight1", "$('ds.table:weight')");
Variable weight2 = createIntVariable("weight2", "$('ds.table2:weight')");
View viewTemplate = View.Builder.newView("view", table, table2).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(weight);
variableWriter.writeVariable(weight1);
variableWriter.writeVariable(weight2);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
VectorSource weightVector = view.getVariableValueSource("weight").asVectorSource();
VectorSource weight1Vector = view.getVariableValueSource("weight1").asVectorSource();
VectorSource weight2Vector = view.getVariableValueSource("weight2").asVectorSource();
SortedSet<VariableEntity> entities = Sets.newTreeSet(view.getVariableEntities());
Iterator<Value> weightValues = weightVector.getValues(entities).iterator();
Iterator<Value> weight1Values = weight1Vector.getValues(entities).iterator();
Iterator<Value> weight2Values = weight2Vector.getValues(entities).iterator();
for(VariableEntity entity : entities) {
Value w = weightValues.next();
Value w1 = weight1Values.next();
Value w2 = weight2Values.next();
assertThat(w.isSequence()).isTrue();
assertThat(w.asSequence().get(0)).isEqualTo(w1);
assertThat(w.asSequence().get(1)).isEqualTo(w2);
}
}
@Test
public void test_$_value_set_with_valid_dependencies() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable varA = createIntVariable("A", "$('ds.view:B') + $('ds.view:C')");
Variable varB = createIntVariable("B", "10");
Variable varC = createIntVariable("C", "$('ds.view:D') * 10");
Variable varD = createIntVariable("D", "$('ds.view:E') + 5");
Variable varE = createIntVariable("E", "5");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(varA);
variableWriter.writeVariable(varB);
variableWriter.writeVariable(varC);
variableWriter.writeVariable(varD);
variableWriter.writeVariable(varE);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
view.getValue(varA, valueSet);
}
}
@Test
public void test_$_vector_with_valid_dependencies() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable varA = createIntVariable("A", "$('ds.view:B') + $('ds.view:C')");
Variable varB = createIntVariable("B", "10");
Variable varC = createIntVariable("C", "$('ds.view:D') * 10");
Variable varD = createIntVariable("D", "$('ds.view:E') + 5");
Variable varE = createIntVariable("E", "5");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(varA);
variableWriter.writeVariable(varB);
variableWriter.writeVariable(varC);
variableWriter.writeVariable(varD);
variableWriter.writeVariable(varE);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
VariableValueSource variableValueSource = view.getVariableValueSource("A");
assertThat(variableValueSource.supportVectorSource()).isTrue();
VectorSource vectorSource = variableValueSource.asVectorSource();
assertThat(vectorSource).isNotNull();
for(Value value : vectorSource.getValues(new TreeSet<>(view.getVariableEntities()))) {
value.getValue();
}
}
@Test
public void test_$this_simple_algo_value_set() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable kgWeight = table.getVariable("weight");
Variable kgWeightRef = createIntVariable("weight_in_kg", "$('ds.table:weight')");
Variable lbsWeight = createIntVariable("weight_in_lbs", "$this('weight_in_kg') * 2.2");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(kgWeightRef);
variableWriter.writeVariable(lbsWeight);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
Map<String,Long> tableValues = Maps.newHashMap();
for(ValueSet valueSet : table.getValueSets()) {
tableValues.put(valueSet.getVariableEntity().getIdentifier(), (Long) table.getValue(kgWeight, valueSet).getValue());
}
Map<String,Long> viewValues = Maps.newHashMap();
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
viewValues.put(valueSet.getVariableEntity().getIdentifier(), (Long) view.getValue(lbsWeight, valueSet).getValue());
}
for(String id : tableValues.keySet()) {
Long kg = tableValues.get(id);
Long lbs = viewValues.get(id);
assertThat(lbs).isEqualTo((long) (kg * 2.2));
}
}
@Test
public void test_$this_bmi_value_set() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable weight_in_kg = createIntVariable("weight_in_kg", "$('ds.table:weight')");
Variable height_in_cm = createIntVariable("height_in_cm", "$('ds.table:height')");
Variable height_in_m = createDecimalVariable("height_in_m", "$this('height_in_cm') / 100");
Variable weight_in_lbs = createIntVariable("weight_in_lbs", "$this('weight_in_kg') * 2.20462");
Variable height_in_inches = createIntVariable("height_in_inches", "$this('height_in_cm') * 0.393701");
Variable bmi_metric = createDecimalVariable("bmi_metric",
"$this('weight_in_kg') / ($this('height_in_m') * $this('height_in_m'))");
Variable bmi = createDecimalVariable("bmi",
"$this('weight_in_lbs') / ($this('height_in_inches') * $this('height_in_inches')) * 703");
Collection<Variable> variables = Lists
.newArrayList(weight_in_kg, height_in_cm, height_in_m, bmi_metric, weight_in_lbs, height_in_inches, bmi);
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
for(Variable variable : variables) {
variableWriter.writeVariable(variable);
}
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
// log.debug("weight_in_kg: {}", view.getValue(weight_in_kg, valueSet));
// log.debug("weight_in_lbs: {}", view.getValue(weight_in_lbs, valueSet));
// log.debug("height_in_cm: {}", view.getValue(height_in_cm, valueSet));
// log.debug("height_in_m: {}", view.getValue(height_in_m, valueSet));
// log.debug("height_in_inches {}", view.getValue(height_in_inches, valueSet));
// log.debug("bmi_metric: {}", view.getValue(bmi_metric, valueSet));
// log.debug("bmi: {}", view.getValue(bmi, valueSet));
double bmiValue = (double) view.getValue(bmi, valueSet).getValue();
double bmiMetricValue = (double) view.getValue(bmi_metric, valueSet).getValue();
assertThat(Math.abs(bmiValue - bmiMetricValue)).isLessThan(2);
}
}
@Test
public void test_$this_vector() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable weight_in_kg = createIntVariable("weight_in_kg", "$('ds.table:weight')");
Variable height_in_cm = createIntVariable("height_in_cm", "$('ds.table:height')");
Variable height_in_m = createDecimalVariable("height_in_m", "$this('height_in_cm') / 100");
Variable weight_in_lbs = createIntVariable("weight_in_lbs", "$this('weight_in_kg') * 2.20462");
Variable height_in_inches = createIntVariable("height_in_inches", "$this('height_in_cm') * 0.393701");
Variable bmi_metric = createDecimalVariable("bmi_metric",
"$this('weight_in_kg') / ($this('height_in_m') * $this('height_in_m'))");
Variable bmi = createDecimalVariable("bmi",
"$this('weight_in_lbs') / ($this('height_in_inches') * $this('height_in_inches')) * 703");
Collection<Variable> variables = Lists
.newArrayList(weight_in_kg, height_in_cm, height_in_m, bmi_metric, weight_in_lbs, height_in_inches, bmi);
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
for(Variable variable : variables) {
variableWriter.writeVariable(variable);
}
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
SortedSet<VariableEntity> entities = new TreeSet<>(view.getVariableEntities());
List<Double> bmiValues = new ArrayList<>();
for(Value viewValue : view.getVariableValueSource("bmi").asVectorSource().getValues(entities)) {
bmiValues.add((Double) viewValue.getValue());
}
List<Double> bmiMetricValues = new ArrayList<>();
for(Value viewValue : view.getVariableValueSource("bmi_metric").asVectorSource().getValues(entities)) {
bmiMetricValues.add((Double) viewValue.getValue());
}
assertThat(bmiValues.size()).isEqualTo(bmiMetricValues.size()).isEqualTo(NB_ENTITIES);
for(int i = 0; i < NB_ENTITIES; i++) {
assertThat(Math.abs(bmiValues.get(i) - bmiMetricValues.get(i))).isLessThan(2);
}
}
@Test
public void test_$this_value_set_performance() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Collection<Variable> variables = Lists.newArrayList( //
createIntVariable("A", "$('ds.table:weight')"), //
createIntVariable("B", "$this('A')"), //
createIntVariable("C", "$this('B')"));
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
for(Variable variable : variables) {
variableWriter.writeVariable(variable);
}
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
Variable varC = view.getVariable("C");
Stopwatch stopwatch = Stopwatch.createStarted();
for(ValueSet valueSet : view.getValueSets()) {
view.getValue(varC, valueSet).getValue();
}
log.info("Load {} value sets in {}", NB_ENTITIES, stopwatch.stop());
assertThat(stopwatch.elapsed(TimeUnit.SECONDS)).isLessThan(1);
}
@Test
public void test_$this_vector_performance() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Collection<Variable> variables = Lists.newArrayList( //
createIntVariable("A", "$('ds.table:weight')"), //
createIntVariable("B", "$this('A')"), //
createIntVariable("C", "$this('B')"));
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
for(Variable variable : variables) {
variableWriter.writeVariable(variable);
}
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
Stopwatch stopwatch = Stopwatch.createStarted();
SortedSet<VariableEntity> entities = new TreeSet<>(view.getVariableEntities());
log.info("Load {} entities in {}", entities.size(), stopwatch);
stopwatch.reset().start();
for(Value viewValue : view.getVariableValueSource("C").asVectorSource().getValues(entities)) {
viewValue.getValue();
}
log.info("Load vector for {} entities in {}", NB_ENTITIES, stopwatch.stop());
assertThat(stopwatch.elapsed(TimeUnit.SECONDS)).isLessThan(1);
datasource.dispose();
}
@Test
public void test_$this_value_set_with_inner_$this() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable varA = createIntVariable("A", "$this('B') + $this('C') + $this('D')");
Variable varB = createIntVariable("B", "1");
Variable varC = createIntVariable("C", "10");
Variable varD = createIntVariable("D", "$this('B') * 5");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(varA);
variableWriter.writeVariable(varB);
variableWriter.writeVariable(varC);
variableWriter.writeVariable(varD);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
view.getValue(varA, valueSet);
}
}
@Test
public void test_$this_vector_with_inner_$this() throws Exception {
Datasource datasource = getTestDatasource();
ValueTable table = datasource.getValueTable(TABLE);
Variable varA = createIntVariable("A", "$this('B') + $this('C') + $this('D')");
Variable varB = createIntVariable("B", "1");
Variable varC = createIntVariable("C", "10");
Variable varD = createIntVariable("D", "$this('B') * 5");
View viewTemplate = View.Builder.newView("view", table).list(new VariablesClause()).build();
try(ValueTableWriter.VariableWriter variableWriter = viewTemplate.getListClause().createWriter()) {
variableWriter.writeVariable(varA);
variableWriter.writeVariable(varB);
variableWriter.writeVariable(varC);
variableWriter.writeVariable(varD);
}
viewManager.addView(DATASOURCE, viewTemplate, null, null);
View view = viewManager.getView(DATASOURCE, "view");
for(ValueSet valueSet : view.getValueSets()) {
view.getValue(varA, valueSet);
}
VectorSource vectorSource = view.getVariableValueSource("A").asVectorSource();
assertThat(vectorSource).isNotNull();
for(Value value : vectorSource.getValues(new TreeSet<>(view.getVariableEntities()))) {
value.getValue();
}
}
}