@Override
 public void removeAssociation(AssociationKey key, AssociationContext associationContext) {
   if (isStoredInEntityStructure(key, associationContext)) {
     EntityDocument owningEntity =
         getDataStore().getEntity(Identifier.createEntityId(key.getEntityKey()));
     if (owningEntity != null) {
       owningEntity.removeAssociation(key.getCollectionRole());
       getDataStore().saveDocument(owningEntity);
     }
   } else {
     removeDocumentIfPresent(Identifier.createAssociationId(key));
   }
 }
  @Override
  public boolean isStoredInEntityStructure(
      AssociationKey associationKey, AssociationContext associationContext) {
    AssociationStorageType associationStorage =
        associationContext.getOptionsContext().getUnique(AssociationStorageOption.class);

    if (associationStorage == null) {
      associationStorage = provider.getDefaultAssociationStorageStrategy();
    }

    return associationKey.getAssociationKind() == AssociationKind.EMBEDDED_COLLECTION
        || associationStorage == AssociationStorageType.IN_ENTITY;
  }
  @Override
  public Association createAssociation(AssociationKey key, AssociationContext associationContext) {
    CouchDBAssociation couchDBAssociation = null;

    if (isStoredInEntityStructure(key, associationContext)) {
      EntityDocument owningEntity =
          getDataStore().getEntity(Identifier.createEntityId(key.getEntityKey()));
      if (owningEntity == null) {
        owningEntity =
            (EntityDocument) getDataStore().saveDocument(new EntityDocument(key.getEntityKey()));
      }

      couchDBAssociation =
          CouchDBAssociation.fromEmbeddedAssociation(owningEntity, key.getCollectionRole());
    } else {
      AssociationDocument association =
          new AssociationDocument(Identifier.createAssociationId(key));
      couchDBAssociation = CouchDBAssociation.fromAssociationDocument(association);
    }

    return new Association(new CouchDBAssociationSnapshot(couchDBAssociation, key));
  }
  /**
   * @param document DBObject containing the association information
   * @param key
   */
  public MongoDBAssociationSnapshot(
      DBObject document, AssociationKey key, AssociationStorage storage) {
    this.storage = storage;
    this.dbObject = document;
    this.map = new LinkedHashMap<RowKey, DBObject>();
    this.associationKey = key;
    // for each element in the association property
    for (DBObject row : getRows()) {
      DBObject mongodbColumnData = row;
      Collection<String> columnNames = Arrays.asList(key.getRowKeyColumnNames());

      // build data to construct the associated RowKey is column names and values
      List<Object> columnValues = new ArrayList<Object>();
      for (String columnKey : columnNames) {
        boolean getFromMongoData = true;
        int length = key.getColumnNames().length;
        // try and find the value in the key metadata
        for (int index = 0; index < length; index++) {
          String assocColumn = key.getColumnNames()[index];
          if (assocColumn.equals(columnKey)) {
            columnValues.add(associationKey.getColumnValues()[index]);
            getFromMongoData = false;
            break;
          }
        }
        // otherwise read it from the database structure
        if (getFromMongoData == true) {
          columnValues.add(mongodbColumnData.get(columnKey));
        }
      }
      RowKey rowKey =
          new RowKey(
              key.getTable(),
              columnNames.toArray(new String[columnNames.size()]),
              columnValues.toArray());
      // Stock database structure per RowKey
      this.map.put(rowKey, row);
    }
  }
  @Override
  public Association getAssociation(AssociationKey key, AssociationContext associationContext) {
    CouchDBAssociation couchDBAssociation = null;

    if (isStoredInEntityStructure(key, associationContext)) {
      EntityDocument owningEntity =
          getDataStore().getEntity(Identifier.createEntityId(key.getEntityKey()));
      if (owningEntity != null
          && owningEntity.getProperties().containsKey(key.getCollectionRole())) {
        couchDBAssociation =
            CouchDBAssociation.fromEmbeddedAssociation(owningEntity, key.getCollectionRole());
      }
    } else {
      AssociationDocument association =
          getDataStore().getAssociation(Identifier.createAssociationId(key));
      if (association != null) {
        couchDBAssociation = CouchDBAssociation.fromAssociationDocument(association);
      }
    }

    return couchDBAssociation != null
        ? new Association(new CouchDBAssociationSnapshot(couchDBAssociation, key))
        : null;
  }
  private List<Map<String, Object>> getAssociationRows(
      Association association, AssociationKey associationKey) {
    List<Map<String, Object>> rows = new ArrayList<Map<String, Object>>();

    for (RowKey rowKey : association.getKeys()) {
      Tuple tuple = association.get(rowKey);

      Map<String, Object> row = new HashMap<String, Object>(3);
      for (String columnName : tuple.getColumnNames()) {
        // don't store columns which are part of the association key and can be retrieved from there
        if (!associationKey.isKeyColumn(columnName)) {
          row.put(columnName, tuple.get(columnName));
        }
      }

      rows.add(row);
    }
    return rows;
  }
  private AssociationKey getCollectionMetadataKey() {
    if (collectionMetadataKey == null) {
      final Object[] columnValues = getKeyColumnValues();
      collectionMetadataKey = new AssociationKey(tableName, keyColumnNames, columnValues);
      // We have a collection on the main side
      if (collectionPersister != null) {
        EntityKey entityKey;
        // we are explicitly looking to update the non owning side
        if (inverse) {
          // look for the other side of the collection, build the key of the other side's entity
          OgmEntityPersister elementPersister =
              (OgmEntityPersister) collectionPersister.getElementPersister();
          entityKey = EntityKeyBuilder.fromPersister(elementPersister, (Serializable) key, session);
          collectionMetadataKey.setCollectionRole(buildCollectionRole(collectionPersister));
        } else {
          // we are on the right side, use the association property
          collectionMetadataKey.setCollectionRole(getUnqualifiedRole(collectionPersister));
          entityKey =
              EntityKeyBuilder.fromPersister(
                  (OgmEntityPersister) collectionPersister.getOwnerEntityPersister(),
                  (Serializable) key,
                  session);
        }
        collectionMetadataKey.setOwnerEntityKey(entityKey);
        // TODO add information on the collection type, set, map, bag, list etc

        AssociationKind type =
            collectionPersister.getElementType().isEntityType()
                ? AssociationKind.ASSOCIATION
                : AssociationKind.EMBEDDED;
        collectionMetadataKey.setAssociationKind(type);
        collectionMetadataKey.setRowKeyColumnNames(collectionPersister.getRowKeyColumnNames());
      }
      // We have a to-one on the main side
      else if (propertyType != null) {
        collectionMetadataKey.setAssociationKind(
            propertyType.isEntityType() ? AssociationKind.ASSOCIATION : AssociationKind.EMBEDDED);
        if (propertyType instanceof EntityType) {
          EntityType entityType = (EntityType) propertyType;
          OgmEntityPersister associatedPersister =
              (OgmEntityPersister) entityType.getAssociatedJoinable(session.getFactory());
          EntityKey entityKey =
              new EntityKey(
                  associatedPersister.getTableName(),
                  associatedPersister.getIdentifierColumnNames(),
                  columnValues);
          collectionMetadataKey.setOwnerEntityKey(entityKey);
          collectionMetadataKey.setRowKeyColumnNames(rowKeyColumnNames);
          collectionMetadataKey.setCollectionRole(getCollectionRoleFromToOne(associatedPersister));
        } else {
          throw new AssertionFailure(
              "Cannot detect associated entity metadata. propertyType is of unexpected type: "
                  + propertyType.getClass());
        }
      } else {
        throw new AssertionFailure(
            "Cannot detect associated entity metadata: collectionPersister and propertyType are both null");
      }
    }
    return collectionMetadataKey;
  }