Skip to content

Commit

Permalink
Add minimal service support for adding, removing and replacing single…
Browse files Browse the repository at this point in the history
…-cell data
  • Loading branch information
arteymix committed Feb 7, 2024
1 parent f1236d8 commit f04ae2f
Show file tree
Hide file tree
Showing 18 changed files with 771 additions and 238 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import ubic.gemma.model.common.AbstractDescribable;

import java.io.Serializable;
import java.util.Objects;

public class QuantitationType extends AbstractDescribable implements Serializable {

Expand Down Expand Up @@ -214,6 +215,10 @@ public boolean equals( Object object ) {
}
final QuantitationType that = ( QuantitationType ) object;

if ( that.getId() != null && this.getId() != null ) {
return Objects.equals( that.getId(), this.getId() );
}

if ( that.getName() != null && this.getName() != null && !this.getName().equals( that.getName() ) ) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ public boolean equals( Object object ) {

@Override
public int hashCode() {
if ( getId() != null ) {
return Objects.hashCode( getId() );
}
return Objects.hash( super.hashCode(), Objects.hashCode( bioAssayDimension ) );
return Objects.hash( super.hashCode(), bioAssayDimension );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ public abstract class DataVector implements Identifiable, Serializable {
*/
@Override
public int hashCode() {
if ( id != null ) {
return Objects.hashCode( id );
}
return Objects.hash( expressionExperiment, quantitationType, Arrays.hashCode( data ) );
// also, we cannot hash the ID because it is assigned on creation
// hashing the data is wasteful because subclasses will have a design element to distinguish distinct vectors
return Objects.hash( expressionExperiment, quantitationType );
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ public class DesignElementDataVector extends DataVector {

@Override
public int hashCode() {
if ( getId() != null ) {
return Objects.hash( getId() );
}
return Objects.hash( super.hashCode(), designElement );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

import lombok.Getter;
import lombok.Setter;
import org.springframework.util.Assert;
import ubic.gemma.core.util.ListUtils;
import ubic.gemma.model.common.Identifiable;
import ubic.gemma.model.expression.bioAssay.BioAssay;
import ubic.gemma.persistence.hibernate.ByteArrayType;
import ubic.gemma.persistence.hibernate.CompressedStringListType;
import ubic.gemma.persistence.hibernate.IntArrayType;

import javax.annotation.Nullable;
import javax.persistence.Transient;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;

import static java.util.Collections.unmodifiableList;
import static ubic.gemma.core.util.ListUtils.getSparseRangeArrayElement;

@Getter
Expand All @@ -30,7 +29,7 @@ public class SingleCellDimension implements Identifiable {
* <p>
* This is stored as a compressed, gzipped blob in the database. See {@link CompressedStringListType} for more details.
*/
private List<String> cellIds;
private List<String> cellIds = new ArrayList<>();

/**
* An internal collection for mapping cell IDs to their position in {@link #cellIds}.
Expand All @@ -44,42 +43,54 @@ public class SingleCellDimension implements Identifiable {
* <p>
* This should always be equal to the size of {@link #cellIds}.
*/
private Integer numberOfCells;
private int numberOfCells = 0;

/**
* Cell types, or null if unknown.
* Cell types assignment to individual cells from the {@link #cellTypeLabels} collections.
* <p>
* If supplied, its size must be equal to that of {@link #cellIds}.
*/
@Nullable
private int[] cellTypes;

/**
* Cell type labels, or null if unknown.
* <p>
* Those are user-supplied cell type identifiers. Its size must be equal to that of {@link #cellIds}.
* <p>
* This is stored as a compressed, gzipped blob in the database. See {@link CompressedStringListType} for more details.
*/
@Nullable
private List<String> cellTypes;
private List<String> cellTypeLabels;

/**
* Number of cell types.
* Number of distinct cell types.
* <p>
* This must always be equal to number of distinct elements of {@link #cellTypes}.
*/
@Nullable
private Integer numberOfCellTypes;
private Integer numberOfCellTypeLabels;

/**
* List of bioassays that each cell belongs to.
* <p>
* The {@link BioAssay} {@code bioAssays[i]} applies to all the cells in the interval {@code [bioAssaysOffset[i], bioAssaysOffset[i+1][}.
* To find the bioassay type of a given cell, use {@link #getBioAssay(int)}.
*/
private List<BioAssay> bioAssays;
private List<BioAssay> bioAssays = new ArrayList<>();

/**
* Offsets of the bioassays.
* <p>
* This always contain {@code bioAssays.size()} elements.
* <p>
* This is stored in the database using {@link IntArrayType}.
* This is stored in the database using {@link ByteArrayType}.
*/
private int[] bioAssaysOffset;
private int[] bioAssaysOffset = new int[0];

public List<String> getCellIds() {
return unmodifiableList( cellIds );
}

public void setCellIds( List<String> cellIds ) {
this.cellIds = cellIds;
Expand All @@ -98,14 +109,31 @@ public BioAssay getBioAssay( int index ) {
* Obtain the {@link BioAssay} for a given cell ID.
*/
public BioAssay getBioAssayByCellId( String cellId ) {
return getBioAssay( getCellIndex( cellId ) );
}

public String getCellTypeLabel( int index ) {
Assert.notNull( cellTypes, "No cell types have been assigned." );
Assert.notNull( cellTypeLabels, "No cell labels exist." );
return cellTypeLabels.get( cellTypes[index] );
}

/**
* Obtain a cell type label by cell ID.
*/
public String getCellTypeLabelByCellId( String cellId ) {
return getCellTypeLabel( getCellIndex( cellId ) );
}

private int getCellIndex( String cellId ) {
if ( cellIdToIndex == null ) {
cellIdToIndex = ListUtils.indexOfElements( cellIds );
}
Integer index = cellIdToIndex.get( cellId );
if ( index == null ) {
throw new IllegalArgumentException( "Cell ID not found: " + cellId );
}
return getBioAssay( index );
return index;
}

@Override
Expand All @@ -114,7 +142,7 @@ public int hashCode() {
return Objects.hash( id );
}
// no need to hash numberOfCells, it's derived from cellIds's size
return Objects.hash( cellIds, cellTypes, cellTypes, bioAssays, Arrays.hashCode( bioAssaysOffset ) );
return Objects.hash( cellIds, Arrays.hashCode( cellTypes ), cellTypeLabels, bioAssays, Arrays.hashCode( bioAssaysOffset ) );
}

@Override
Expand All @@ -129,8 +157,14 @@ public boolean equals( Object obj ) {
}
if ( id != null && ( ( SingleCellDimension ) obj ).id != null )
return id.equals( ( ( SingleCellDimension ) obj ).id );
return Objects.equals( cellTypes, scd.cellTypes )
return Objects.equals( cellTypeLabels, scd.cellTypeLabels )
&& Objects.equals( bioAssays, scd.bioAssays )
&& Arrays.equals( cellTypes, scd.cellTypes )
&& Objects.equals( cellIds, scd.cellIds ); // this is the most expensive to compare
}

@Override
public String toString() {
return String.format( "SingleCellDimension %s", id != null ? "Id=" + id : "" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import lombok.Getter;
import lombok.Setter;
import ubic.gemma.persistence.hibernate.IntArrayType;
import ubic.gemma.persistence.hibernate.ByteArrayType;

import java.util.Arrays;
import java.util.Objects;

/**
* An expression data vector that contains data at the resolution of a single cell.
Expand All @@ -26,7 +29,21 @@ public class SingleCellExpressionDataVector extends DesignElementDataVector {
/**
* Positions of the non-zero data in the {@link #getData()} vector.
* <p>
* This is mapped in the database using {@link IntArrayType}.
* This is mapped in the database using {@link ByteArrayType}.
*/
private int[] dataIndices;

@Override
public boolean equals( Object object ) {
if ( this == object ) {
return true;
}
if ( !( object instanceof SingleCellExpressionDataVector ) ) {
return false;
}
SingleCellExpressionDataVector other = ( SingleCellExpressionDataVector ) object;
return super.equals( object )
&& Objects.equals( singleCellDimension, other.singleCellDimension )
&& Arrays.equals( dataIndices, other.dataIndices );
}
}
Loading

0 comments on commit f04ae2f

Please sign in to comment.