How do I recompute an existing document in the backend?

Thread derived from Control fields not triggered

Thank you for your explanation.

I get what happens then.
About the external data, we get them and include them in our IDocument in data-services. Then we call the "update "method from “DocumentService”:

public void updateCandidateDocument(CandidateDataPayload payload) {
	String ext<PROJECT_NAME>UserId = payload.getExt<PROJECT_NAME>UserId();
	log.info("Starting synchronize document from external <PROJECT_NAME> for ext<PROJECT_NAME>UserId: {}", ext<PROJECT_NAME>UserId);
	try {
		runAsUserService.runAsSystemAdmin(() -> {
			DocumentReference candidateDocRef = candidateDocumentService.getDocRefByExternal<PROJECT_NAME>UserId(ext<PROJECT_NAME>UserId);
			if (candidateDocRef == null) {
				return;
			}

			IDocument candidateDocument = <PROJECT_NAME>DocumentService.getDocument(candidateDocRef);

			external<PROJECT_NAME>Convertor.injectExtCandidateDataToCandidateDocument(candidateDocument, payload.getDocument(), payload.getAttachments(), candidateDocRef);

			<PROJECT_NAME>DocumentService.update(candidateDocRef, candidateDocument);
			log.info("Document {} has been successfully updated for external user [{}].", candidateDocRef, ext<PROJECT_NAME>UserId);
		});
	} catch (NullPointerException | NotFoundException | InvalidInputException e) {
		log.error(String.format("Could not update document for external <PROJECT_NAME> user [%s] due to: %s", ext<PROJECT_NAME>UserId, e));
		throw new RuntimeException(e);
	}
}

Our overriden update method:

private final DocumentService documentService;

...

/**
 * @param docRef      {@link DocumentReference}
 * @param newDocument the iDocument we want to update
 */
@Override
public void update(DocumentReference docRef, IDocument newDocument) {
	documentService.update(docRef, newDocument, null);
}

Shouldn’t the update retrigger the computations? Or should I trigger them manually before the update?

I don’t think that data-services automatically re-computes when you update the document.
I guess you have to add this yourself.

	private DataServicesDocument update(DataServicesDocument oldDocument, IDocument document, IDocumentRepository documentRepository, Locale locale) {
		StopWatch stopWatch = StopWatch.createStarted();
		DataServicesDocument updatedDocument = documentBeforeUpdate(oldDocument, document);
		computeAndValidateDocument(updatedDocument.getKernelDocument(), locale);

		documentRepository.update(updatedDocument);
		eventPublisher.publishEvent(new DocumentAfterRepositoryUpdateEvent(oldDocument, updatedDocument));
		searchService.updateIndex(updatedDocument);
		syncAttachments(oldDocument, updatedDocument);

		eventPublisher.publishEvent(new DocumentAfterUpdateEvent(oldDocument.getDocRef(), oldDocument, updatedDocument));
		log.debug("Document [{}] has been updated in [{}] ms", oldDocument.getDocRef(), stopWatch.getTime());
		return updatedDocument;
	}

On third line, the update method is supposed to recompute and validate the document.

Moin @alexis-raw-iron,
did you check if the DocumentService#update method is properly executed while debugging?

If you call DocumentService.update(), the document is computed and validated.
The same for create

How can I explicitly run the calculations in the backend? The classes I’ve found for this (as well as the method called by update) all appear to be A12-internal.

Moin @marcel-calm-dew,
you can use the IDocumentRtService#compute which will compute all fields for a given document.

Data Services provides an autoconfigured bean already for this. Therefore, you can just autowire/inject the IDocumentRtService.

Hello,
As discussed in the topic Control fields not triggered, our condition to hide the field IDNo is this one:


I debugged the code and can confirm the computation happens in the IDocumentRtService.
I also debugged this snippet of code:

IDocumentComputationResult computedDocument = documentRtService.compute(candidateDocument, Locale.ENGLISH);

deveeDocumentService.update(candidateDocRef, candidateDocument);

and added conditional breakpoints to log :

  • The fields used in the computation:
String.format(
"Country: %s - NationalCitizen: %s - EUCitizen: %s", 
candidateDocument.getEntityInstances()
    .stream()
    .filter(ei -> ei.getPath().contains("CountryEmploy"))
    .findFirst()
    .orElseThrow(), 
candidateDocument.getEntityInstances()
    .stream()
    .filter(ei -> ei.getPath().contains("NationalCitizen"))
    .findFirst()
    .orElseThrow(), 
candidateDocument.getEntityInstances()
    .stream()
    .filter(ei -> ei.getPath().contains("EUCitizen"))
    .findFirst()
    .orElseThrow(), 
candidateDocument.getEntityInstances()
    .stream()
    .filter(ei -> ei.getPath().contains("IDNoHideHelpField"))
    .findFirst()
    .orElseThrow()
);
  • The computed field instances:
computedDocument.getComputedFieldInstances();

When I receive my data and want to update my document, here are the logs of the conditional breakpoints:

Country: /Candidate[1]/Contract[1]/CountryEmploy[1]=CZ - NationalCitizen: /Candidate[1]/PersonalData[1]/NationalCitizen[1]=false - EUCitizen: /Candidate[1]/PersonalData[1]/EUCitizen[1]=true, IDNoHideHelpField:/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/IDNoHideHelpField[1]=true

[/Candidate[1]/MetaData[1]/UIDependency[1]/SocialSecurityNoExistHideHelpField[1], 
/Candidate[1]/Contract[1]/Compensation[1]/CurrencyCalc[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/WeekHoursHHMMHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/VacationParttimeHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/OfferLetterDecisionShowHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/MultiEmployTableHideHelpField[1], 
/Candidate[1]/MetaData[1]/Process[1]/LatestOfferSentOnDate[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/DateFromBirthdateHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/YearFromBirthdateHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/OfferDecisionShowHelpField[1], 
/Candidate[1]/Contract[1]/CompanyCalc[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/DeclinedByShowHelpField[1], 
/Candidate[1]/Contract[1]/Organisational[1]/JobDescriptionCalc[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/MonthFromBirthdateHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/DistributionHoursHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HiringForm[1]/DeclineReasonShowHelpField[1], 
/Candidate[1]/PersonalData[1]/GenderCalc[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/PassportHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/HourlySalaryHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/MultiEmployOtherHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/MonthlySalaryHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/WorkPermitShowHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/YearlySalaryAfterPropHideHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/OnboardingForm[1]/OnboardFormSentDecisionShowHelpField[1], 
/Candidate[1]/MetaData[1]/UIDependency[1]/StudentDetailsHideHelpField[1]]

We can see that th IDNoHideHelpField is not recalculated, but it was already true so it should become false. How is that possible?

Moin @alexis-raw-iron,
:information_source: the modeling solution can be found in Control fields not triggered.

If you want to clear your computed field you could do something like:

    @Autowired
    IDocumentRtService documentRtService;
    
    @Autowired
    DocumentService documentService;

    public void clearFieldAndRecalculateComputations(DataServicesDocument document) {
        IDocument kernelDocument = document.getKernelDocument();
        // Remove your computed field
        IEntityInstance instance = kernelDocument.getEntityInstances().stream()
                .filter(ei -> ei.getPath().contains("IDNoHideHelpField"))
                .findFirst().orElseThrow();
        kernelDocument.removeEntityInstance(instance);

        documentRtService.compute(kernelDocument, Locale.ENGLISH);
        
        documentService.update(document.getDocRef(), kernelDocument, Locale.ENGLISH);
    }

Hello,
Thank you for this solution.
I fanally applied the modeling solution because it makes no sense to compute twice in a row, the calculation should work fine from modeling.