Repository: opencb/opencga (language: python)

Issue:
TASK-6596 - Error loading long variants: DocValuesField "variantId" is too large, must be <=32766



Discussion / hints:

Fix patch:
diff --git a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataRepairTool.java b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataRepairTool.java
index 04606492bff..6121cf1e247 100644
--- a/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataRepairTool.java
+++ b/opencga-analysis/src/main/java/org/opencb/opencga/analysis/variant/operations/VariantStorageMetadataRepairTool.java
@@ -134,7 +134,6 @@ private void rebuildSampleFileIds(VariantStorageMetadataManager metadataManager,
             for (Map.Entry<Integer, List<Integer>> entry : batch.entrySet()) {
                 Integer sampleId = entry.getKey();
                 List<Integer> fileIds = entry.getValue();
-
                 List<Integer> actualFiles = metadataManager.getSampleMetadata(studyId, sampleId).getFiles();
                 if (actualFiles.size() != fileIds.size() || !actualFiles.containsAll(fileIds)) {
                     fixedSamples++;
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java
index 680d30a20db..1e8dcdc94dc 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManager.java
@@ -56,7 +56,6 @@
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
@@ -191,14 +190,7 @@ public ObjectMap getConfiguration() {
 
     public Lock lockGlobal(long lockDuration, long timeout, String lockName)
             throws StorageEngineException {
-        try {
-            return projectDBAdaptor.lockProject(lockDuration, timeout, lockName);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new StorageEngineException("Unable to lock the Project", e);
-        } catch (TimeoutException e) {
-            throw new StorageEngineException("Unable to lock the Project", e);
-        }
+        return projectDBAdaptor.lockProject(lockDuration, timeout, lockName);
     }
 
     public Lock lockStudy(int studyId) throws StorageEngineException {
@@ -282,8 +274,7 @@ public <E extends Exception> StudyMetadata updateStudyMetadata(Object study, Upd
             throws StorageEngineException, E {
         int studyId = getStudyId(study);
 
-        Lock lock = lockStudy(studyId);
-        try {
+        try (Lock lock = lockStudy(studyId)) {
             StudyMetadata sm = getStudyMetadata(studyId);
 
             sm = updater.update(sm);
@@ -291,8 +282,6 @@ public <E extends Exception> StudyMetadata updateStudyMetadata(Object study, Upd
             lock.checkLocked();
             unsecureUpdateStudyMetadata(sm);
             return sm;
-        } finally {
-            lock.unlock();
         }
     }
 
@@ -557,16 +546,8 @@ public <E extends Exception> ProjectMetadata updateProjectMetadata(UpdateConsume
     public <E extends Exception> ProjectMetadata updateProjectMetadata(UpdateFunction<ProjectMetadata, E> function)
             throws StorageEngineException, E {
         Objects.requireNonNull(function);
-        Lock lock;
-        try {
-            lock = projectDBAdaptor.lockProject(lockDuration, lockTimeout);
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new StorageEngineException("Unable to lock the Project", e);
-        } catch (TimeoutException e) {
-            throw new StorageEngineException("Unable to lock the Project", e);
-        }
-        try {
+
+        try (Lock lock = projectDBAdaptor.lockProject(lockDuration, lockTimeout)) {
             ProjectMetadata projectMetadata = getProjectMetadata();
             int countersHash = (projectMetadata == null ? Collections.emptyMap() : projectMetadata.getCounters()).hashCode();
 
@@ -579,8 +560,6 @@ public <E extends Exception> ProjectMetadata updateProjectMetadata(UpdateFunctio
             lock.checkLocked();
             projectDBAdaptor.updateProjectMetadata(projectMetadata, updateCounters);
             return projectMetadata;
-        } finally {
-            lock.unlock();
         }
     }
 
@@ -594,11 +573,15 @@ public ProjectMetadata getProjectMetadata() {
 
     public ProjectMetadata getAndUpdateProjectMetadata(ObjectMap options) throws StorageEngineException {
         ProjectMetadata projectMetadata = getProjectMetadata();
+
+        checkSameSpeciesAndAssembly(options, projectMetadata);
         if (options != null && (projectMetadata == null
                 || StringUtils.isEmpty(projectMetadata.getSpecies()) && options.containsKey(SPECIES.key())
                 || StringUtils.isEmpty(projectMetadata.getAssembly()) && options.containsKey(ASSEMBLY.key()))) {
 
             projectMetadata = updateProjectMetadata(pm -> {
+                // Check again, in case it was updated by another thread
+                checkSameSpeciesAndAssembly(options, pm);
                 if (pm == null) {
                     pm = new ProjectMetadata();
                 }
@@ -619,6 +602,25 @@ public ProjectMetadata getAndUpdateProjectMetadata(ObjectMap options) throws Sto
         return projectMetadata;
     }
 
+    private static void checkSameSpeciesAndAssembly(ObjectMap options, ProjectMetadata projectMetadata) throws StorageEngineException {
+        if (options != null && projectMetadata != null) {
+            if (options.containsKey(ASSEMBLY.key())) {
+                if (StringUtils.isNotEmpty(projectMetadata.getAssembly()) && !projectMetadata.getAssembly()
+                        .equalsIgnoreCase(options.getString(ASSEMBLY.key()))) {
+                    throw new StorageEngineException("Incompatible assembly change from '" + projectMetadata.getAssembly() + "' to '"
+                            + options.getString(ASSEMBLY.key()) + "'");
+                }
+            }
+            if (options.containsKey(SPECIES.key())) {
+                if (StringUtils.isNotEmpty(projectMetadata.getSpecies()) && !projectMetadata.getSpecies()
+                        .equalsIgnoreCase(toCellBaseSpeciesName(options.getString(SPECIES.key())))) {
+                    throw new StorageEngineException("Incompatible species change from '" + projectMetadata.getSpecies() + "' to '"
+                            + options.getString(SPECIES.key()) + "'");
+                }
+            }
+        }
+    }
+
     public DataResult<VariantFileMetadata> getVariantFileMetadata(int studyId, int fileId, QueryOptions options)
             throws StorageEngineException {
         return fileDBAdaptor.getVariantFileMetadata(studyId, fileId, options);
@@ -673,16 +675,14 @@ public void unsecureUpdateFileMetadata(int studyId, FileMetadata file) {
     public <E extends Exception> FileMetadata updateFileMetadata(int studyId, int fileId, UpdateConsumer<FileMetadata, E> update)
             throws E, StorageEngineException {
         getFileName(studyId, fileId); // Check file exists
-        Lock lock = fileDBAdaptor.lock(studyId, fileId, lockDuration, lockTimeout);
-        try {
+
+        try (Lock lock = fileDBAdaptor.lock(studyId, fileId, lockDuration, lockTimeout)) {
             FileMetadata fileMetadata = getFileMetadata(studyId, fileId);
             update.update(fileMetadata);
             lock.checkLocked();
             unsecureUpdateFileMetadata(studyId, fileMetadata);
             fileIdIndexedCache.put(studyId, fileId, fileMetadata.isIndexed());
             return fileMetadata;
-        } finally {
-            lock.unlock();
         }
     }
 
@@ -863,6 +863,19 @@ public Iterator<FileMetadata> fileMetadataIterator(int studyId) {
         return fileDBAdaptor.fileIterator(studyId);
     }
 
+    public SampleMetadata getSampleMetadata(Integer studyId, Integer sampleId) {
+        return getSampleMetadata(studyId.intValue(), sampleId.intValue());
+    }
+
+    public SampleMetadata getSampleMetadata(int studyId, Integer sampleId) {
+        return getSampleMetadata(studyId, sampleId.intValue());
+    }
+
+    public SampleMetadata getSampleMetadata(int studyId, Object sample) {
+        int sampleId = getSampleIdOrFail(studyId, sample);
+        return getSampleMetadata(studyId, sampleId);
+    }
+
     public SampleMetadata getSampleMetadata(int studyId, int sampleId) {
         return sampleDBAdaptor.getSampleMetadata(studyId, sampleId, null);
     }
@@ -875,15 +888,13 @@ public void unsecureUpdateSampleMetadata(int studyId, SampleMetadata sample) {
     public <E extends Exception> SampleMetadata updateSampleMetadata(int studyId, int sampleId, UpdateConsumer<SampleMetadata, E> consumer)
             throws E, StorageEngineException {
         getSampleName(studyId, sampleId); // Check sample exists
-        Lock lock = sampleDBAdaptor.lock(studyId, sampleId, lockDuration, lockTimeout);
-        try {
+
+        try (Lock lock = sampleDBAdaptor.lock(studyId, sampleId, lockDuration, lockTimeout)) {
             SampleMetadata sample = getSampleMetadata(studyId, sampleId);
             sample = consumer.toFunction().update(sample);
             lock.checkLocked();
             unsecureUpdateSampleMetadata(studyId, sample);
             return sample;
-        } finally {
-            lock.unlock();
         }
     }
 
@@ -1054,15 +1065,12 @@ public void unsecureUpdateCohortMetadata(int studyId, CohortMetadata cohort) {
     public <E extends Exception> CohortMetadata updateCohortMetadata(int studyId, int cohortId, UpdateConsumer<CohortMetadata, E> update)
             throws E, StorageEngineException {
         getCohortName(studyId, cohortId); // Check cohort exists
-        Lock lock = cohortDBAdaptor.lock(studyId, cohortId, lockDuration, lockTimeout);
-        try {
+        try (Lock lock = cohortDBAdaptor.lock(studyId, cohortId, lockDuration, lockTimeout)) {
             CohortMetadata cohortMetadata = getCohortMetadata(studyId, cohortId);
             update.update(cohortMetadata);
             lock.checkLocked();
             unsecureUpdateCohortMetadata(studyId, cohortMetadata);
             return cohortMetadata;
-        } finally {
-            lock.unlock();
         }
     }
 
@@ -1190,13 +1198,19 @@ private CohortMetadata updateCohortSamples(int studyId, String cohortName, Colle
         for (Integer sampleId : sampleIds) {
             Integer finalCohortId = cohortId;
             if (secondaryIndexCohort) {
-                updateSampleMetadata(studyId, sampleId, sampleMetadata -> {
-                    sampleMetadata.addSecondaryIndexCohort(finalCohortId);
-                });
+                if (!getSampleMetadata(studyId, sampleId).getSecondaryIndexCohorts().contains(finalCohortId)) {
+                    // Avoid unnecessary updates
+                    updateSampleMetadata(studyId, sampleId, sampleMetadata -> {
+                        sampleMetadata.addSecondaryIndexCohort(finalCohortId);
+                    });
+                }
             } else {
-                updateSampleMetadata(studyId, sampleId, sampleMetadata -> {
-                    sampleMetadata.addCohort(finalCohortId);
-                });
+                if (!getSampleMetadata(studyId, sampleId).getCohorts().contains(finalCohortId)) {
+                    // Avoid unnecessary updates
+                    updateSampleMetadata(studyId, sampleId, sampleMetadata -> {
+                        sampleMetadata.addCohort(finalCohortId);
+                    });
+                }
             }
         }
 
@@ -1209,13 +1223,19 @@ private CohortMetadata updateCohortSamples(int studyId, String cohortName, Colle
                 Integer finalCohortId = cohortId;
                 if (!sampleIds.contains(sampleFromCohort)) {
                     if (secondaryIndexCohort) {
-                        updateSampleMetadata(studyId, sampleFromCohort, sampleMetadata -> {
-                            sampleMetadata.getSecondaryIndexCohorts().remove(finalCohortId);
-                        });
+                        if (getSampleMetadata(studyId, sampleFromCohort).getSecondaryIndexCohorts().contains(finalCohortId)) {
+                            // Avoid unnecessary updates
+                            updateSampleMetadata(studyId, sampleFromCohort, sampleMetadata -> {
+                                sampleMetadata.getSecondaryIndexCohorts().remove(finalCohortId);
+                            });
+                        }
                     } else {
-                        updateSampleMetadata(studyId, sampleFromCohort, sampleMetadata -> {
-                            sampleMetadata.getCohorts().remove(finalCohortId);
-                        });
+                        if (getSampleMetadata(studyId, sampleFromCohort).getCohorts().contains(finalCohortId)) {
+                            // Avoid unnecessary updates
+                            updateSampleMetadata(studyId, sampleFromCohort, sampleMetadata -> {
+                                sampleMetadata.getCohorts().remove(finalCohortId);
+                            });
+                        }
                     }
                 }
             }
@@ -1326,15 +1346,12 @@ public void unsecureUpdateTask(int studyId, TaskMetadata task) throws StorageEng
     public <E extends Exception> TaskMetadata updateTask(int studyId, int taskId, UpdateConsumer<TaskMetadata, E> consumer)
             throws E, StorageEngineException {
         getTask(studyId, taskId); // Check task exists
-        Lock lock = taskDBAdaptor.lock(studyId, taskId, lockDuration, lockTimeout);
-        try {
+        try (Lock lock = taskDBAdaptor.lock(studyId, taskId, lockDuration, lockTimeout)) {
             TaskMetadata task = getTask(studyId, taskId);
             consumer.update(task);
             lock.checkLocked();
             unsecureUpdateTask(studyId, task);
             return task;
-        } finally {
-            lock.unlock();
         }
     }
 
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/ProjectMetadataAdaptor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/ProjectMetadataAdaptor.java
index dc88a85d338..3045ee8f3cd 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/ProjectMetadataAdaptor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/metadata/adaptors/ProjectMetadataAdaptor.java
@@ -7,7 +7,6 @@
 import org.opencb.opencga.storage.core.metadata.models.ProjectMetadata;
 
 import java.io.IOException;
-import java.util.concurrent.TimeoutException;
 
 /**
  * Created on 02/05/18.
@@ -17,14 +16,12 @@
 public interface ProjectMetadataAdaptor extends AutoCloseable {
 
     default Lock lockProject(long lockDuration, long timeout)
-            throws InterruptedException, TimeoutException, StorageEngineException {
+            throws StorageEngineException {
         return lockProject(lockDuration, timeout, null);
     }
 
     Lock lockProject(long lockDuration, long timeout, String lockName)
-            throws InterruptedException, TimeoutException, StorageEngineException;
-
-    void unLockProject(long lockId) throws StorageEngineException;
+            throws StorageEngineException;
 
     DataResult<ProjectMetadata> getProjectMetadata();
 
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java
index dd24ee1334d..b541b2f4ae4 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageEngine.java
@@ -1348,7 +1348,7 @@ public VariantQueryExecutor getVariantQueryExecutor(Query query, QueryOptions op
     public VariantQueryExecutor getVariantQueryExecutor(ParsedVariantQuery variantQuery) {
         try {
             for (VariantQueryExecutor executor : getVariantQueryExecutors()) {
-                if (executor.canUseThisExecutor(variantQuery.getQuery(), variantQuery.getInputOptions())) {
+                if (executor.canUseThisExecutor(variantQuery, variantQuery.getInputOptions())) {
                     logger.info("Using VariantQueryExecutor : " + executor.getClass().getName());
                     logger.info("  Query : " + VariantQueryUtils.printQuery(variantQuery.getInputQuery()));
                     logger.info("  Options : " + variantQuery.getInputOptions().toJson());
@@ -1362,6 +1362,19 @@ public VariantQueryExecutor getVariantQueryExecutor(ParsedVariantQuery variantQu
         throw new VariantQueryException("No VariantQueryExecutor found to run the query!");
     }
 
+    public final VariantQueryExecutor getVariantQueryExecutor(Class<? extends VariantQueryExecutor> clazz)
+            throws StorageEngineException {
+        Optional<VariantQueryExecutor> first = getVariantQueryExecutors()
+                .stream()
+                .filter(e -> e instanceof SearchIndexVariantQueryExecutor)
+                .findFirst();
+        if (first.isPresent()) {
+            return first.get();
+        } else {
+            throw new StorageEngineException("VariantQueryExecutor " + clazz + " not found");
+        }
+    }
+
     public Query preProcessQuery(Query originalQuery, QueryOptions options) {
         try {
             return getVariantQueryParser().preProcessQuery(originalQuery, options);
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageOptions.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageOptions.java
index caefbb5260e..bc1be055466 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageOptions.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStorageOptions.java
@@ -83,8 +83,8 @@ public enum VariantStorageOptions implements ConfigurationOption {
 
     INDEX_SEARCH("indexSearch", false), // Build secondary indexes using search engine.
 
-    METADATA_LOCK_DURATION("metadata.lock.duration", 5000),
-    METADATA_LOCK_TIMEOUT("metadata.lock.timeout", 60000),
+    METADATA_LOCK_DURATION("metadata.lock.duration", 60000),
+    METADATA_LOCK_TIMEOUT("metadata.lock.timeout", 600000),
     METADATA_LOAD_BATCH_SIZE("metadata.load.batchSize", 10),
     METADATA_LOAD_THREADS("metadata.load.numThreads", 4),
 
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java
index 5b37d2512b1..722d79e59fd 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/VariantStoragePipeline.java
@@ -60,10 +60,7 @@
 import org.opencb.opencga.storage.core.io.plain.StringDataReader;
 import org.opencb.opencga.storage.core.io.plain.StringDataWriter;
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
-import org.opencb.opencga.storage.core.metadata.models.CohortMetadata;
-import org.opencb.opencga.storage.core.metadata.models.FileMetadata;
-import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
-import org.opencb.opencga.storage.core.metadata.models.TaskMetadata;
+import org.opencb.opencga.storage.core.metadata.models.*;
 import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;
 import org.opencb.opencga.storage.core.variant.io.VariantReaderUtils;
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java
index 8468ab34317..300f2e6b48a 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/ParsedVariantQuery.java
@@ -267,14 +267,6 @@ public VariantStudyQuery setStudies(ParsedQuery<String> studies) {
             return this;
         }
 
-        public String getStudyOrFail() {
-            if (studies == null || studies.size() != 1) {
-                throw new VariantQueryException("Require exactly one study");
-            } else {
-                return studies.get(0);
-            }
-        }
-
         public ParsedQuery<KeyOpValue<SampleMetadata, List<String>>> getGenotypes() {
             return genotypes;
         }
@@ -311,6 +303,19 @@ public void setDefaultStudy(StudyMetadata defaultStudy) {
         public StudyMetadata getDefaultStudy() {
             return defaultStudy;
         }
+
+        public StudyMetadata getDefaultStudyOrFail() {
+            if (defaultStudy == null) {
+                if (studies.size() != 1) {
+                    throw new VariantQueryException("Only one study is allowed. Found " + studies.size() + " studies");
+                } else {
+                    throw new VariantQueryException("One study required. None provided");
+                }
+            } else {
+                return defaultStudy;
+            }
+        }
+
     }
 
     public static class VariantQueryXref {
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java
index 641e365a51d..bd02a6b4303 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/VariantQueryParser.java
@@ -17,10 +17,7 @@
 import org.opencb.commons.datastore.core.QueryParam;
 import org.opencb.opencga.core.models.variant.VariantAnnotationConstants;
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
-import org.opencb.opencga.storage.core.metadata.models.SampleMetadata;
-import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
-import org.opencb.opencga.storage.core.metadata.models.TaskMetadata;
-import org.opencb.opencga.storage.core.metadata.models.VariantScoreMetadata;
+import org.opencb.opencga.storage.core.metadata.models.*;
 import org.opencb.opencga.storage.core.utils.CellBaseUtils;
 import org.opencb.opencga.storage.core.variant.VariantStorageOptions;
 import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass;
@@ -170,15 +167,14 @@ public ParsedVariantQuery parseQuery(Query inputQuery, QueryOptions options, boo
                 VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.key(),
                 VariantStorageOptions.APPROXIMATE_COUNT_SAMPLING_SIZE.defaultValue()));
 
-        variantQuery.setProjection(projectionParser.parseVariantQueryProjection(inputQuery, options));
-
         VariantQuery query;
         if (!skipPreProcess) {
-            query = new VariantQuery(preProcessQuery(inputQuery, options, variantQuery.getProjection()));
+            query = new VariantQuery(preProcessQuery(inputQuery, options));
         } else {
             query = new VariantQuery(inputQuery);
         }
         variantQuery.setQuery(query);
+        variantQuery.setProjection(projectionParser.parseVariantQueryProjection(query, options));
 
         List<Region> geneRegions = Region.parseRegions(query.getString(ANNOT_GENE_REGIONS.key()));
         variantQuery.setGeneRegions(geneRegions == null ? Collections.emptyList() : geneRegions);
@@ -221,10 +217,7 @@ public ParsedVariantQuery parseQuery(Query inputQuery, QueryOptions options, boo
                     = new ParsedQuery<>(sampleDataQuery.getKey(), sampleDataQuery.getOperation(), new ArrayList<>(sampleDataQuery.size()));
             for (KeyValues<String, KeyOpValue<String, String>> keyValues : sampleDataQuery) {
                 sampleDataQueryWithMetadata.getValues().add(
-                        keyValues.mapKey(sample -> {
-                            int sampleId = metadataManager.getSampleIdOrFail(defaultStudy.getId(), sample);
-                            return metadataManager.getSampleMetadata(defaultStudy.getId(), sampleId);
-                        }));
+                        keyValues.mapKey(sample -> metadataManager.getSampleMetadata(defaultStudy.getId(), sample)));
             }
             studyQuery.setSampleDataQuery(sampleDataQueryWithMetadata);
         }
@@ -232,17 +225,13 @@ public ParsedVariantQuery parseQuery(Query inputQuery, QueryOptions options, boo
         return variantQuery;
     }
 
-    public final Query preProcessQuery(Query originalQuery, QueryOptions options) {
-        return preProcessQuery(originalQuery, options, null);
-    }
-
-    protected Query preProcessQuery(Query originalQuery, QueryOptions options, VariantQueryProjection projection) {
+    public Query preProcessQuery(Query originalQuery, QueryOptions options) {
         // Copy input query! Do not modify original query!
         Query query = VariantQueryUtils.copy(originalQuery);
 
         preProcessAnnotationParams(query);
 
-        preProcessStudyParams(query, options, projection);
+        preProcessStudyParams(query, options);
 
         if (options != null && options.getLong(QueryOptions.LIMIT) < 0) {
             throw VariantQueryException.malformedParam(QueryOptions.LIMIT, options.getString(QueryOptions.LIMIT),
@@ -388,7 +377,7 @@ private VariantType parseVariantType(String type) {
         }
     }
 
-    protected void preProcessStudyParams(Query query, QueryOptions options, VariantQueryProjection projection) {
+    protected void preProcessStudyParams(Query query, QueryOptions options) {
         StudyMetadata defaultStudy = getDefaultStudy(query);
         QueryOperation formatOperator = null;
         if (isValidParam(query, SAMPLE_DATA)) {
@@ -613,10 +602,13 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
 
         if (isValidParam(query, SAMPLE_MENDELIAN_ERROR)
                 || isValidParam(query, SAMPLE_DE_NOVO)
-                || isValidParam(query, SAMPLE_DE_NOVO_STRICT)) {
+                || isValidParam(query, SAMPLE_DE_NOVO_STRICT)
+                || isValidParam(query, SAMPLE_COMPOUND_HETEROZYGOUS)) {
+            boolean requireMendelianReady = false;
             QueryParam param = null;
             if (isValidParam(query, SAMPLE_MENDELIAN_ERROR)) {
                 param = SAMPLE_MENDELIAN_ERROR;
+                requireMendelianReady = true;
             }
             if (isValidParam(query, SAMPLE_DE_NOVO)) {
                 if (param != null) {
@@ -624,6 +616,7 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
                             param, query.getString(param.key()),
                             SAMPLE_DE_NOVO, query.getString(SAMPLE_DE_NOVO.key()));
                 }
+                requireMendelianReady = true;
                 param = SAMPLE_DE_NOVO;
             }
             if (isValidParam(query, SAMPLE_DE_NOVO_STRICT)) {
@@ -632,8 +625,21 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
                             param, query.getString(param.key()),
                             SAMPLE_DE_NOVO_STRICT, query.getString(SAMPLE_DE_NOVO_STRICT.key()));
                 }
+                requireMendelianReady = true;
                 param = SAMPLE_DE_NOVO_STRICT;
             }
+            if (isValidParam(query, SAMPLE_COMPOUND_HETEROZYGOUS)) {
+                if (param != null) {
+                    throw VariantQueryException.unsupportedParamsCombination(
+                            param, query.getString(param.key()),
+                            SAMPLE_COMPOUND_HETEROZYGOUS, query.getString(SAMPLE_COMPOUND_HETEROZYGOUS.key()));
+                }
+                requireMendelianReady = false;
+                param = SAMPLE_COMPOUND_HETEROZYGOUS;
+            }
+            if (param == null) {
+                throw new IllegalStateException("Unknown param");
+            }
             if (defaultStudy == null) {
                 throw VariantQueryException.missingStudyForSamples(query.getAsStringList(param.key()),
                         metadataManager.getStudyNames());
@@ -645,15 +651,18 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
                         genotypeParam, query.getString(genotypeParam.key())
                 );
             }
-            List<String> samples = query.getAsStringList(param.key());
+            Object value = query.get(param.key());
+            List<String> samples;
+            if (value instanceof Trio) {
+                samples = Collections.singletonList(((Trio) value).getChild());
+            } else {
+                samples = query.getAsStringList(param.key());
+            }
             Set<String> samplesAndParents = new LinkedHashSet<>(samples);
             for (String sample : samples) {
-                Integer sampleId = metadataManager.getSampleId(defaultStudy.getId(), sample);
-                if (sampleId == null) {
-                    throw VariantQueryException.sampleNotFound(sample, defaultStudy.getName());
-                }
-                SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(defaultStudy.getId(), sampleId);
-                if (TaskMetadata.Status.READY != sampleMetadata.getMendelianErrorStatus()) {
+                SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(defaultStudy.getId(), sample);
+                if (requireMendelianReady
+                        && TaskMetadata.Status.READY != sampleMetadata.getMendelianErrorStatus()) {
                     throw VariantQueryException.malformedParam(param, "Sample \"" + sampleMetadata.getName()
                             + "\" does not have the Mendelian Errors precomputed yet");
                 }
@@ -674,6 +683,21 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
             } else {
                 query.put(INCLUDE_SAMPLE.key(), new ArrayList<>(samplesAndParents));
             }
+            if (param == SAMPLE_COMPOUND_HETEROZYGOUS) {
+                int studyId = defaultStudy.getId();
+                if (!(value instanceof Trio)) {
+                    if (samples.size() > 1) {
+                        throw VariantQueryException.malformedParam(SAMPLE, value.toString(),
+                                "More than one sample provided for compound heterozygous filter.");
+                    }
+                    SampleMetadata sm = metadataManager.getSampleMetadata(studyId, samples.get(0));
+                    Trio trio = new Trio(null,
+                            metadataManager.getSampleName(studyId, sm.getFather()),
+                            metadataManager.getSampleName(studyId, sm.getMother()),
+                            sm.getName());
+                    query.put(SAMPLE_COMPOUND_HETEROZYGOUS.key(), trio);
+                }
+            }
         }
 
         if (isValidParam(query, SCORE)) {
@@ -704,9 +728,7 @@ protected void preProcessStudyParams(Query query, QueryOptions options, VariantQ
                 || isValidParam(query, SAMPLE_SKIP)
                 || isValidParam(query, SAMPLE_LIMIT)
         ) {
-            if (projection == null) {
-                projection = projectionParser.parseVariantQueryProjection(query, options);
-            }
+            VariantQueryProjection projection = projectionParser.parseVariantQueryProjection(query, options);
             // Apply the sample pagination.
             // Remove the sampleLimit and sampleSkip to avoid applying the pagination twice
             query.remove(SAMPLE_SKIP.key());
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java
index 6eb237ea4b3..bc40c5b6418 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/BreakendVariantQueryExecutor.java
@@ -14,6 +14,7 @@
 import org.opencb.commons.datastore.core.QueryParam;
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;
+import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam;
 import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator;
@@ -40,7 +41,8 @@ public BreakendVariantQueryExecutor(String storageEngineId, ObjectMap options,
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException {
+        VariantQuery query = variantQuery.getQuery();
         return query.getString(VariantQueryParam.TYPE.key()).equals(VariantType.BREAKEND.name())
                 && VariantQueryUtils.isValidParam(query, VariantQueryParam.GENOTYPE);
     }
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java
index c6f4b87a5f0..bcecac57b5a 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/CompoundHeterozygousQueryExecutor.java
@@ -64,14 +64,14 @@ public CompoundHeterozygousQueryExecutor(VariantStorageMetadataManager metadataM
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException {
-        return isValidParam(query, VariantQueryUtils.SAMPLE_COMPOUND_HETEROZYGOUS);
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException {
+        return isValidParam(variantQuery.getQuery(), VariantQueryUtils.SAMPLE_COMPOUND_HETEROZYGOUS);
     }
 
     @Override
     protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) {
         Trio trio = getCompHetTrio(variantQuery.getQuery());
-        String study = variantQuery.getStudyQuery().getStudyOrFail();
+        String study = variantQuery.getStudyQuery().getDefaultStudyOrFail().getName();
         return getOrIterator(study, trio.getChild(), trio.getFather(), trio.getMother(),
                 variantQuery, iterator);
     }
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java
index 474cbc3fa9f..e667e2a1485 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/DBAdaptorVariantQueryExecutor.java
@@ -52,9 +52,9 @@ protected Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) {
         for (QueryParam unsupportedParam : UNSUPPORTED_PARAMS) {
-            if (VariantQueryUtils.isValidParam(query, unsupportedParam)) {
+            if (VariantQueryUtils.isValidParam(variantQuery.getQuery(), unsupportedParam)) {
                 logger.warn("Unsupported variant query param {} in {}",
                         unsupportedParam.key(),
                         DBAdaptorVariantQueryExecutor.class.getSimpleName());
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java
index e286b4a07ce..5f2da2c2056 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/NoOpVariantQueryExecutor.java
@@ -8,6 +8,7 @@
 import org.opencb.opencga.storage.core.metadata.models.CohortMetadata;
 import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
 import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass;
+import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam;
 import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator;
 import org.opencb.opencga.storage.core.variant.query.*;
@@ -34,7 +35,8 @@ public NoOpVariantQueryExecutor(VariantStorageMetadataManager metadataManager, S
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException {
+        VariantQuery query = variantQuery.getQuery();
         boolean sampleQuery = false;
         String sample = null;
         if (VariantQueryUtils.isValidParam(query, VariantQueryParam.GENOTYPE)) {
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java
index 26d53e89e5c..49ddc339658 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutor.java
@@ -2,7 +2,6 @@
 
 import org.opencb.biodata.models.variant.Variant;
 import org.opencb.commons.datastore.core.ObjectMap;
-import org.opencb.commons.datastore.core.Query;
 import org.opencb.commons.datastore.core.QueryOptions;
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
@@ -65,12 +64,12 @@ public static void setDefaultTimeout(QueryOptions queryOptions, ObjectMap config
 
     /**
      * Determine if this VariantQueryExecutor can run the given query.
-     * @param query    Query to execute
+     * @param variantQuery    Query to execute
      * @param options  Options for the query
      * @return         True if this variant query executor is valid for the query
      * @throws StorageEngineException if there is an error
      */
-    public abstract boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException;
+    public abstract boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException;
 
     protected abstract Object getOrIterator(ParsedVariantQuery variantQuery, boolean iterator) throws StorageEngineException;
 
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java
index bdcd501ad88..95add22ec8e 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/query/projection/VariantQueryProjectionParser.java
@@ -71,7 +71,14 @@ public VariantQueryProjection parseVariantQueryProjection(Query query, QueryOpti
         }
 
         Map<Integer, List<Integer>> sampleIdsMap = getIncludeSampleIds(query, options, includeStudies, metadataManager);
-        int numTotalSamples = sampleIdsMap.values().stream().mapToInt(List::size).sum();
+        int numTotalSamples;
+        if (isValidParam(query, NUM_TOTAL_SAMPLES)) {
+            // NUM_TOTAL_SAMPLES might have been defined in the PreProcess step.
+            // This implies that the current query has the samples already paginated.
+            numTotalSamples = query.getInt(NUM_TOTAL_SAMPLES.key());
+        } else {
+            numTotalSamples = sampleIdsMap.values().stream().mapToInt(List::size).sum();
+        }
         skipAndLimitSamples(query, sampleIdsMap);
         int numSamples = sampleIdsMap.values().stream().mapToInt(List::size).sum();
 
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java
index 200b2eb463d..cfa794cbe10 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SamplesSearchIndexVariantQueryExecutor.java
@@ -7,6 +7,7 @@
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.exceptions.VariantSearchException;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;
+import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException;
 import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery;
 import org.opencb.opencga.storage.core.variant.search.solr.VariantSearchManager;
@@ -28,7 +29,8 @@ public SamplesSearchIndexVariantQueryExecutor(VariantDBAdaptor dbAdaptor, Varian
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException {
+        VariantQuery query = variantQuery.getQuery();
         String samplesCollection = inferSpecificSearchIndexSamplesCollection(query, options, getMetadataManager(), dbName);
         return samplesCollection != null && searchActiveAndAlive(samplesCollection);
     }
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java
index 12c86cc4e2b..899f71a9b72 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/SearchIndexVariantQueryExecutor.java
@@ -11,10 +11,7 @@
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.exceptions.VariantSearchException;
 import org.opencb.opencga.storage.core.variant.VariantStorageEngine;
-import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;
-import org.opencb.opencga.storage.core.variant.adaptors.VariantField;
-import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException;
-import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam;
+import org.opencb.opencga.storage.core.variant.adaptors.*;
 import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery;
 import org.opencb.opencga.storage.core.variant.query.VariantQueryResult;
 import org.opencb.opencga.storage.core.variant.query.VariantQueryUtils;
@@ -76,7 +73,8 @@ public SearchIndexVariantQueryExecutor setIntersectParamsThreshold(int intersect
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) throws StorageEngineException {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) throws StorageEngineException {
+        VariantQuery query = variantQuery.getQuery();
         return doQuerySearchManager(query, options) || doIntersectWithSearch(query, options);
     }
 
@@ -185,7 +183,9 @@ public VariantQueryResult<Long> approximateCount(ParsedVariantQuery variantQuery
 
                 DataResult<VariantSearchModel> nativeResult = searchManager
                         .nativeQuery(dbName, searchEngineQuery, queryOptions);
-                List<String> variantIds = nativeResult.getResults().stream().map(VariantSearchModel::getId).collect(Collectors.toList());
+                List<Variant> variantIds = nativeResult.getResults().stream()
+                        .map(VariantSearchModel::toVariantSimple)
+                        .collect(Collectors.toList());
                 // Adjust numSamples if the results from SearchManager is smaller than numSamples
                 // If this happens, the count is not approximated
                 if (variantIds.size() < sampling) {
@@ -285,12 +285,12 @@ public boolean doIntersectWithSearch(Query query, QueryOptions options) {
         return intersect;
     }
 
-    protected Iterator<String> variantIdIteratorFromSearch(Query query) {
+    protected Iterator<Variant> variantIdIteratorFromSearch(Query query) {
         return variantIdIteratorFromSearch(query, Integer.MAX_VALUE, 0, null);
     }
 
-    protected Iterator<String> variantIdIteratorFromSearch(Query query, int limit, int skip, AtomicLong numTotalResults) {
-        Iterator<String> variantsIterator;
+    protected Iterator<Variant> variantIdIteratorFromSearch(Query query, int limit, int skip, AtomicLong numTotalResults) {
+        Iterator<Variant> variantsIterator;
         QueryOptions queryOptions = new QueryOptions()
                 .append(QueryOptions.LIMIT, limit)
                 .append(QueryOptions.SKIP, skip)
@@ -304,14 +304,14 @@ protected Iterator<String> variantIdIteratorFromSearch(Query query, int limit, i
                 }
                 variantsIterator = nativeResult.getResults()
                         .stream()
-                        .map(VariantSearchModel::getId)
+                        .map(VariantSearchModel::toVariantSimple)
                         .iterator();
             } else {
                 SolrNativeIterator nativeIterator = searchManager.nativeIterator(dbName, query, queryOptions);
                 if (numTotalResults != null) {
                     numTotalResults.set(nativeIterator.getNumFound());
                 }
-                variantsIterator = Iterators.transform(nativeIterator, VariantSearchModel::getId);
+                variantsIterator = Iterators.transform(nativeIterator, VariantSearchModel::toVariantSimple);
             }
         } catch (VariantSearchException | IOException e) {
             throw new VariantQueryException("Error querying " + VariantSearchManager.SEARCH_ENGINE_ID, e);
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchModel.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchModel.java
index 9b0bb69792c..835af18a0a0 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchModel.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchModel.java
@@ -17,12 +17,15 @@
 package org.opencb.opencga.storage.core.variant.search;
 
 import org.apache.solr.client.solrj.beans.Field;
+import org.opencb.biodata.models.variant.Variant;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.opencb.opencga.storage.core.variant.search.VariantSearchToVariantConverter.HASH_PREFIX;
+
 /**
  * Created by wasim on 09/11/16.
  */
@@ -140,6 +143,9 @@ public class VariantSearchModel {
     @Field("fileInfo_*")
     private Map<String, String> fileInfo;
 
+    @Field("attr_*")
+    private Map<String, Object> attr;
+
 
     public static final double MISSING_VALUE = -100.0;
 
@@ -171,6 +177,7 @@ public VariantSearchModel() {
         this.qual = new HashMap<>();
         this.filter = new HashMap<>();
         this.fileInfo = new HashMap<>();
+        this.attr = new HashMap<>();
     }
 
     public VariantSearchModel(VariantSearchModel init) {
@@ -210,6 +217,7 @@ public VariantSearchModel(VariantSearchModel init) {
         this.qual = init.getQual();
         this.filter = init.getFilter();
         this.fileInfo = init.getFileInfo();
+        this.attr = init.getAttr();
     }
 
     @Override
@@ -251,6 +259,7 @@ public String toString() {
         sb.append(", qual=").append(qual);
         sb.append(", filter=").append(filter);
         sb.append(", fileInfo=").append(fileInfo);
+        sb.append(", attr=").append(attr);
         sb.append('}');
         return sb.toString();
     }
@@ -259,6 +268,17 @@ public String getId() {
         return id;
     }
 
+    public Variant toVariantSimple() {
+        String variantId = getId();
+        if (variantId.startsWith(HASH_PREFIX)) {
+            Object o = getAttr().get("attr_id");
+            variantId = o instanceof String ? (String) o : ((List<String>) o).get(0);
+        }
+        Variant variant = new Variant(variantId);
+        variant.setId(variantId);
+        return variant;
+    }
+
     public VariantSearchModel setId(String id) {
         this.id = id;
         return this;
@@ -579,4 +599,12 @@ public VariantSearchModel setFileInfo(Map<String, String> fileInfo) {
         return this;
     }
 
+    public Map<String, Object> getAttr() {
+        return attr;
+    }
+
+    public VariantSearchModel setAttr(Map<String, Object> attr) {
+        this.attr = attr;
+        return this;
+    }
 }
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java
index 6f55a892f7c..d9c119c2340 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverter.java
@@ -56,6 +56,7 @@ public class VariantSearchToVariantConverter implements ComplexTypeConverter<Var
     public static final double MISSING_VALUE = -100.0;
     private static final String LIST_SEP = "___";
     private static final String FIELD_SEP = " -- ";
+    static final String HASH_PREFIX = "#";
 
     private final Logger logger = LoggerFactory.getLogger(VariantSearchToVariantConverter.class);
     private final Set<VariantField> includeFields;
@@ -79,10 +80,9 @@ public VariantSearchToVariantConverter(Set<VariantField> includeFields) {
     @Override
     public Variant convertToDataModelType(VariantSearchModel variantSearchModel) {
         // set chromosome, start, end, ref, alt from ID
-        Variant variant = new Variant(variantSearchModel.getId());
+        Variant variant = variantSearchModel.toVariantSimple();
 
-        // set ID, chromosome, start, end, ref, alt, type
-        variant.setId(variantSearchModel.getVariantId());
+        // set chromosome, start, end, ref, alt, type
 
         // set variant type
         if (StringUtils.isNotEmpty(variantSearchModel.getType())) {
@@ -662,8 +662,10 @@ public VariantSearchModel convertToStorageType(Variant variant) {
         List<String> other = new ArrayList<>();
 
         // Set general Variant attributes: id, dbSNP, chromosome, start, end, type
-        variantSearchModel.setId(variant.toString());   // Internal unique ID e.g.  3:1000:AT:-
-        variantSearchModel.setVariantId(variant.getId());
+        String variantId = getVariantId(variant);
+        variantSearchModel.setId(variantId);   // Internal unique ID e.g.  3:1000:AT:-
+        variantSearchModel.setVariantId(variantId);
+        variantSearchModel.getAttr().put("attr_id", variant.toString());
         variantSearchModel.setChromosome(variant.getChromosome());
         variantSearchModel.setStart(variant.getStart());
         variantSearchModel.setEnd(variant.getEnd());
@@ -1024,8 +1026,7 @@ public VariantSearchModel convertToStorageType(Variant variant) {
         // This field contains all possible IDs: id, dbSNP, names, genes, transcripts, protein, clinvar, hpo, ...
         // This will help when searching by variant id. This is added at the end of the method after collecting all IDs
         Set<String> xrefs = variantAnnotationModelUtils.extractXRefs(variant.getAnnotation());
-        xrefs.add(variantSearchModel.getId());
-        xrefs.add(variantSearchModel.getVariantId());
+        xrefs.add(variantId);
         if (variant.getNames() != null && !variant.getNames().isEmpty()) {
             variant.getNames().forEach(name -> {
                 if (name != null) {
@@ -1037,6 +1038,20 @@ public VariantSearchModel convertToStorageType(Variant variant) {
         return variantSearchModel;
     }
 
+    public static String getVariantId(Variant variant) {
+        String variantString = variant.toString();
+        if (variantString.length() > 32766) {
+            // variantString.length() >= Short.MAX_VALUE
+            return hashVariantId(variant, variantString);
+        } else {
+            return variantString;
+        }
+    }
+
+    public static String hashVariantId(Variant variant, String variantString) {
+        return HASH_PREFIX + variant.getChromosome() + ":" + variant.getStart() + ":" + Integer.toString(variantString.hashCode());
+    }
+
     private void convertStudies(Variant variant, VariantSearchModel variantSearchModel, List<String> other) {
         // Sanity check
         if (CollectionUtils.isEmpty(variant.getStudies())) {
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchUtils.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchUtils.java
index e46c6379dc6..5365643b1fa 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchUtils.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/VariantSearchUtils.java
@@ -280,7 +280,9 @@ public static String inferSpecificSearchIndexSamplesCollection(
                 Set<String> samples = new HashSet<>();
                 if (isValidParam(query, VariantQueryParam.SAMPLE)) {
                     String value = query.getString(VariantQueryParam.SAMPLE.key());
-                    samples.addAll(splitValue(value).getValue());
+                    for (String sample : splitValue(value).getValue()) {
+                        samples.add(sample.split(IS)[0]);
+                    }
                 }
                 if (isValidParam(query, VariantQueryParam.GENOTYPE)) {
                     HashMap<Object, List<String>> map = new HashMap<>();
diff --git a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParser.java b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParser.java
index 461b6e498ba..f12bb85a8b1 100644
--- a/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParser.java
+++ b/opencga-storage/opencga-storage-core/src/main/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParser.java
@@ -25,7 +25,6 @@
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.common.SolrException;
 import org.opencb.biodata.models.core.Region;
-import org.opencb.biodata.models.variant.Variant;
 import org.opencb.commons.datastore.core.Query;
 import org.opencb.commons.datastore.core.QueryOptions;
 import org.opencb.commons.datastore.solr.FacetQueryParser;
@@ -35,7 +34,10 @@
 import org.opencb.opencga.storage.core.variant.adaptors.VariantField;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryException;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam;
-import org.opencb.opencga.storage.core.variant.query.*;
+import org.opencb.opencga.storage.core.variant.query.KeyOpValue;
+import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery;
+import org.opencb.opencga.storage.core.variant.query.Values;
+import org.opencb.opencga.storage.core.variant.query.VariantQueryParser;
 import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjectionParser;
 import org.opencb.opencga.storage.core.variant.search.VariantSearchToVariantConverter;
 import org.slf4j.Logger;
@@ -79,7 +81,7 @@ public class SolrQueryParser {
     static {
         includeMap = new HashMap<>();
 
-        includeMap.put("id", "id,variantId");
+        includeMap.put("id", "id,variantId,attr_id");
         includeMap.put("chromosome", "chromosome");
         includeMap.put("start", "start");
         includeMap.put("end", "end");
@@ -477,7 +479,9 @@ private String parseGenomicFilter(Query query) {
         genes.addAll(variantQueryXref.getGenes());
         xrefs.addAll(variantQueryXref.getIds());
         xrefs.addAll(variantQueryXref.getOtherXrefs());
-        xrefs.addAll(variantQueryXref.getVariants().stream().map(Variant::toString).collect(Collectors.toList()));
+        xrefs.addAll(variantQueryXref.getVariants().stream()
+                .map(VariantSearchToVariantConverter::getVariantId)
+                .collect(Collectors.toList()));
 
         // Regions
         if (StringUtils.isNotEmpty(query.getString(REGION.key()))) {
@@ -1616,15 +1620,12 @@ private String[] includeFieldsWithMandatory(String[] includes) {
             return new String[0];
         }
 
-        String[] mandatoryIncludeFields  = new String[]{"id", "chromosome", "start", "end", "type"};
-        String[] includeWithMandatory = new String[includes.length + mandatoryIncludeFields.length];
-        for (int i = 0; i < includes.length; i++) {
-            includeWithMandatory[i] = includes[i];
-        }
-        for (int i = 0; i < mandatoryIncludeFields.length; i++) {
-            includeWithMandatory[includes.length + i] = mandatoryIncludeFields[i];
-        }
-        return includeWithMandatory;
+        Set<String> mandatoryIncludeFields = new HashSet<>(Arrays.asList("id", "attr_id", "chromosome", "start", "end", "type"));
+        Set<String> includeWithMandatory = new LinkedHashSet<>(includes.length + mandatoryIncludeFields.size());
+
+        includeWithMandatory.addAll(Arrays.asList(includes));
+        includeWithMandatory.addAll(mandatoryIncludeFields);
+        return includeWithMandatory.toArray(new String[0]);
     }
 
     /**
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManager.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManager.java
index 6fc7609b998..b1f827b2baf 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManager.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManager.java
@@ -24,7 +24,6 @@
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.filter.CompareFilter;
 import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.solr.common.StringUtils;
 import org.opencb.opencga.core.common.TimeUtils;
 import org.opencb.opencga.storage.core.metadata.models.Lock;
 import org.slf4j.Logger;
@@ -134,18 +133,17 @@ public Lock lock(byte[] row, byte[] column, long lockDuration, long timeout)
         // Minimum lock duration of 100ms
         lockDuration = Math.max(lockDuration, 100);
 
-        byte[] lockValue;
-        String readToken = "";
+        LockToken lockToken;
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
 
         do {
-            lockValue = readLockValue(row, column);
+            lockToken = readLockToken(row, column);
 
             // If the lock is taken, wait
-            while (isLockTaken(lockValue)) {
+            while (lockToken.isTaken()) {
                 Thread.sleep(100);
-                lockValue = readLockValue(row, column);
+                lockToken = readLockToken(row, column);
                 //Check if the lock is still valid
                 if (stopWatch.getTime() > timeout) {
                     throw new TimeoutException("Unable to get the lock");
@@ -157,19 +155,19 @@ public Lock lock(byte[] row, byte[] column, long lockDuration, long timeout)
             }
 
             // Try to lock cell
-            if (tryToPutToken(token, lockDuration, row, column, lockValue, CURRENT)) {
-                readToken = parseValidLockToken(readLockValue(row, column));
+            if (tryToPutToken(token, lockDuration, row, column, lockToken, CURRENT)) {
+                lockToken = readLockToken(row, column);
             }
 
-            // You win the lock if the first available lock is yours.
-        } while (!token.equals(readToken));
+            // You win the lock if you manage to write your lock.
+        } while (!lockToken.equals(token));
 
-        boolean prevTokenExpired = lockValue != null && lockValue.length > 0;
+        boolean prevTokenExpired = !lockToken.isEmpty() && lockToken.isExpired();
         boolean slowQuery = stopWatch.getTime() > 60000;
         if (prevTokenExpired || slowQuery) {
             StringBuilder msg = new StringBuilder("Lock column '").append(Bytes.toStringBinary(column)).append("'");
             if (prevTokenExpired) {
-                long expireDate = parseExpireDate(lockValue);
+                long expireDate = lockToken.getExpireDate();
                 msg.append(". Previous token expired ")
                         .append(TimeUtils.durationToString(System.currentTimeMillis() - expireDate))
                         .append(" ago");
@@ -181,105 +179,65 @@ public Lock lock(byte[] row, byte[] column, long lockDuration, long timeout)
             logger.warn(msg.toString());
         }
 
-        long tokenHash = token.hashCode();
-        logger.debug("Won the lock with token " + token + " (" + tokenHash + ")");
-
-        long finalLockDuration = lockDuration;
-        return new Lock(threadPool, (int) (finalLockDuration / 4), tokenHash) {
-            @Override
-            public void unlock0() {
-                try {
-                    HBaseLockManager.this.unlock(row, column, tokenHash);
-                } catch (IOException e) {
-                    throw new UncheckedIOException(e);
-                }
-            }
+        logger.debug("Won the lock with token " + token + " (" + token.hashCode() + ")");
 
-            @Override
-            public synchronized void refresh() throws IOException {
-                HBaseLockManager.this.refresh(row, column, tokenHash, finalLockDuration);
-            }
-        };
+        return new HBaseLock(lockDuration, token, row, column);
     }
 
-    /**
-     * Refreshes the lock.
-     *
-     * @param column        Column to find the lock cell
-     * @param lockToken     Lock token
-     * @param lockDuration  Duration un milliseconds of the token. After this time the token is expired.
-     * @throws IOException      if there is an error writing or reading from HBase.
-     */
-    public void refresh(byte[] column, long lockToken, int lockDuration) throws IOException {
-        refresh(defaultRow, column, lockToken, lockDuration);
-    }
-
-
     /**
      * Refreshes the lock.
      *
      * @param row           Row to find the lock cell
      * @param column        Column to find the lock cell
-     * @param lockToken     Lock token
+     * @param lockTokenHash Lock token
      * @param lockDuration  Duration un milliseconds of the token. After this time the token is expired.
      * @throws IOException      if there is an error writing or reading from HBase.
      */
-    public void refresh(byte[] row, byte[] column, long lockToken, long lockDuration) throws IOException {
+    private void refresh(byte[] row, byte[] column, long lockTokenHash, long lockDuration) throws IOException {
         // Check token is valid
-        byte[] lockValue = readLockValue(row, column);
-        String currentLockToken = parseValidLockToken(lockValue);
-        if (currentLockToken == null || currentLockToken.hashCode() != lockToken) {
-            throw IllegalLockStatusException.inconsistentLock(row, column, lockToken, currentLockToken, lockValue);
+        LockToken currentLockToken = readLockToken(row, column);
+        if (currentLockToken.isEmpty() || currentLockToken.isExpired() || !currentLockToken.equals(lockTokenHash)) {
+            throw IllegalLockStatusException.inconsistentLock(row, column, lockTokenHash, currentLockToken);
+        }
+        if (currentLockToken.getRemainingTime() < lockDuration / 2) {
+            logger.warn("Refreshing lock with less than half of the duration remaining. Expected duration: {} Remaining time: {}ms",
+                    lockDuration,
+                    currentLockToken.getRemainingTime());
         }
 
-        if (!tryToPutToken(currentLockToken, lockDuration, row, column, lockValue, REFRESH)) {
+        if (!tryToPutToken(currentLockToken.token, lockDuration, row, column, currentLockToken, REFRESH)) {
             // Error refreshing!
-            lockValue = readLockValue(row, column);
-            String newLockToken = parseValidLockToken(lockValue);
+            LockToken newLockToken = readLockToken(row, column);
 
-            logger.error("Current lock token:" + currentLockToken);
-            logger.error("New lock token: " + newLockToken);
-            throw IllegalLockStatusException.inconsistentLock(row, column, lockToken, currentLockToken, lockValue);
+            logger.error("Current lock token:" + currentLockToken.token);
+            logger.error("New lock token: " + newLockToken.token);
+            throw IllegalLockStatusException.inconsistentLock(row, column, lockTokenHash, currentLockToken);
         }
     }
 
-    /**
-     * Releases the lock.
-     *
-     * @param column    Column to find the lock cell
-     * @param lockToken Lock token
-     * @throws IOException                if there is an error writing or reading from HBase.
-     * @throws IllegalLockStatusException if the lockToken does not match with the current lockToken
-     */
-    public void unlock(byte[] column, long lockToken) throws IOException, IllegalLockStatusException {
-        unlock(defaultRow, column, lockToken);
-    }
-
     /**
      * Releases the lock.
      *
      * @param row       Row to find the lock cell
      * @param column    Column to find the lock cell
-     * @param lockToken Lock token
+     * @param lockTokenHash Lock token
      * @throws IOException                if there is an error writing or reading from HBase.
      * @throws IllegalLockStatusException if the lockToken does not match with the current lockToken
      */
-    public void unlock(byte[] row, byte[] column, long lockToken) throws IOException, IllegalLockStatusException {
-        byte[] lockValue = readLockValue(row, column);
+    private void unlock(byte[] row, byte[] column, long lockTokenHash) throws IOException, IllegalLockStatusException {
+        LockToken currentToken = readLockToken(row, column);
 
-        String currentToken = parseValidLockToken(lockValue);
-
-        if (currentToken == null || currentToken.hashCode() != lockToken) {
-            throw IllegalLockStatusException.inconsistentLock(row, column, lockToken, currentToken, lockValue);
+        if (currentToken.isEmpty() || currentToken.isExpired() || !currentToken.equals(lockTokenHash)) {
+            throw IllegalLockStatusException.inconsistentLock(row, column, lockTokenHash, currentToken);
         }
 
-        logger.debug("Unlock lock with token " + lockToken);
-        if (!clearLock(row, column, lockValue)) {
-            throw IllegalLockStatusException.inconsistentLock(row, column, lockToken, currentToken, lockValue);
+        logger.debug("Unlock lock with token " + lockTokenHash);
+        if (!clearLock(row, column, currentToken)) {
+            throw IllegalLockStatusException.inconsistentLock(row, column, lockTokenHash, currentToken);
         }
     }
 
-    private Boolean tryToPutToken(String token, long lockDuration, byte[] row, byte[] qualifier, byte[] lockValue, String type)
+    private Boolean tryToPutToken(String token, long lockDuration, byte[] row, byte[] qualifier, LockToken currentLock, String type)
             throws IOException {
         return hbaseManager.act(tableName, table -> {
             Put put = new Put(row)
@@ -288,30 +246,31 @@ private Boolean tryToPutToken(String token, long lockDuration, byte[] row, byte[
                                     + token
                                     + LOCK_EXPIRING_DATE_SEPARATOR_STR
                                     + (System.currentTimeMillis() + lockDuration)));
-            return table.checkAndPut(row, columnFamily, qualifier, CompareFilter.CompareOp.EQUAL, lockValue, put);
+            return table.checkAndPut(row, columnFamily, qualifier, CompareFilter.CompareOp.EQUAL, currentLock.lockValue, put);
         });
     }
 
-    private boolean clearLock(byte[] row, byte[] qualifier, byte[] lockValue) throws IOException {
+    private boolean clearLock(byte[] row, byte[] qualifier, LockToken lockToken) throws IOException {
         return hbaseManager.act(tableName, table -> {
             Put put = new Put(row)
                     .addColumn(columnFamily, qualifier, Bytes.toBytes(""));
-            return table.checkAndPut(row, columnFamily, qualifier, CompareFilter.CompareOp.EQUAL, lockValue, put);
+            return table.checkAndPut(row, columnFamily, qualifier, CompareFilter.CompareOp.EQUAL, lockToken.lockValue, put);
         });
     }
 
     /**
-     * Parse non-expired lock token.
+     * Parse lock token.
      * @param lockValue lock values
-     * @return Current lock token, if any
+     * @return Current lock token.
      */
-    protected static String parseValidLockToken(byte[] lockValue) {
+    protected static LockToken parseLockToken(byte[] lockValue) {
         if (lockValue == null || lockValue.length == 0) {
-            return null;
+            return new LockToken();
         }
 
         int idx1 = Bytes.indexOf(lockValue, LOCK_PREFIX_SEPARATOR_BYTE);
         int idx2 = Bytes.indexOf(lockValue, LOCK_EXPIRING_DATE_SEPARATOR_BYTE);
+        String type = Bytes.toString(lockValue, 0, idx1);
         String token = Bytes.toString(lockValue, idx1 + 1, idx2 - idx1 - 1);
         long expireDate;
         try {
@@ -319,45 +278,82 @@ protected static String parseValidLockToken(byte[] lockValue) {
         } catch (NumberFormatException e) {
             // Deprecated token. Assume expired token
             if (Bytes.contains(lockValue, DEPRECATED_LOCK_SEPARATOR_BYTE)) {
-                return null;
+                return new LockToken();
             }
             throw e;
         }
+        return new LockToken(lockValue, type, token, expireDate);
+    }
+
+    protected static final class LockToken {
+        protected final byte[] lockValue;
+        protected final String type;
+        protected final String token;
+        protected final Long expireDate;
+
+        private LockToken() {
+            this.lockValue = new byte[0];
+            this.type = null;
+            this.token = null;
+            this.expireDate = null;
+        }
+
+        private LockToken(byte[] lockValue, String type, String token, long expireDate) {
+            this.lockValue = lockValue;
+            this.type = type;
+            this.token = token;
+            this.expireDate = expireDate;
+        }
+
+        /**
+         * A lock is taken if there is any lockValue, and
+         * the token has not expired.
+         *
+         * @return if the lock is taken
+         */
+        public boolean isTaken() {
+            return token != null && !isExpired();
+        }
+
+        public boolean isExpired() {
+            return expireDate != null && expireDate < System.currentTimeMillis();
+        }
 
-        if (isExpired(expireDate)) {
-            return null;
-        } else {
+        public boolean isEmpty() {
+            return token == null;
+        }
+
+        public boolean equals(String token) {
+            return !isEmpty() && this.token.equals(token);
+        }
+
+        public boolean equals(long tokenHash) {
+            return !isEmpty() && this.token.hashCode() == tokenHash;
+        }
+
+        public byte[] getLockValue() {
+            return lockValue;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public String getToken() {
             return token;
         }
-    }
 
-    protected static long parseExpireDate(byte[] lockValue) {
-        int idx2 = Bytes.indexOf(lockValue, LOCK_EXPIRING_DATE_SEPARATOR_BYTE);
-        try {
-            return Long.parseLong(Bytes.toString(lockValue, idx2 + 1));
-        } catch (NumberFormatException e) {
-            // Deprecated token. Assume expired token
-            if (Bytes.contains(lockValue, DEPRECATED_LOCK_SEPARATOR_BYTE)) {
-                return -1;
-            }
-            throw e;
+        public Long getExpireDate() {
+            return expireDate;
         }
-    }
 
-    /**
-     * A lock is taken if there is any lockValue in the array, and
-     * the token has not expired.
-     *
-     *
-     * @param lockValue lock values
-     * @return if the lock is taken
-     */
-    protected static boolean isLockTaken(byte[] lockValue) {
-        return parseValidLockToken(lockValue) != null;
+        public long getRemainingTime() {
+            return expireDate == null ? 0 : expireDate - System.currentTimeMillis();
+        }
     }
 
-    private static boolean isExpired(long expireDate) {
-        return expireDate < System.currentTimeMillis();
+    private LockToken readLockToken(byte[] row, byte[] qualifier) throws IOException {
+        return parseLockToken(readLockValue(row, qualifier));
     }
 
     private byte[] readLockValue(byte[] row, byte[] qualifier) throws IOException {
@@ -380,18 +376,22 @@ public IllegalLockStatusException(String s) {
             super(s);
         }
 
-        public static IllegalLockStatusException inconsistentLock(byte[] row, byte[] column, long lockToken, String currentLock,
-                                                                  byte[] lockValue) {
-            if (StringUtils.isEmpty(currentLock)) {
-                return new IllegalLockStatusException("Inconsistent lock status. You don't have the lock! "
+        private static IllegalLockStatusException inconsistentLock(byte[] row, byte[] column, long lockTokenHash, LockToken currentLock) {
+            if (currentLock.isEmpty()) {
+                return new IllegalLockStatusException("Inconsistent lock status. You don't have the lock! Empty lock. "
+                        + "Row: '" + Bytes.toStringBinary(row) + "', "
+                        + "column: '" + Bytes.toStringBinary(column) + "'. "
+                        + "Lock: " + Bytes.toString(currentLock.lockValue) + ".");
+            } else if (currentLock.isExpired()) {
+                return new IllegalLockStatusException("Inconsistent lock status. You don't have the lock! Expired lock. "
                         + "Row: '" + Bytes.toStringBinary(row) + "', "
                         + "column: '" + Bytes.toStringBinary(column) + "'. "
-                        + "Lock: " + Bytes.toString(lockValue) + ".");
+                        + "Lock: " + Bytes.toString(currentLock.lockValue) + ".");
             } else {
-                return new IllegalLockStatusException("Inconsistent lock status. You don't have the lock! "
+                return new IllegalLockStatusException("Inconsistent lock status. You don't have the lock! Lock is taken. "
                         + "Row: '" + Bytes.toStringBinary(row) + "', "
                         + "column: '" + Bytes.toStringBinary(column) + "'. "
-                        + lockToken + " != " + currentLock.hashCode() + " from " + Bytes.toString(lockValue));
+                        + lockTokenHash + " != " + currentLock.token.hashCode() + " from " + Bytes.toString(currentLock.lockValue));
             }
         }
     }
@@ -403,4 +403,38 @@ protected static ExecutorService buildThreadPool() {
                         .build());
     }
 
+    private final class HBaseLock extends Lock {
+        private final long lockDuration;
+        private final String token;
+        private final long tokenHash;
+        private final byte[] row;
+        private final byte[] column;
+
+        private HBaseLock(long lockDuration, String token, byte[] row, byte[] column) {
+            super(HBaseLockManager.threadPool, (int) (lockDuration / 4), token.hashCode());
+            this.lockDuration = lockDuration;
+            this.token = token;
+            this.tokenHash = token.hashCode();
+            this.row = row;
+            this.column = column;
+        }
+
+        @Override
+        public void unlock0() {
+            try {
+                synchronized (this) {
+                    HBaseLockManager.this.unlock(row, column, tokenHash);
+                }
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        @Override
+        public void refresh() throws IOException {
+            synchronized (this) {
+                HBaseLockManager.this.refresh(row, column, tokenHash, lockDuration);
+            }
+        }
+    }
 }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java
index 6aa472ee735..45682df31f4 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantQueryParser.java
@@ -5,7 +5,6 @@
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
 import org.opencb.opencga.storage.core.utils.CellBaseUtils;
 import org.opencb.opencga.storage.core.variant.query.VariantQueryParser;
-import org.opencb.opencga.storage.core.variant.query.projection.VariantQueryProjection;
 
 import java.util.List;
 
@@ -18,8 +17,8 @@ public HadoopVariantQueryParser(CellBaseUtils cellBaseUtils, VariantStorageMetad
     }
 
     @Override
-    protected Query preProcessQuery(Query originalQuery, QueryOptions options, VariantQueryProjection projection) {
-        Query query = super.preProcessQuery(originalQuery, options, projection);
+    public Query preProcessQuery(Query originalQuery, QueryOptions options) {
+        Query query = super.preProcessQuery(originalQuery, options);
         List<String> studyNames = metadataManager.getStudyNames();
 
         if (isValidParam(query, STUDY) && studyNames.size() == 1) {
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java
index c672e94fb58..ceb52e5eeaa 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HBaseColumnIntersectVariantQueryExecutor.java
@@ -7,6 +7,7 @@
 import org.opencb.commons.datastore.core.QueryOptions;
 import org.opencb.opencga.storage.core.variant.VariantStorageOptions;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantDBAdaptor;
+import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery;
 import org.opencb.opencga.storage.core.variant.query.ParsedQuery;
 import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery;
 import org.opencb.opencga.storage.core.variant.query.VariantQueryResult;
@@ -43,8 +44,8 @@ public HBaseColumnIntersectVariantQueryExecutor(VariantDBAdaptor dbAdaptor, Stri
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) {
-
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) {
+        VariantQuery query = variantQuery.getQuery();
         if (!options.getBoolean(HBASE_COLUMN_INTERSECT, ACTIVE_BY_DEFAULT)) {
             // HBase column intersect not active
             return false;
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java
index 4dd50c9eacf..5063ca1fe4b 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexMendelianErrorQueryExecutor.java
@@ -9,7 +9,6 @@
 import org.opencb.biodata.tools.pedigree.MendelianError;
 import org.opencb.commons.datastore.core.DataResult;
 import org.opencb.commons.datastore.core.ObjectMap;
-import org.opencb.commons.datastore.core.Query;
 import org.opencb.commons.datastore.core.QueryOptions;
 import org.opencb.opencga.storage.core.metadata.models.SampleMetadata;
 import org.opencb.opencga.storage.core.metadata.models.Trio;
@@ -33,10 +32,10 @@ public SampleIndexMendelianErrorQueryExecutor(VariantHadoopDBAdaptor dbAdaptor,
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) {
-        if (VariantQueryUtils.isValidParam(query, VariantQueryUtils.SAMPLE_MENDELIAN_ERROR)
-                || VariantQueryUtils.isValidParam(query, VariantQueryUtils.SAMPLE_DE_NOVO)
-                || VariantQueryUtils.isValidParam(query, VariantQueryUtils.SAMPLE_DE_NOVO_STRICT)) {
+    public boolean canUseThisExecutor(ParsedVariantQuery query, QueryOptions options) {
+        if (VariantQueryUtils.isValidParam(query.getQuery(), VariantQueryUtils.SAMPLE_MENDELIAN_ERROR)
+                || VariantQueryUtils.isValidParam(query.getQuery(), VariantQueryUtils.SAMPLE_DE_NOVO)
+                || VariantQueryUtils.isValidParam(query.getQuery(), VariantQueryUtils.SAMPLE_DE_NOVO_STRICT)) {
             return super.canUseThisExecutor(query, options);
         } else {
             return false;
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java
index 371d57ac4da..7b1cdc5dceb 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexOnlyVariantQueryExecutor.java
@@ -86,7 +86,8 @@ public SampleIndexOnlyVariantQueryExecutor(VariantHadoopDBAdaptor dbAdaptor, Sam
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) {
+    public boolean canUseThisExecutor(ParsedVariantQuery variantQuery, QueryOptions options) {
+        VariantQuery query = variantQuery.getQuery();
         if (SampleIndexQueryParser.validSampleIndexQuery(query)) {
 
             if (isFullyCoveredQuery(query, options)) {
@@ -182,7 +183,8 @@ private boolean isFullyCoveredQuery(Query inputQuery, QueryOptions options) {
 //        ParsedVariantQuery parsedVariantQuery = variantQueryProjectionParser.parseQuery(query, options, true);
         SampleIndexQuery sampleIndexQuery = sampleIndexDBAdaptor.parseSampleIndexQuery(query);
 
-        return isQueryCovered(query) && isIncludeCovered(sampleIndexQuery, inputQuery, options);
+        return isQueryCovered(sampleIndexQuery.getUncoveredQuery())
+                && isIncludeCovered(sampleIndexQuery, inputQuery, options);
     }
 
     private boolean isQueryCovered(Query query) {
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java
index 6175128018d..03e8a5fe880 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/SampleIndexVariantQueryExecutor.java
@@ -58,9 +58,9 @@ public SampleIndexVariantQueryExecutor(VariantHadoopDBAdaptor dbAdaptor, SampleI
     }
 
     @Override
-    public boolean canUseThisExecutor(Query query, QueryOptions options) {
+    public boolean canUseThisExecutor(ParsedVariantQuery query, QueryOptions options) {
         if (options.getBoolean(SAMPLE_INDEX_INTERSECT, true)) {
-            return SampleIndexQueryParser.validSampleIndexQuery(query);
+            return SampleIndexQueryParser.validSampleIndexQuery(query.getQuery());
         }
         return false;
     }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java
index af632d69436..ad09896e627 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexDriver.java
@@ -112,10 +112,9 @@ protected void parseAndValidateParameters() throws IOException {
                 } else {
                     trioList.add(metadataManager.getSampleIdOrFail(getStudyId(), trio.getMother()));
                 }
-                int childId = metadataManager.getSampleIdOrFail(getStudyId(), trio.getChild());
-                trioList.add(childId);
 
-                SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(getStudyId(), childId);
+                SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(getStudyId(), trio.getChild());
+                trioList.add(sampleMetadata.getId());
                 if (!overwrite && sampleMetadata.getFamilyIndexStatus(sampleIndexVersion) == TaskMetadata.Status.READY) {
                     LOGGER.info("Skip sample " + sampleMetadata.getName() + ". Already precomputed!");
                 } else {
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java
index 027d241282c..d4605b918f4 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParser.java
@@ -1286,6 +1286,7 @@ protected SampleAnnotationIndexQuery parseAnnotationIndexQuery(SampleIndexSchema
                     intergenic == Boolean.FALSE && (!ctFilterCoveredBySummary
                                     || (!ctBtCombinationCoveredBySummary && combination.isBiotype())
                                     || combination.isFlag());
+
             if (useCtIndexFilter) {
                 ctCovered = completeIndex;
                 consequenceTypeFilter = schema.getCtIndex().getField().buildFilter(new OpValue<>("=", soNames));
@@ -1515,6 +1516,8 @@ protected SampleAnnotationIndexQuery parseAnnotationIndexQuery(SampleIndexSchema
 
         if (intergenic == null || intergenic) {
             // If intergenic is undefined, or true, CT and BT filters can not be used.
+            biotypeFilter = schema.getBiotypeIndex().getField().noOpFilter();
+            consequenceTypeFilter = schema.getCtIndex().getField().noOpFilter();
             if (!biotypeFilter.isNoOp()) {
                 throw new IllegalStateException("Unexpected BT filter for intergenic=" + intergenic);
             }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexSchemaFactory.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexSchemaFactory.java
index ef2aecb803b..a087420f425 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexSchemaFactory.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexSchemaFactory.java
@@ -78,8 +78,7 @@ public Collection<Integer> getSampleIndexConfigurationVersions(int studyId, Coll
 
     private Collection<Integer> getSampleIndexConfigurationVersions(int studyId, Object sample, boolean withAnnotation,
                                                                    boolean withFamilyIndex) {
-        int sampleId = metadataManager.getSampleIdOrFail(studyId, sample);
-        SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(studyId, sampleId);
+        SampleMetadata sampleMetadata = metadataManager.getSampleMetadata(studyId, sample);
         Collection<Integer> versions = sampleMetadata.getSampleIndexVersions();
         if (withAnnotation) {
             versions = CollectionUtils.intersection(
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/AbstractHBaseDBAdaptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/AbstractHBaseDBAdaptor.java
index 3f6a23c5abf..fbdfdf920ec 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/AbstractHBaseDBAdaptor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/AbstractHBaseDBAdaptor.java
@@ -279,14 +279,5 @@ protected Lock lockToken(byte[] rowKey, byte[] lockName, long lockDuration, long
         }
     }
 
-    protected void unLock(byte[] rowKey, byte[] lockName, long token) {
-        try {
-            this.lock.unlock(rowKey, lockName, token);
-        } catch (IOException e) {
-            throw new UncheckedIOException(e);
-        }
-    }
-
-
 
 }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseProjectMetadataDBAdaptor.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseProjectMetadataDBAdaptor.java
index 85a1d0cec0c..6ddae38be65 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseProjectMetadataDBAdaptor.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/metadata/HBaseProjectMetadataDBAdaptor.java
@@ -49,20 +49,14 @@ public HBaseProjectMetadataDBAdaptor(HBaseManager hBaseManager, String metaTable
 
     @Override
     public Lock lockProject(long lockDuration, long timeout, String lockName)
-            throws InterruptedException, TimeoutException, StorageEngineException {
+            throws StorageEngineException {
         try {
             ensureTableExists();
             return lock.lock(getProjectRowKey(), getLockColumn(lockName), lockDuration, timeout);
-        } catch (IOException e) {
-            throw new StorageEngineException("Error locking project in HBase", e);
-        }
-    }
-
-    @Override
-    public void unLockProject(long lockId) throws StorageEngineException {
-        try {
-            lock.unlock(getProjectRowKey(), getLockColumn(), lockId);
-        } catch (IOException e) {
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new StorageEngineException("Unable to lock the Project", e);
+        } catch (IOException | TimeoutException e) {
             throw new StorageEngineException("Error locking project in HBase", e);
         }
     }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/HadoopVariantSearchDataWriter.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/HadoopVariantSearchDataWriter.java
index 17c61739496..39c63923c02 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/HadoopVariantSearchDataWriter.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/main/java/org/opencb/opencga/storage/hadoop/variant/search/HadoopVariantSearchDataWriter.java
@@ -68,7 +68,7 @@ protected void add(List<SolrInputDocument> batch) throws Exception {
                     return PhoenixHelper.toBytes(studyIds, PIntegerArray.INSTANCE);
                 });
 
-                byte[] row = VariantPhoenixKeyFactory.generateVariantRowKey(new Variant(document.getFieldValue("id").toString()));
+                byte[] row = VariantPhoenixKeyFactory.generateVariantRowKey(new Variant(document.getFieldValue("attr_id").toString()));
                 variantRows.add(row);
                 mutations.add(new Put(row)
                         .addColumn(family, VariantPhoenixSchema.VariantColumn.INDEX_STUDIES.bytes(), bytes));
diff --git a/pom.xml b/pom.xml
index 9dc0893e133..bb88bd97b17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,13 +43,13 @@
     </modules>
 
     <properties>
-
         <pyopencga.version>3.2.1_dev</pyopencga.version>
         <opencgar.version>3.2.1_dev</opencgar.version>
         <cellbase.version>6.1.1-SNAPSHOT</cellbase.version>
         <biodata.version>3.2.1-SNAPSHOT</biodata.version>
         <java-common-libs.version>5.2.1-SNAPSHOT</java-common-libs.version>
         <iva.version>3.2.1-SNAPSHOT</iva.version>
+
         <oskar.version>0.2.0</oskar.version>
 
         <jackson.version>2.14.3</jackson.version>


Test patch:
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantMetadataConverterTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantMetadataConverterTest.java
index 2aacde2e3ce..30e4bba2e5f 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantMetadataConverterTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantMetadataConverterTest.java
@@ -9,6 +9,7 @@
 import org.junit.experimental.categories.Category;
 import org.opencb.biodata.models.variant.VariantFileMetadata;
 import org.opencb.biodata.models.variant.metadata.VariantMetadata;
+import org.opencb.commons.datastore.core.ObjectMap;
 import org.opencb.opencga.core.testclassification.duration.ShortTests;
 import org.opencb.opencga.storage.core.io.managers.IOConnectorProvider;
 import org.opencb.opencga.storage.core.io.managers.LocalIOConnector;
@@ -45,6 +46,7 @@ public class VariantMetadataConverterTest {
     @Before
     public void setUp() throws Exception {
         metadataManager = new VariantStorageMetadataManager(new DummyVariantStorageMetadataDBAdaptorFactory());
+        projectMetadata = metadataManager.getAndUpdateProjectMetadata(new ObjectMap());
 
         URI uri = VariantStorageBaseTest.getResourceUri("platinum/1K.end.platinum-genomes-vcf-NA12877_S1.genome.vcf.gz");
         variantReaderUtils = new VariantReaderUtils(new IOConnectorProvider(LocalIOConnector.class));
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManagerTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManagerTest.java
index 1b3311958f4..71ea72de3c0 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManagerTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/metadata/VariantStorageMetadataManagerTest.java
@@ -4,12 +4,14 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
 import org.opencb.opencga.storage.core.metadata.models.TaskMetadata;
 import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest;
 import org.opencb.opencga.storage.core.variant.VariantStorageTest;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -103,4 +105,36 @@ public List<String> getTasks(StudyMetadata study, List<TaskMetadata.Status> stat
                 .map(TaskMetadata::getName)
                 .collect(Collectors.toList());
     }
+
+    @Test
+    public void testAddSampleToCohort() throws Exception {
+        StudyMetadata study = metadataManager.createStudy("study");
+
+        metadataManager.registerCohort(study.getName(), "cohort1", Collections.emptyList());
+
+        int numSamples = 100;
+        List<Integer> sampleIds = new ArrayList<>(numSamples);
+        for (int i = 0; i < numSamples; i++) {
+            sampleIds.add(metadataManager.registerSample(study.getId(), null, "sample_" + i));
+        }
+
+        metadataManager.addSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 10));
+        VariantStorageMetadataManager metadataManager = Mockito.spy(this.metadataManager);
+        metadataManager.addSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 11));
+        Mockito.verify(metadataManager, Mockito.times(1)).updateSampleMetadata(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
+
+        Mockito.reset(metadataManager);
+        metadataManager.addSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 11));
+        Mockito.verify(metadataManager, Mockito.never()).updateSampleMetadata(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
+        metadataManager.setSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 11));
+        Mockito.verify(metadataManager, Mockito.never()).updateSampleMetadata(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
+
+        metadataManager.setSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 12));
+        Mockito.verify(metadataManager, Mockito.times(1)).updateSampleMetadata(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
+
+        Mockito.reset(metadataManager);
+        metadataManager.setSamplesToCohort(study.getId(), "cohort1", sampleIds.subList(0, 6));
+        Mockito.verify(metadataManager, Mockito.times(6)).updateSampleMetadata(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
+    }
+
 }
\ No newline at end of file
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java
index 32e1657fcb8..a675e5304b0 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineBNDTest.java
@@ -48,6 +48,8 @@ public void before() throws Exception {
         variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
         variantStorageEngine.getConfiguration().getCellbase().setVersion(ParamConstants.CELLBASE_VERSION);
         variantStorageEngine.getConfiguration().getCellbase().setDataRelease(ParamConstants.CELLBASE_DATA_RELEASE_GRCH38);
+        variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
+
         if (!loaded) {
             clearDB(DB_NAME);
             loadFiles();
@@ -59,6 +61,7 @@ protected void loadFiles() throws Exception {
         variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
         variantStorageEngine.getConfiguration().getCellbase().setVersion(ParamConstants.CELLBASE_VERSION);
         variantStorageEngine.getConfiguration().getCellbase().setDataRelease(ParamConstants.CELLBASE_DATA_RELEASE_GRCH38);
+        variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
         studyMetadata = new StudyMetadata(1, "s1");
 //        variantStorageEngine.getOptions().append(VariantStorageOptions.ANNOTATOR_CELLBASE_EXCLUDE.key(), "expression,clinical");
         input1 = getResourceUri("variant-test-bnd.vcf");
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java
index 64fc14c4c6a..2523fad5ba2 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/VariantStorageEngineSVTest.java
@@ -1,5 +1,6 @@
 package org.opencb.opencga.storage.core.variant;
 
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -20,6 +21,10 @@
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQueryParam;
 import org.opencb.opencga.storage.core.variant.adaptors.iterators.VariantDBIterator;
 import org.opencb.opencga.storage.core.variant.io.VariantWriterFactory;
+import org.opencb.opencga.storage.core.variant.query.ParsedVariantQuery;
+import org.opencb.opencga.storage.core.variant.query.VariantQueryResult;
+import org.opencb.opencga.storage.core.variant.query.executors.VariantQueryExecutor;
+import org.opencb.opencga.storage.core.variant.search.SearchIndexVariantQueryExecutor;
 
 import java.net.URI;
 import java.nio.file.Paths;
@@ -53,18 +58,20 @@ public abstract class VariantStorageEngineSVTest extends VariantStorageBaseTest
     public void before() throws Exception {
         if (!loaded) {
             clearDB(DB_NAME);
+        }
+        variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
+        variantStorageEngine.getConfiguration().getCellbase().setVersion(ParamConstants.CELLBASE_VERSION);
+        variantStorageEngine.getConfiguration().getCellbase().setDataRelease(ParamConstants.CELLBASE_DATA_RELEASE_GRCH38);
+        variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
+        variantStorageEngine.reloadCellbaseConfiguration();
+
+        if (!loaded) {
             loadFiles();
             loaded = true;
         }
     }
 
     protected void loadFiles() throws Exception {
-        variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
-        variantStorageEngine.getConfiguration().getCellbase().setVersion("v5.2");
-        variantStorageEngine.getConfiguration().getCellbase().setDataRelease("3");
-        variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
-        variantStorageEngine.reloadCellbaseConfiguration();
-
         input1 = getResourceUri("variant-test-sv.vcf");
         studyMetadata = new StudyMetadata(1, "s1");
         variantStorageEngine.getOptions().append(VariantStorageOptions.ANNOTATOR_CELLBASE_EXCLUDE.key(), "expression,clinical");
@@ -106,6 +113,19 @@ public void checkCount() throws Exception {
         assertEquals(expected, count);
     }
 
+    @Test
+    public void checkSecondaryAnnotationIndex() throws Exception {
+        Assume.assumeTrue(variantStorageEngine.secondaryAnnotationIndexActiveAndAlive());
+        VariantQueryExecutor variantQueryExecutor = variantStorageEngine.getVariantQueryExecutor(SearchIndexVariantQueryExecutor.class);
+        for (Variant variant : variantStorageEngine) {
+            ParsedVariantQuery query = variantStorageEngine
+                    .parseQuery(new Query(VariantQueryParam.ID.key(), variant.toString()), new QueryOptions());
+            VariantQueryResult<Variant> result = variantQueryExecutor.get(query);
+            assertEquals(1, result.getNumResults());
+            assertEquals(variant.toString(), result.first().toString());
+        }
+    }
+
     @Test
     public void checkCorrectnessFile1() throws Exception {
         checkCorrectness(VariantStorageEngineSVTest.input1);
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java
index 905f64a5373..8071b86a14e 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/adaptors/VariantDBAdaptorMultiFileTest.java
@@ -377,18 +377,22 @@ public void testSampleLimitSkip() throws Exception {
         VariantQueryResult<Variant> result = query(new Query(SAMPLE_METADATA.key(), true).append(VariantQueryParam.INCLUDE_SAMPLE.key(), ALL), options);
         System.out.println("samples(ALL) = " + result.getSamples());
 
-        for (int i : new int[]{1, 3, 6, 8, 10}) {
+        int numSamples = metadataManager.getStudyIds().stream().mapToInt(id -> metadataManager.getIndexedSamples(id).size()).sum();
+        assertEquals(8, numSamples);
+        for (int i : new int[]{1, 3, 6, numSamples, 10}) {
             result = query(new VariantQuery().sampleSkip(i).includeSampleAll().sampleMetadata(true), options);
 //            System.out.println("samples(SKIP=" + i + ") = " + result.getSamples());
-            assertEquals(Math.max(0, 8 - i), result.getSamples().values().stream().mapToInt(List::size).sum());
-            assertEquals(Math.max(0, 8 - i), result.getNumSamples().intValue());
-            assertEquals(8, result.getNumTotalSamples().intValue());
+            int expected = Math.max(0, numSamples - i);
+            assertEquals("Skip = " + i + " , expected " + expected + " out of 8 samples", expected, result.getSamples().values().stream().mapToInt(List::size).sum());
+            assertEquals("Skip = " + i + " , expected " + expected + " out of 8 samples", expected, result.getNumSamples().intValue());
+            assertEquals(numSamples, result.getNumTotalSamples().intValue());
 
             result = query(new VariantQuery().sampleLimit(i).includeSampleAll().sampleMetadata(true), options);
 //            System.out.println("samples(LIMIT=" + i + ") = " + result.getSamples());
-            assertEquals(Math.min(8, i), result.getSamples().values().stream().mapToInt(List::size).sum());
-            assertEquals(Math.min(8, i), result.getNumSamples().intValue());
-            assertEquals(8, result.getNumTotalSamples().intValue());
+            expected = Math.min(numSamples, i);
+            assertEquals("Limit = " + i + " , expected " + expected + " out of 8 samples", expected, result.getSamples().values().stream().mapToInt(List::size).sum());
+            assertEquals("Limit = " + i + " , expected " + expected + " out of 8 samples", expected, result.getNumSamples().intValue());
+            assertEquals(numSamples, result.getNumTotalSamples().intValue());
         }
     }
 
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyProjectMetadataAdaptor.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyProjectMetadataAdaptor.java
index bed8d419666..3ba92ed7f1c 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyProjectMetadataAdaptor.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/dummy/DummyProjectMetadataAdaptor.java
@@ -16,7 +16,6 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -29,7 +28,7 @@ public class DummyProjectMetadataAdaptor implements ProjectMetadataAdaptor {
     private static Map<String, Integer> counters = new HashMap<>();
 
     @Override
-    public Lock lockProject(long lockDuration, long timeout, String lockName) throws InterruptedException, TimeoutException {
+    public Lock lockProject(long lockDuration, long timeout, String lockName) {
         return new Lock(0) {
             @Override
             public void unlock0() {
@@ -43,17 +42,12 @@ public void refresh() {
         };
     }
 
-    @Override
-    public void unLockProject(long lockId) {
-    }
-
     @Override
     public synchronized DataResult<ProjectMetadata> getProjectMetadata() {
         final DataResult<ProjectMetadata> result = new DataResult<>();
-        if (projectMetadata == null) {
-            projectMetadata = new ProjectMetadata("hsapiens", "grch37", 1);
+        if (projectMetadata != null) {
+            result.setResults(Collections.singletonList(projectMetadata.copy()));
         }
-        result.setResults(Collections.singletonList(projectMetadata.copy()));
         return result;
     }
 
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/io/VariantWriterFactoryTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/io/VariantWriterFactoryTest.java
index 221a126cd92..38f995c823d 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/io/VariantWriterFactoryTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/io/VariantWriterFactoryTest.java
@@ -23,11 +23,13 @@
 import org.opencb.biodata.models.variant.Variant;
 import org.opencb.biodata.models.variant.metadata.VariantFileHeader;
 import org.opencb.biodata.models.variant.metadata.VariantFileHeaderComplexLine;
+import org.opencb.commons.datastore.core.ObjectMap;
 import org.opencb.commons.datastore.core.Query;
 import org.opencb.commons.datastore.core.QueryOptions;
 import org.opencb.commons.io.DataWriter;
 import org.opencb.opencga.core.testclassification.duration.ShortTests;
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
+import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
 import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
 import org.opencb.opencga.storage.core.variant.dummy.DummyVariantDBAdaptor;
 import org.opencb.opencga.storage.core.variant.dummy.DummyVariantStorageMetadataDBAdaptorFactory;
@@ -85,8 +87,10 @@ public void testContigLengthNull() throws IOException, StorageEngineException {
                 new VariantFileHeaderComplexLine("contig", "chr3", null, null, null, Collections.singletonMap("length", ".")),
                 new VariantFileHeaderComplexLine("contig", "chr4", null, null, null, Collections.singletonMap("length", "1234"))
         ));
-        StudyMetadata study = dbAdaptor.getMetadataManager().createStudy("study");
-        dbAdaptor.getMetadataManager().unsecureUpdateStudyMetadata(study.setVariantHeader(header));
+        VariantStorageMetadataManager metadataManager = dbAdaptor.getMetadataManager();
+        metadataManager.getAndUpdateProjectMetadata(new ObjectMap());
+        StudyMetadata study = metadataManager.createStudy("study");
+        metadataManager.unsecureUpdateStudyMetadata(study.setVariantHeader(header));
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream(10000);
         DataWriter<Variant> writer = new VariantWriterFactory(dbAdaptor).newDataWriter(
                 VariantWriterFactory.VariantOutputFormat.VCF,
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java
index 1fc7617422c..f5e4c2526b9 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/query/executors/VariantQueryExecutorTest.java
@@ -12,6 +12,7 @@
 import org.opencb.opencga.storage.core.exceptions.StorageEngineException;
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
 import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
+import org.opencb.opencga.storage.core.metadata.models.Trio;
 import org.opencb.opencga.storage.core.variant.VariantStorageBaseTest;
 import org.opencb.opencga.storage.core.variant.VariantStorageOptions;
 import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass;
@@ -88,6 +89,9 @@ public void setUp() throws Exception {
             fileIndexed = true;
             Integer indexedFileId = metadataManager.getIndexedFiles(studyMetadata.getId()).iterator().next();
 
+            Trio trio = new Trio("NA19660", "NA19661", "NA19685");
+            variantStorageEngine.familyIndex(studyMetadata.getName(), Collections.singletonList(trio), new ObjectMap());
+
             //Calculate stats
             QueryOptions options = new QueryOptions(VariantStorageOptions.STUDY.key(), STUDY_NAME)
                     .append(VariantStorageOptions.LOAD_BATCH_SIZE.key(), 100)
@@ -206,23 +210,53 @@ public void testXRefRs() throws StorageEngineException {
         }
     }
 
+    @Test
+    public void testCompHetQuery() throws StorageEngineException {
+//        Matcher<Variant> matcher = allOf(
+//                anyOf(
+//                        samePosition(new Variant("1:2441358:T:C")),
+//                        samePosition(new Variant("1:2458010:G:C")),
+//                        samePosition(new Variant("19:501725:G:A")),
+//                        samePosition(new Variant("19:501900:C:A"))),
+//                withStudy(STUDY_NAME, withSampleGt("NA19685")));
+        Matcher<Variant> matcher = null;
+        testQuery(new VariantQuery().sample("NA19685:compoundheterozygous")
+                        .study(STUDY_NAME)
+                        .biotype("protein_coding"),
+                new QueryOptions(),
+                matcher,
+                false);
+    }
+
     public VariantQueryResult<Variant> testQuery(Query query, QueryOptions options, Matcher<Variant> matcher) throws StorageEngineException {
+        return testQuery(query, options, matcher, true);
+    }
+
+    public VariantQueryResult<Variant> testQuery(Query query, QueryOptions options, Matcher<Variant> matcher, boolean expectDBAdaptorExecutor)
+            throws StorageEngineException {
         logger.info("");
         logger.info("");
         logger.info("####################################################");
         logger.info("########## TEST QUERY :" + query.toJson());
         logger.info("####################################################");
         logger.info("## Allowed VariantQueryExecutors:");
+        ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options);
         for (VariantQueryExecutor variantQueryExecutor : variantQueryExecutors) {
-            if (variantQueryExecutor.canUseThisExecutor(query, options)) {
+            if (variantQueryExecutor.canUseThisExecutor(variantQuery, options)) {
                 logger.info("## - " + variantQueryExecutor.getClass().getSimpleName());
             }
         }
-        logger.info("## Using DBAdaptorVariantQueryExecutor for expected results");
-        Assert.assertTrue(dbQueryExecutor.canUseThisExecutor(query, options));
+        VariantQueryResult<Variant> expected;
+        if (expectDBAdaptorExecutor) {
+            logger.info("## Using DBAdaptorVariantQueryExecutor for expected results");
+            Assert.assertTrue(dbQueryExecutor.canUseThisExecutor(variantQuery, options));
 
-        ParsedVariantQuery variantQuery = variantStorageEngine.parseQuery(query, options);
-        VariantQueryResult<Variant> expected = dbQueryExecutor.get(variantQuery);
+            expected = dbQueryExecutor.get(variantQuery);
+        } else {
+            logger.info("## DBAdaptorVariantQueryExecutor can not be used for expected results");
+            Assert.assertFalse(dbQueryExecutor.canUseThisExecutor(variantQuery, options));
+            expected = null;
+        }
 
         VariantQueryResult<Variant> unfilteredResult = null;
         VariantQueryResult<Variant> result = null;
@@ -261,21 +295,24 @@ public VariantQueryResult<Variant> testQuery(Query query, QueryOptions options,
             QueryOptions emptyOptions = new QueryOptions();
             emptyOptions.putIfNotEmpty(QueryOptions.INCLUDE, options.getString(QueryOptions.INCLUDE));
             emptyOptions.putIfNotEmpty(QueryOptions.EXCLUDE, options.getString(QueryOptions.EXCLUDE));
+            logger.info("## unfiltered query " + VariantQueryUtils.printQuery(emptyQuery));
+            logger.info("## unfiltered options " + emptyOptions.toJson());
             unfilteredResult = dbQueryExecutor.get(variantStorageEngine.parseQuery(emptyQuery, emptyOptions));
         }
 
         for (VariantQueryExecutor variantQueryExecutor : variantQueryExecutors) {
-            if (variantQueryExecutor.canUseThisExecutor(query, options)) {
+            if (variantQueryExecutor.canUseThisExecutor(variantQuery, options)) {
                 logger.info("");
                 logger.info("###################");
                 logger.info("### Testing " + variantQueryExecutor.getClass().getSimpleName());
                 result = variantQueryExecutor.get(variantQuery);
                 logger.info("### Num results : " + result.getNumResults());
                 logger.info("###################");
-                expected.getResults().sort(Comparator.comparing(Variant::toString));
-                result.getResults().sort(Comparator.comparing(Variant::toString));
-                Assert.assertEquals(expected.getResults(), result.getResults());
-
+                if (expected != null) {
+                    expected.getResults().sort(Comparator.comparing(Variant::toString));
+                    result.getResults().sort(Comparator.comparing(Variant::toString));
+                    Assert.assertEquals(expected.getResults(), result.getResults());
+                }
                 assertThat(result, numResults(gt(0)));
 
                 if (matcher != null) {
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverterTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverterTest.java
index 5839db1745b..539ea8c22d6 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverterTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/VariantSearchToVariantConverterTest.java
@@ -49,7 +49,7 @@ public void test() throws Exception {
         expectedVariant.addStudyEntry(aux.getStudy("2"));
 
         VariantSearchModel variantSearchModel = converter.convertToStorageType(expectedVariant);
-        assertNull(variantSearchModel.getVariantId());
+        assertNotNull(variantSearchModel.getVariantId());
         assertEquals(variantId, variantSearchModel.getId());
         Variant actualVariant = converter.convertToDataModelType(variantSearchModel);
 
diff --git a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParserTest.java b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParserTest.java
index d46cafe43df..2b1c3c3ba08 100644
--- a/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParserTest.java
+++ b/opencga-storage/opencga-storage-core/src/test/java/org/opencb/opencga/storage/core/variant/search/solr/SolrQueryParserTest.java
@@ -43,8 +43,8 @@ public class SolrQueryParserTest {
 
     private String studyName = "platinum";
     private String flBase = "fl=other,geneToSoAcc,traits,type,soAcc,score_*,sift,passStats_*,caddRaw,biotypes,polyphenDesc,studies,end,id,variantId,"
-            + "popFreq_*,caddScaled,genes,chromosome,xrefs,start,gerp,polyphen,siftDesc,"
-            + "phastCons,phylop,altStats_*,id,chromosome,start,end,type";
+            + "popFreq_*,caddScaled,genes,chromosome,xrefs,start,gerp,polyphen,attr_id,siftDesc,"
+            + "phastCons,phylop,altStats_*";
     private String flDefault1 = flBase + ",fileInfo__*,qual__*,filter__*,sampleFormat__*";
     private String flDefaultStudy = flBase + ",fileInfo__" + studyName + "__*,qual__" + studyName + "__*,"
             + "filter__" + studyName + "__*,sampleFormat__" + studyName + "__*";
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManagerTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManagerTest.java
index 0f421081ff6..5943a43519f 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManagerTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/utils/HBaseLockManagerTest.java
@@ -214,23 +214,47 @@ public void testLockRefreshExpiredRefresh() throws Exception {
     @Test
     public void testGetCurrent() {
         long e = System.currentTimeMillis() + 1000;
-        String s;
+        HBaseLockManager.LockToken s;
+
+        // null token
+        s = HBaseLockManager.parseLockToken(null);
+        assertTrue(s.isEmpty());
+        assertFalse(s.isTaken());
+        assertEquals(null, s.getType());
+        assertArrayEquals(new byte[0], s.getLockValue());
+
+        // Empty token
+        s = HBaseLockManager.parseLockToken(Bytes.toBytes(""));
+        assertTrue(s.isEmpty());
+        assertFalse(s.isTaken());
+        assertEquals(null, s.getType());
+        assertArrayEquals(new byte[0], s.getLockValue());
 
         // Expired current token
-        s = HBaseLockManager.parseValidLockToken(Bytes.toBytes("CURRENT-abc:123"));
-        assertNull(s);
+        s = HBaseLockManager.parseLockToken(Bytes.toBytes("CURRENT-abc:123"));
+        assertFalse(s.isEmpty());
+        assertEquals("CURRENT", s.getType());
 
         // Valid current token
-        s = HBaseLockManager.parseValidLockToken(Bytes.toBytes("CURRENT-abc:" + e));
-        assertEquals("abc", s);
+        s = HBaseLockManager.parseLockToken(Bytes.toBytes("CURRENT-abc:" + e));
+        assertEquals("abc", s.token);
+        assertEquals("CURRENT", s.getType());
+        assertFalse(s.isExpired());
+        assertTrue(s.isTaken());
 
         // Current expired, first refresh valid
-        s = HBaseLockManager.parseValidLockToken(Bytes.toBytes("REFRESH-abc:" + e));
-        assertEquals("abc", s);
+        s = HBaseLockManager.parseLockToken(Bytes.toBytes("REFRESH-abc:" + e));
+        assertEquals("abc", s.token);
+        assertEquals("REFRESH", s.getType());
+        assertFalse(s.isExpired());
+        assertTrue(s.isTaken());
 
         // Expired refresh
-        s = HBaseLockManager.parseValidLockToken(Bytes.toBytes("REFRESH-abc:200"));
-        assertNull(s);
+        s = HBaseLockManager.parseLockToken(Bytes.toBytes("REFRESH-abc:200"));
+        assertEquals("abc", s.token);
+        assertEquals("REFRESH", s.getType());
+        assertTrue(s.isExpired());
+        assertFalse(s.isTaken());
     }
 
 }
\ No newline at end of file
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineBNDTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineBNDTest.java
index aea720d356a..b613df935ba 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineBNDTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineBNDTest.java
@@ -19,7 +19,7 @@ public class HadoopVariantStorageEngineBNDTest extends VariantStorageEngineBNDTe
     @Override
     protected void loadFiles() throws Exception {
         super.loadFiles();
-        VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri());
+        VariantHbaseTestUtils.printVariants(((HadoopVariantStorageEngine) variantStorageEngine).getDBAdaptor(), newOutputUri());
     }
 
 }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java
index fbcbd773477..26bcb49bb0f 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/HadoopVariantStorageEngineSVTest.java
@@ -1,8 +1,6 @@
 package org.opencb.opencga.storage.hadoop.variant;
 
-import org.junit.Assert;
-import org.junit.ClassRule;
-import org.junit.Test;
+import org.junit.*;
 import org.junit.experimental.categories.Category;
 import org.opencb.biodata.models.variant.StudyEntry;
 import org.opencb.biodata.models.variant.Variant;
@@ -12,13 +10,15 @@
 import org.opencb.opencga.storage.core.variant.adaptors.GenotypeClass;
 import org.opencb.opencga.storage.core.variant.adaptors.VariantQuery;
 import org.opencb.opencga.storage.core.variant.query.VariantQueryResult;
+import org.opencb.opencga.storage.core.variant.solr.VariantSolrExternalResource;
+import org.opencb.opencga.storage.hadoop.HBaseCompat;
 import org.opencb.opencga.storage.hadoop.variant.adaptors.VariantHadoopDBAdaptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.*;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 import static org.opencb.opencga.core.api.ParamConstants.OVERWRITE;
 
 /**
@@ -33,9 +33,46 @@ public class HadoopVariantStorageEngineSVTest extends VariantStorageEngineSVTest
     public static HadoopExternalResource externalResource = new HadoopExternalResource();
     private Logger logger = LoggerFactory.getLogger(getClass());
 
+    public static VariantSolrExternalResource solr = new VariantSolrExternalResource();
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        if (HBaseCompat.getInstance().isSolrTestingAvailable()) {
+            solr.before();
+            solr.configure(externalResource.getVariantStorageEngine());
+            System.out.println("Start embedded solr");
+        } else {
+            System.out.println("Skip embedded solr tests");
+        }
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        if (HBaseCompat.getInstance().isSolrTestingAvailable()) {
+            solr.after();
+        }
+    }
+
+    @Override
+    public void before() throws Exception {
+        super.before();
+        if (HBaseCompat.getInstance().isSolrTestingAvailable()) {
+            solr.configure(variantStorageEngine);
+        }
+    }
+
     @Override
     protected void loadFiles() throws Exception {
+        if (HBaseCompat.getInstance().isSolrTestingAvailable()) {
+            solr.configure(variantStorageEngine);
+        }
         super.loadFiles();
+        if (HBaseCompat.getInstance().isSolrTestingAvailable()) {
+            variantStorageEngine.secondaryIndex();
+            assertTrue(variantStorageEngine.secondaryAnnotationIndexActiveAndAlive());
+        } else {
+            assertFalse(variantStorageEngine.secondaryAnnotationIndexActiveAndAlive());
+        }
         VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri(getTestName().getMethodName()));
     }
 
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorTest.java
index d609e769f0d..8cead56116a 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/adaptors/HadoopVariantDBAdaptorTest.java
@@ -119,6 +119,7 @@ public void before() throws Exception {
                 e.printStackTrace();
             }
         }
+        variantStorageEngine.getOptions().append(VariantStorageOptions.ASSEMBLY.key(), "GRCH38");
         cellBaseUtils = variantStorageEngine.getCellBaseUtils();
         expectedConnections = GlobalClientMetrics.GLOBAL_OPEN_PHOENIX_CONNECTIONS.getMetric().getValue();
     }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTaskTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTaskTest.java
index 3d9db73719e..63f579cb99e 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTaskTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/gaps/FillGapsTaskTest.java
@@ -13,6 +13,7 @@
 import org.opencb.biodata.models.variant.metadata.VariantFileHeaderComplexLine;
 import org.opencb.biodata.models.variant.protobuf.VcfSliceProtos;
 import org.opencb.biodata.tools.variant.converters.proto.VariantToVcfSliceConverter;
+import org.opencb.commons.datastore.core.ObjectMap;
 import org.opencb.opencga.core.testclassification.duration.ShortTests;
 import org.opencb.opencga.storage.core.metadata.VariantStorageMetadataManager;
 import org.opencb.opencga.storage.core.metadata.models.StudyMetadata;
@@ -44,6 +45,7 @@ public class FillGapsTaskTest {
     public void setUp() throws Exception {
         DummyVariantStorageMetadataDBAdaptorFactory.clear();
         metadataManager = new VariantStorageMetadataManager(new DummyVariantStorageMetadataDBAdaptorFactory());
+        metadataManager.getAndUpdateProjectMetadata(new ObjectMap());
         studyMetadata = metadataManager.createStudy("S");
         metadataManager.updateStudyMetadata("S", sm -> {
             sm.getAttributes().put(VariantStorageOptions.EXTRA_FORMAT_FIELDS.key(), "DP");
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java
index f67d5734d7c..a6aeba91145 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/family/FamilyIndexTest.java
@@ -63,13 +63,13 @@ public class FamilyIndexTest extends VariantStorageBaseTest implements HadoopVar
 
     @Before
     public void before() throws Exception {
+        HadoopVariantStorageEngine variantStorageEngine = getVariantStorageEngine();
+        variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
+        variantStorageEngine.getConfiguration().getCellbase().setVersion("v5.2");
+        variantStorageEngine.getConfiguration().getCellbase().setDataRelease("3");
+        variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
+        variantStorageEngine.reloadCellbaseConfiguration();
         if (!loaded) {
-            HadoopVariantStorageEngine variantStorageEngine = getVariantStorageEngine();
-            variantStorageEngine.getConfiguration().getCellbase().setUrl(ParamConstants.CELLBASE_URL);
-            variantStorageEngine.getConfiguration().getCellbase().setVersion("v5.2");
-            variantStorageEngine.getConfiguration().getCellbase().setDataRelease("3");
-            variantStorageEngine.getOptions().put(VariantStorageOptions.ASSEMBLY.key(), "grch38");
-            variantStorageEngine.reloadCellbaseConfiguration();
             URI outputUri = newOutputUri();
 
             ObjectMap params = new ObjectMap(VariantStorageOptions.ANNOTATE.key(), false)
@@ -91,7 +91,7 @@ public void before() throws Exception {
 
             variantStorageEngine.annotate(outputUri, new ObjectMap());
 
-            VariantHbaseTestUtils.printVariants(getVariantStorageEngine().getDBAdaptor(), newOutputUri(getTestName().getMethodName()));
+            VariantHbaseTestUtils.printVariants(variantStorageEngine.getDBAdaptor(), newOutputUri(getTestName().getMethodName()));
 
             mendelianErrorVariants = new HashSet<>();
             deNovoVariants = new HashSet<>();
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java
index 99cf76e71cc..e9c46d5214f 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexQueryParserTest.java
@@ -1387,7 +1387,6 @@ public void parseIntergenicTest() {
         checkIntergenic(null, new Query(ANNOT_CONSEQUENCE_TYPE.key(), VariantAnnotationConstants.REGULATORY_REGION_VARIANT));
         checkIntergenic(false, new Query(ANNOT_CONSEQUENCE_TYPE.key(), VariantAnnotationConstants.REGULATORY_REGION_VARIANT)
                 .append(ANNOT_BIOTYPE.key(), "protein_coding"));
-
         // Nonsense combination
         checkIntergenic(false, new Query(ANNOT_CONSEQUENCE_TYPE.key(), "intergenic_variant").append(ANNOT_BIOTYPE.key(), "protein_coding"));
     }
diff --git a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java
index 55d9d98a0f8..98062c27b8f 100644
--- a/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java
+++ b/opencga-storage/opencga-storage-hadoop/opencga-storage-hadoop-core/src/test/java/org/opencb/opencga/storage/hadoop/variant/index/sample/SampleIndexTest.java
@@ -1067,7 +1067,11 @@ public void testFamilyIndexQueryCount() {
                         .append(QueryOptions.LIMIT, 10)
                         .append(QueryOptions.COUNT, true));
 
-        System.out.println(result.getResults().stream().map(Variant::getAnnotation).flatMap(v -> v.getConsequenceTypes().stream()).map(ConsequenceType::getGeneName).collect(Collectors.toSet()));
+        System.out.println(result.getResults().stream()
+                .map(Variant::getAnnotation)
+                .flatMap(v -> v.getConsequenceTypes().stream())
+                .map(ConsequenceType::getGeneName)
+                .collect(Collectors.toSet()));
 
         result = variantStorageEngine.get(
                 new Query()
@@ -1081,7 +1085,11 @@ public void testFamilyIndexQueryCount() {
                         .append(QueryOptions.LIMIT, 10)
                         .append(QueryOptions.COUNT, true));
 
-        System.out.println(result.getResults().stream().map(Variant::getAnnotation).flatMap(v -> v.getConsequenceTypes().stream()).map(ConsequenceType::getGeneName).collect(Collectors.toSet()));
+        System.out.println(result.getResults().stream()
+                .map(Variant::getAnnotation)
+                .flatMap(v -> v.getConsequenceTypes().stream())
+                .map(ConsequenceType::getGeneName)
+                .collect(Collectors.toSet()));
     }
 
     @Test