ContentFullTextWriter.java
package net.sasasin.sreader.service.extraction;
import java.util.Objects;
import java.util.Optional;
import net.sasasin.sreader.domain.ContentFullText;
import net.sasasin.sreader.domain.ContentHeader;
import net.sasasin.sreader.domain.FullTextMethod;
import net.sasasin.sreader.repository.ContentFullTextRepository;
import net.sasasin.sreader.service.article.HashIds;
import org.springframework.stereotype.Service;
@Service
public class ContentFullTextWriter {
private final ContentFullTextRepository repository;
public ContentFullTextWriter(ContentFullTextRepository repository) {
this.repository = repository;
}
/**
* Feed-entry convenience: method {@code feed}, source {@code feed}, extracted URL = header
* canonical URL. AutoPagerize columns remain null.
*/
public ContentFullTextWriteOutcome saveIfAbsent(ContentHeader header, String fullText) {
if (fullText == null || fullText.isBlank()) {
return ContentFullTextWriteOutcome.NO_CONTENT;
}
TextExtractionOutcome.Extracted extracted =
new TextExtractionOutcome.Extracted(
fullText,
ExtractionDecision.of(ExtractionSource.FEED),
Optional.empty(),
Optional.of(header.canonicalUrl()));
return saveIfAbsent(header, FullTextMethod.FEED, extracted);
}
/**
* Persist a successful extraction with method, source kind, extracted URL, and optional
* AutoPagerize columns. Never inserts failure or partial rows. Callers must pass non-blank
* extracted text ({@link TextExtractionOutcome.Extracted} already enforces this).
*/
public ContentFullTextWriteOutcome saveIfAbsent(
ContentHeader header, FullTextMethod method, TextExtractionOutcome.Extracted extracted) {
Objects.requireNonNull(header, "header must not be null");
Objects.requireNonNull(method, "method must not be null");
Objects.requireNonNull(extracted, "extracted must not be null");
String id = HashIds.md5(header.canonicalUrl());
String extractedUrl = extracted.extractedUrl().orElse(header.fetchUrl());
String sourceKind = extracted.decision().source().wireValue();
ContentFullText row;
Optional<PaginationMetadata> pagination = extracted.pagination();
if (pagination.isEmpty()) {
row =
ContentFullText.success(
id, header.id(), extracted.text(), method.value(), sourceKind, extractedUrl);
} else {
PaginationMetadata meta = pagination.get();
// Success rows always store page_count >= 1 (schema check).
int pageCount = meta.pageCount();
if (pageCount < 1) {
pageCount = 1;
}
row =
ContentFullText.successAutopagerize(
id,
header.id(),
extracted.text(),
method.value(),
sourceKind,
extractedUrl,
meta.datasetId(),
meta.ruleOrdinal(),
pageCount,
meta.stopReason().name(),
meta.complete());
}
boolean inserted = repository.insertIfAbsent(row);
if (inserted) {
return ContentFullTextWriteOutcome.INSERTED;
}
return ContentFullTextWriteOutcome.ALREADY_EXISTS;
}
}