Converting a $cond Native query to Java DSL

I have a complex native query which I want to convert to java DSL for future maintainability.

    @Query("{'_id': ?0, 'lockStatus' : false}")
    @Update(pipeline = {"{$set: { counter: {$add: [ '$counter', 1 ] }} }",
            "{$set: { lockStatus: {  $in: ['$counter', ?1 ]} }}",
            "{$set: { lockUpdatedOn : ?2, 'metadata.updatedUuid': ?5, 'metadata.updatedTime': ?2}}",
            "{ $set:{'completedDetail':{$cond:[{$eq:['$lockStatus',true]},{$concatArrays:[{$ifNull:['$completedDetail',[]]},"
                    + "[{'card':?3, 'transactionId': 'NOT SET', 'transNo': '$counter', 'redeemStatus': false,"
                    + " 'metadata': ?4 }]]},'$completedDetail']}}}"
    })

Now the problem is I am not sure how to convert the $cond operation to DSL

I have tried something like this

 Bson filter = Filters.and(Filters.eq(ID, generateCounterId(request)), Filters.eq(LOCK_STATUS, false));

        List<Bson> updates = new ArrayList<>();
        updates.add( Updates.inc(COUNTER, 1));
        updates.add(Updates.set(LOCK_STATUS, Filters.in(COUNTER, getIWTransactionNumbers(request))));
        updates.add(Updates.combine(
                Updates.set(LOCK_UPDATED_ON, new Date()),
                Updates.set(METADATA_UPDATED_UUID, updatedUuid),
                Updates.set(METADATA_UPDATED_TIME, new Date())

        ));

// few more updates for the $cond operation

 collection.updateOne(filter, updates);

Any help will be really appreciated

This is an update as aggregation pipeline, right? You are trying to use regular update builders it looks like - you cannot mix the two, the update either has to use all regular update modifiers, or it has to use all aggregation pipeline syntax (that’s not a Java restriction, that’s an update restriction in general).

Asya

Thanks for the quick reply.

I was trying to build a solution around your suggestion.

I have used below AggregationUpdate logic for converting the Native query to Java DSL

 AggregationExpression aggExpression = context ->
                Document.parse("{'card':'342134', 'transactionId': 'NOT SET', 'rStatus': false }");
     
        AggregationUpdate aggUpdate = AggregationUpdate
                .update()
                .set(COUNTER).toValue(ArithmeticOperators.valueOf(COUNTER).add(1))
                .set(LOCK_STATUS).toValue(ArrayOperators.In.arrayOf(Arrays.asList(1, 2)).containsValue(REF_COUNTER))
                .set(
                        SetOperation.builder()
                                .set(LOCK_UPDATED_ON).toValue(new Date())
                                .and()
                                .set(METADATA_UPDATED_UUID).toValue("updatedUuid")
                                .and()
                                .set(METADATA_UPDATED_TIME).toValue(new Date())
                )
                .set(COMPLETED).toValue(
                        ConditionalOperators
                                .when(ComparisonOperators.valueOf(LOCK_STATUS).equalToValue(true))
                                .then(ArrayOperators.arrayOf(REF_COMPLETED).concat(aggExpression))
                                .otherwiseValueOf(REF_COMPLETED)); 

mongoTemplate
                .update(Counter.class)
                .matching(Query.query(Criteria.where(ID).is(counterId).and(LOCK_STATUS).is(false)))
                .apply(aggUpdate).all();

This looks like the sloution for my problem. But when I am running this I am getting below error:

Write operation error on server localhost:12345. Write error: WriteError{code=28664, message='$concatArrays only supports arrays, not object', details={}}.; nested exception is com.mongodb.MongoWriteException: Write operation error on server localhost:12345. Write error: WriteError{code=28664, message='$concatArrays only supports arrays, not object', details={}}.
org.springframework.dao.DataIntegrityViolationException: Write operation error on server localhost:12345. Write error: WriteError{code=28664, message='$concatArrays only supports arrays, not object', details={}}.; nested exception is com.mongodb.MongoWriteException: Write operation error on server localhost:12345. Write error: WriteError{code=28664, message='$concatArrays only supports arrays, not object', details={}}.
	at app//org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:117)
	at app//org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:3044)

The COMPLETED field is a array [ ] of documents which initially is empty.

I am not sure how to fix it. Any suggestion are highly appreciated.

I would debug this by trying to run the same update in the shell against a sample data - if the error doesn’t appear then the issue is something in how the update is expressed in Java, if you get the same error then some field that’s expected to be an array maybe isn’t?

Asya

This is very generic answer to my query.

Also, the query works in the shell and as native query.

Anyways, was able to progress by updating the code as below:

AggregationExpression aggExpression = context ->
                Document.parse("{'card':'342134', 'transactionId': 'NOT SET', 'rStatus': false }");
     
        AggregationUpdate aggUpdate = AggregationUpdate
                .update()
                .set(COUNTER).toValue(ArithmeticOperators.valueOf(COUNTER).add(1))
                .set(LOCK_STATUS).toValue(ArrayOperators.In.arrayOf(Arrays.asList(1, 2)).containsValue(REF_COUNTER))
                .set(
                        SetOperation.builder()
                                .set(LOCK_UPDATED_ON).toValue(new Date())
                                .and()
                                .set(METADATA_UPDATED_UUID).toValue("updatedUuid")
                                .and()
                                .set(METADATA_UPDATED_TIME).toValue(new Date())
                )
                .set(COMPLETED).toValue(
                        ConditionalOperators
                                .when(ComparisonOperators.valueOf(LOCK_STATUS).equalToValue(true))
                                .then(ArrayOperators.arrayOf(REF_IW_COMPLETED).concat(ObjectOperators.ObjectToArray.valueOfToArray(aggExpression)))
                                .otherwiseValueOf(REF_COMPLETED)); 

mongoTemplate
                .update(Counter.class)
                .matching(Query.query(Criteria.where(ID).is(counterId).and(LOCK_STATUS).is(false)))
                .apply(aggUpdate).all();

This fix the above problem of

'$concatArrays only supports arrays, not object'

But now I am getting another problem, in the field COMPLETED array, now four new entries are getting created with default values.

Now sure about why?