Skip to content

Commit

Permalink
fix(data): Fix conflict on index creation for the data collection. A…
Browse files Browse the repository at this point in the history
…dd migration command

Signed-off-by: renaud colin <renaud.colin@inra.fr>
  • Loading branch information
renaud colin authored and renaud colin committed Apr 9, 2024
1 parent cd4d5f1 commit ea805ae
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 41 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [1.2.3]

> ⚠️ WARNING : upgrading to this new version require manual operations. Please
> see the [versioning notes](https://github.com/OpenSILEX/opensilex/blob/master/opensilex-doc/src/main/resources/release/1.2.3.md).
### Highlight

- Some data-related pages and services have been **greatly optimized**, especially for OpenSILEX instances where a
Expand All @@ -106,7 +109,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- An error message is now returned if the move is incorrectly declared, even though nothing was indicated and nothing happened before.
- (!1178) You can no longer select an end date that is earlier than the start date during Event creation. The target
column of the Event list now redirects to the resource in question upon click.
- (!1195) Germplasm and Germplasm Group filter no longer treated as a logical OR with the tragets filter
- (!1195) Germplasm and Germplasm Group filter no longer treated as a logical OR with the targets filter
- (!1203) The details tab is now the first and default tab for facilities instead of monitoring

### Fixed or optimized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,13 @@ public void install(boolean reset) throws Exception {

@Override
public void startup() throws Exception {

MongoDBServiceV2 mongoDBServiceV2 = getOpenSilex().getServiceInstance(MongoDBServiceV2.DEFAULT_SERVICE, MongoDBServiceV2.class);
mongoDBServiceV2.registerIndexes(DataDaoV2.COLLECTION_NAME, DataDaoV2.getIndexes());

// Ensure index creation on application start (only in production)
if (!getOpenSilex().isTest() && !getOpenSilex().isReservedProfile()) {
MongoDBServiceV2 mongoDBServiceV2 = getOpenSilex().getServiceInstance(MongoDBServiceV2.DEFAULT_SERVICE, MongoDBServiceV2.class);
mongoDBServiceV2.createIndexes(DataDaoV2.COLLECTION_NAME, DataDaoV2.getIndexes());
mongoDBServiceV2.createIndexes();
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,6 @@ public DataDAO(MongoDBService nosql, SPARQLService sparql, FileStorageService fs
public void createIndexes() {
IndexOptions unicityOptions = new IndexOptions().unique(true);

MongoCollection<DataModel> dataCollection = nosql.getDatabase()
.getCollection(DATA_COLLECTION_NAME, DataModel.class);
dataCollection.createIndex(Indexes.ascending("uri"), unicityOptions);
dataCollection.createIndex(Indexes.ascending(DataModel.VARIABLE_FIELD, "provenance", DataModel.TARGET_FIELD, "date"), unicityOptions);
dataCollection.createIndex(Indexes.ascending(DataModel.VARIABLE_FIELD, DataModel.TARGET_FIELD, "date"));
dataCollection.createIndex(Indexes.compoundIndex(Arrays.asList(Indexes.ascending(DataModel.VARIABLE_FIELD),Indexes.descending("date"))));
dataCollection.createIndex(Indexes.ascending("date"));
dataCollection.createIndex(Indexes.descending("date"));

MongoCollection<DataFileModel> fileCollection = nosql.getDatabase()
.getCollection(FILE_COLLECTION_NAME, DataFileModel.class);
fileCollection.createIndex(Indexes.ascending("uri"), unicityOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static Map<Bson, IndexOptions> getIndexes() {

IndexOptions defaultOptions = new IndexOptions();

Bson idIndex = Indexes.ascending(MongoModel.MONGO_ID_FIELD);
Bson dateDescIndex = Indexes.descending(DataModel.DATE_FIELD);
Bson variableAscIndex = Indexes.ascending(DataModel.VARIABLE_FIELD);
Bson targetDescIndex = Indexes.ascending(DataModel.TARGET_FIELD);
Expand All @@ -53,7 +54,8 @@ public static Map<Bson, IndexOptions> getIndexes() {

Map<Bson, IndexOptions> indexes = new HashMap<>();

// index on field : URI
// index on field : _id, URI and date
indexes.put(idIndex, new IndexOptions().unique(true));
indexes.put(Indexes.ascending(MongoModel.URI_FIELD), new IndexOptions().unique(true));
indexes.put(dateDescIndex, defaultOptions);

Expand All @@ -66,6 +68,8 @@ public static Map<Bson, IndexOptions> getIndexes() {

// Multi-fields indexes : Access by experiment and (variable, target, provenance agent). Add date to ensure index usage in case of sorting by date
indexes.put(Indexes.compoundIndex(experimentAscIndex, variableAscIndex, targetDescIndex, dateDescIndex), defaultOptions);
indexes.put(Indexes.compoundIndex(agentAscIndex, targetDescIndex, dateDescIndex), defaultOptions);
indexes.put(Indexes.compoundIndex(variableAscIndex, targetDescIndex, dateDescIndex), defaultOptions);

// Compound index : ensure unicity #TODO delete this index (index on whole field,too big and not well used in query)
indexes.put(Indexes.compoundIndex(variableAscIndex, Indexes.ascending(DataModel.PROVENANCE_FIELD), targetDescIndex, dateDescIndex), new IndexOptions().unique(true));
Expand Down
12 changes: 11 additions & 1 deletion opensilex-doc/src/main/resources/how-to/migration_command.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ This document describes how to execute migration commands into OpenSILEX, the li
* [Description](#description-4)
* [org.opensilex.migration.AddAccountCredentialsToProfilWithUserCredential](#orgopensilexmigrationaddaccountcredentialstoprofilwithusercredential)
* [Description](#description-5)
* [org.opensilex.migration.MongoDbIndexesMigration](#orgopensilexmigrationmongodbindexesmigration)
* [Description](#description-6)
* [Create an update command (For developers)](#create-an-update-command-for-developers)
* [Example](#example)
<!-- TOC -->
Expand Down Expand Up @@ -73,6 +75,7 @@ org.opensilex.migration.GraphAndCollectionMigration
| 2023/01/24 | <b>org.opensilex.migration.ScientificObjectNameIntegerConvertMigration</b> | 1.0.0-rc+6.5 | |
| 2023/03/17 | <b>org.opensilex.migration.AgentsMigrateToAccountAndPersons</b> | 1.0.0-rc+7 | 8ed0303a |
| 2023/06/26 | <b> org.opensilex.migration.ObjectMigrationFromAccountToPerson </b> | 1.0.0 | 613f6d59 |
| 2024/04/09 | <b> org.opensilex.migration.MongoDbIndexesMigration </b> | 1.2.3 | |

# Descriptions

Expand Down Expand Up @@ -161,9 +164,16 @@ following predicates are concerned :
### Description

This migration make a list of all profiles that has a credential on user. Then, it adds the same credential (show, add/update or delete) but for the accounts.
This migration was done because Users credentials was replaced by account credentials in the web Interface, so it is necessary to migrate credentials, otherwise some people may have the suprise to not be able to reach the "account menu' anymore after the last deployment of OpenSilex 1.2.
This migration was done because Users credentials was replaced by account credentials in the web Interface, so it is necessary to migrate credentials, otherwise some people may have the surprise to not be able to reach the "account menu' anymore after the last deployment of OpenSilex 1.2.


## org.opensilex.migration.MongoDbIndexesMigration

### Description

- This migration ensures that the database has the indexes specified by OpenSILEX
- **Note**: Executing this migration cause the deletion of indexes which are not registered by OpenSILEX

# Create an update command (For developers)

Each Opensilex update implements the <b>`org.opensilex.update.OpenSilexModuleUpdate`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,21 @@ title: MongoDB indexation on data collection

## Access by one field for equality, sort by date

| **Name** | **Description** | **Tri** | **Query example** | **Options** |
| **Name** | **Description** | **Sort** | **Query example** | **Options** |
|--------------------------------------------------|-----------------------------|-------------|-------------------|-------------|
| `target_1_date_-1` | Access by target | date (DESC) | | |
| `provenance.experiments_1_date_-1` | Access by experiment | date (DESC) | | |
| `variable_date_-1` | Access by variable | date (DESC) | | |
| `variable_1_date_-1` | Access by variable | date (DESC) | | |
| `provenance.provWasAssociatedWith.uri_1_date_-1` | Access by agent | date (DESC) | | |
| `provenance.uri_1_date_-1` | Access by global provenance | date (DESC) | | |

## Access by multiple field for equality, sort by date

| **Name** | **Description** | **Tri** | **Query example** | **Options** |
|------------------------------------------------------------------------------------|----------------------------------------|-------------|-------------------|-------------|
| `provenance.experiments_1_variable_1_target_1_date_-1` | Access by experiment, variable, target | date (DESC) | | |
| `provenance.experiments_1_provenance.provWasAssociatedWith.uri_1_target_1_date_-1` | Access by experiment, agent, target | date (DESC) | | |
| `provenance.experiments_1_target_1_provenance.provWasAssociatedWith.uri_1_date_-1` | Access by experiment, target, agent | date (DESC) | | |
| **Name** | **Description** | **Sort** | **Query example** | **Options** |
|-----------------------------------------------------------|----------------------------------------|-------------|-------------------|-------------|
| `provenance.experiments_1_variable_1_target_1_date_-1` | Access by experiment, variable, target | date (DESC) | | |
| `provenance.provWasAssociatedWith.uri_1_target_1_date_-1` | Access by agent, target | date (DESC) | | |
| `variable_1_target_1_date_-1` | Access by variable, target | date (DESC) | | |

## Access by multiple field : unique index

Expand All @@ -54,7 +54,7 @@ title: MongoDB indexation on data collection
## target_1_date_-1

```javascript
db.data.find({target: "opensilex:target_1"}).sort({date: -1})
db.data.find({"target": "opensilex:target_1"}).sort({date: -1})
```

## provenance.experiments_1_date_-1
Expand All @@ -81,3 +81,33 @@ db.data.find({"provenance.provWasAssociatedWith": "opensilex:device_1"}).sort({d
db.data.find({"provenance.uri": "opensilex:provenance_1"}).sort({date: -1})
```

## provenance.experiments_1_variable_1_target_1_date_-1

```javascript
db.data.find({"provenance.experiment": "opensilex:experiment_1", "variable": "opensilex:variable_1"}).sort({date: -1})

db.data.find({
"provenance.experiment": "opensilex:experiment_1",
"variable": "opensilex:variable_1",
"target": "opensilex:target_1"
}).sort({date: -1})
```

## provenance.provWasAssociatedWith.uri_1_target_1_date_-1

```javascript
db.data.find({
"provenance.provWasAssociatedWith": "opensilex:device_1",
"target": "opensilex:target_1"
}).sort({date: -1})
```

## variable_1_target_1_date_-1

```javascript
db.data.find({
"variable": "opensilex:variable_1",
"target": "opensilex:target_1"
}).sort({date: -1})
```

10 changes: 10 additions & 0 deletions opensilex-doc/src/main/resources/release/1.2.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Migrate to 1.2.3

## Migration of data collection index

- Run this command in order to delete previously created indexes and create new indexes required by OpenSILEX
- Depending on your number of document in the `data` collection, it can take several minutes to complete

```bash
java -jar opensilex.jar --CONFIG_FILE=<config_file> system run-update org.opensilex.migration.MongoDbIndexesMigration --DEBUG
```[1.2.0.md](1.2.0.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.opensilex.migration;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import org.apache.commons.collections4.SetUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.opensilex.nosql.mongodb.service.v2.MongoDBServiceV2;
import org.opensilex.update.AbstractOpenSilexModuleUpdate;
import org.opensilex.update.OpensilexModuleUpdateException;

import java.util.*;

/**
* @author rcolin
*/
public class MongoDbIndexesMigration extends AbstractOpenSilexModuleUpdate {

@Override
public String getDescription() {
return "Update MongoDB indexes";
}

@Override
public void execute() throws OpensilexModuleUpdateException {

MongoDBServiceV2 mongoDBServiceV2 = opensilex.getServiceInstance(MongoDBServiceV2.DEFAULT_SERVICE, MongoDBServiceV2.class);
MongoDatabase database = mongoDBServiceV2.getDatabase();

// for each collection, ensure that indexes correspond to those that the OpenSILEX application register
for(var indexesByCollection : mongoDBServiceV2.getIndexRegister().entrySet()){

// Get Set of required indexes, Set of existing index and Compute Set of indexes to create/delete by using set difference
MongoCollection<?> collection = database.getCollection(indexesByCollection.getKey());
Map<Bson, IndexOptions> indexesOptions = indexesByCollection.getValue();

Set<Bson> existingIndexes = new HashSet<>();
collection.listIndexes().forEach(bson -> {
Document existingIndex = bson.get("key", Document.class);
existingIndexes.add(getIndexSpecificationWithoutOption(existingIndex));
});

// Delete old indexes and create new indexes
Set<Bson> indexesToDelete = SetUtils.difference(existingIndexes, indexesOptions.keySet());
for(Bson index : indexesToDelete){
mongoDBServiceV2.dropIndex(collection, index);
}

Set<Bson> indexesToCreate = SetUtils.difference(indexesOptions.keySet(), existingIndexes);
for(Bson index : indexesToCreate){
mongoDBServiceV2.createIndex(collection, index, indexesOptions.get(index));
}
}
}

/**
*
* @param existingIndex Document returned from a {@link MongoCollection#listIndexes()}
* @return a Bson document matching the index format when passing to {@link MongoCollection#createIndex(Bson, IndexOptions)} or {@link MongoCollection#dropIndex(Bson)}
*/

private Bson getIndexSpecificationWithoutOption(Document existingIndex){

List<Bson> indexes = new LinkedList<>();
existingIndex.keySet().forEach(indexField -> {
int order = existingIndex.getInteger(indexField);
if (order == 1) {
indexes.add(Indexes.ascending(indexField));
} else {
indexes.add(Indexes.descending(indexField));
}
});

if (indexes.size() == 1) {
return indexes.get(0);
} else {
return Indexes.compoundIndex(indexes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public interface MongoDBConfig extends ServiceConfig {
*/
@ConfigDescription(
value = "Max socket read timeout",
defaultInt = 60000
defaultInt = 120000
)
int readTimeoutMs();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ public class MongoLogger {
public static final String CHECK_MONGO_SERVER_CONNECTION = "mongo_server_check_connection";
public static final String TIMEOUT_MS = "timeout_ms";

public static final String MONGO_CREATE_INDEX_LOG_MSG = "create_index";
public static final String MONGO_CREATE_INDEX = "create_index";

public static final String MONGO_DELETE_INDEX = "create_index";

public static final String COMMAND_TYPE = "server_command";
public static final String COMMAND_NAME_FIELD = "command_name";

Expand Down
Loading

0 comments on commit ea805ae

Please sign in to comment.