diff --git a/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt index d06735ff..321fd94e 100644 --- a/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/offer/PostgresOfferRepositoryImpl.kt @@ -21,8 +21,10 @@ import org.springframework.data.domain.SliceImpl import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Component import java.math.BigInteger +import java.text.SimpleDateFormat import java.util.HashMap import javax.persistence.EntityManager +import org.springframework.transaction.annotation.Transactional import kotlin.system.measureTimeMillis @Component @@ -44,9 +46,106 @@ class PostgresOfferRepositoryImpl( return result } - + @Transactional override fun saveAll(offers: List): List { - val result = repository.saveAll(offers).toList() + var result: List = listOf() + val values = offers.map { + val isoPattern = "yyyy-MM-dd'T'HH:mm:ss'Z'" + val updatedAt = SimpleDateFormat(isoPattern).format(it.updatedAt) + val createdAt = SimpleDateFormat(isoPattern).format(it.createdAt) + val id = if (it.id == 0L) "nextval('offer_id_seq')" else it.id.toString() + + "($id, " + + "'${it.title.replace("'", "''")}'," + + " '${it.description.replace("'", "''")}'," + + " '${it.owner.replace("'", "''")}', " + + "'${it.imageUrl}', '${it.worth}', '$createdAt', '$updatedAt')" + } + if (values.isNotEmpty()) { + val insertOffers = "INSERT INTO offer " + + "(id, title, description, owner, image_url, worth, created_at, updated_at) VALUES \n" + val insertOffersQuery = insertOffers + values.joinToString(",\n") + + "\n ON CONFLICT (id) DO UPDATE SET\n" + + " title = EXCLUDED.title,\n" + + " description = EXCLUDED.description,\n" + + " owner = EXCLUDED.owner,\n" + + " image_url = EXCLUDED.image_url,\n" + + " worth = EXCLUDED.worth,\n" + + " updated_at = EXCLUDED.updated_at\n" + + "RETURNING id;" + + @Suppress("UNCHECKED_CAST") + val insertedOfferIds: List = entityManager + .createNativeQuery(insertOffersQuery) + .resultList as List + + // cleanup tags for the all offers in the request + val offerInByIds = insertedOfferIds.joinToString(", ") + val cleanUpTagsQuery = "DELETE FROM offer_tags\n" + + "WHERE offer_tags.offer_id IN ($offerInByIds);" + entityManager.createNativeQuery(cleanUpTagsQuery).executeUpdate() + + val insertTags = "INSERT INTO offer_tags (offer_id, tags, tags_key) VALUES \n" + val insertedOfferTagsValues = offers.mapIndexed { index, offer -> + offer.tags.map { "( ${insertedOfferIds[index]}, " + + "'${it.value.replace("'", "''")}', " + + "'${it.key.replace("'", "''")}' )" } + }.flatten().joinToString(",\n") + if (insertedOfferTagsValues.isNotEmpty()) { + val ifConflictPartTagsQuery = "\nON CONFLICT ON CONSTRAINT offer_tags_pkey DO UPDATE SET\n" + + " tags_key = EXCLUDED.tags_key,\n" + + " tags = EXCLUDED.tags" + val insertOfferTagsQuery = insertTags + insertedOfferTagsValues + ifConflictPartTagsQuery + entityManager.createNativeQuery(insertOfferTagsQuery).executeUpdate() + } + + // cleanup compare for the all offers in the request + val cleanUpCompareQuery = "DELETE FROM offer_compare\n" + + "WHERE offer_compare.offer_id IN ($offerInByIds);" + entityManager.createNativeQuery(cleanUpCompareQuery).executeUpdate() + + val insertCompare = "INSERT INTO offer_compare (offer_id, compare, compare_key) VALUES \n" + val insertedOfferCompareValues = offers.mapIndexed { index, offer -> + offer.compare.map { "( ${insertedOfferIds[index]}, " + + "'${it.value.replace("'", "''")}', " + + "'${it.key.replace("'", "''")}' )" } + }.flatten().joinToString(",\n") + if (insertedOfferCompareValues.isNotEmpty()) { + val ifConflictOfferCompare = "\n ON CONFLICT ON CONSTRAINT offer_compare_pkey " + + "DO UPDATE SET\n" + + " compare = EXCLUDED.compare,\n" + + " compare_key = EXCLUDED.compare_key" + val offerCompareQuery = insertCompare + insertedOfferCompareValues + ifConflictOfferCompare + entityManager.createNativeQuery(offerCompareQuery).executeUpdate() + } + + // cleanup rules for the all offers in the request + val cleanUpRulesQuery = "DELETE FROM offer_rules\n" + + "WHERE offer_rules.offer_id IN ($offerInByIds);" + entityManager.createNativeQuery(cleanUpRulesQuery).executeUpdate() + + val insertRules = "INSERT INTO offer_rules (offer_id, rules, rules_key) VALUES \n" + val insertedOfferRulesValues = offers.mapIndexed { index, offer -> + offer.rules.map { "( ${insertedOfferIds[index]}, ${it.value.ordinal}, " + + "'${it.key.replace("'", "''")}' )" } + }.flatten().joinToString(",\n") + if (insertedOfferRulesValues.isNotEmpty()) { + val ifConflictOfferRules = "\n ON CONFLICT ON CONSTRAINT offer_rules_pkey " + + "DO UPDATE SET\n" + + " rules = EXCLUDED.rules,\n" + + " rules_key = EXCLUDED.rules_key" + val insertOfferRulesQuery = insertRules + insertedOfferRulesValues + ifConflictOfferRules + entityManager.createNativeQuery(insertOfferRulesQuery).executeUpdate() + } + + // for proper return data purpose only + val ids = insertedOfferIds.joinToString(", ") + val query = "SELECT * FROM offer WHERE offer.id IN ($ids)" + @Suppress("UNCHECKED_CAST") + val wrongSortedResult = entityManager.createNativeQuery(query, Offer::class.java).resultList as List + val wrongSortedResultAsMap = wrongSortedResult.map { it.id to it }.toMap() + result = insertedOfferIds.map { wrongSortedResultAsMap[it] ?: error("was not find bt ids") } + } return syncElementCollections(result) } @@ -68,7 +167,7 @@ class PostgresOfferRepositoryImpl( override fun deleteOffers(owner: String): Int { val deletedOffers = repository.deleteByOwner(owner) deletedOffers.forEach { - wsService.sendEvent(OfferEvent.OnDelete, it.id) +// wsService.sendEvent(OfferEvent.OnDelete, it.id) } return deletedOffers.size diff --git a/src/main/kotlin/com/bitclave/node/repository/price/PostgresOfferPriceRepositoryImpl.kt b/src/main/kotlin/com/bitclave/node/repository/price/PostgresOfferPriceRepositoryImpl.kt index 62a9e20e..d86265a6 100644 --- a/src/main/kotlin/com/bitclave/node/repository/price/PostgresOfferPriceRepositoryImpl.kt +++ b/src/main/kotlin/com/bitclave/node/repository/price/PostgresOfferPriceRepositoryImpl.kt @@ -7,6 +7,7 @@ import com.bitclave.node.repository.priceRule.OfferPriceRulesCrudRepository import com.bitclave.node.services.errors.DataNotSavedException import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional @Component @Qualifier("postgres") @@ -14,7 +15,10 @@ class PostgresOfferPriceRepositoryImpl( val repository: OfferPriceCrudRepository, val rulesRepository: OfferPriceRulesCrudRepository ) : OfferPriceRepository { + + @Transactional override fun saveAllPrices(prices: List): List { + val savedPrices = repository.saveAll(prices) val rules = savedPrices.mapIndexed { index, offerPrice -> diff --git a/src/main/kotlin/com/bitclave/node/services/v1/OfferService.kt b/src/main/kotlin/com/bitclave/node/services/v1/OfferService.kt index 9b820632..56837841 100644 --- a/src/main/kotlin/com/bitclave/node/services/v1/OfferService.kt +++ b/src/main/kotlin/com/bitclave/node/services/v1/OfferService.kt @@ -141,7 +141,7 @@ class OfferService( val saveAllTiming = measureTimeMillis { result = offerRepository.changeStrategy(strategy).saveAll(readyForSaveOffers) } -// logger.debug(" - save all timing $saveAllTiming") +// Logger.debug(" - save all timing $saveAllTiming") val prices = result.mapIndexed { index, offer -> readyForSaveOffers[index].offerPrices.map { offerPrice -> @@ -152,9 +152,10 @@ class OfferService( }.flatten() val saveAllPricesTiming = measureTimeMillis { + val ids = result.map { it.id } offerPriceRepository.changeStrategy(strategy).saveAllPrices(prices) } -// logger.debug(" - save all prices timing $saveAllPricesTiming") +// Logger.debug(" - save all prices timing $saveAllPricesTiming") val offersIdsForCleanupOfferSearches = offers.filter { it.id != 0L }.map { it.id } val deleteOfferSearchTiming = measureTimeMillis { @@ -162,7 +163,7 @@ class OfferService( offerSearchService.deleteByOfferIds(offersIdsForCleanupOfferSearches, strategy) } } -// logger.debug(" - delete all offer searches by offerId timing $deleteOfferSearchTiming") +// Logger.debug(" - delete all offer searches by offerId timing $deleteOfferSearchTiming") result.map { it.id } })