Search code examples
mongodbspring-dataspring-data-mongodb

Combine two fields in one and sort by that new combined field


I have the following Document (... means more properties and getters and setters)

@Document
public class Equipment
{
...
private String name;
private String internalManufacturer;
private String externalManufacturer;
...
}

And this method in a service, where the name of field is passed (code) and the sorting is set.

private void setSorting(String code)
{
    ...
    List<AggregationOperation> pagedAndSortedQuery= new ArrayList<>(baseQuery);
    SortOperation sortOperation = sort(Sort.Direction.ASC, code);
    if (EquipmentsSortingField.MANUFACTURER.getCode().equals(code))
    {
        sortOperation = sort(sortingDirection, "internalManufacturer", "externalManufacturer");
        pagedAndSortedQuery.add(sortOperation);
    }
}

I want to sort internalManufacturer and externalManufacturer as if they were one field, because if I do it as in the service, first I get the result sorted internalManufacturers and them sorted externalManufacturers. Important, if externalManufacturer is empty, internalManufacturer is not and viceversa.

For example, I have this in database:

Equipment (externalManufacturer: John, internalManufacturer: null)
Equipment (externalManufacturer: null, internalManufacturer: Carlos)
Equipment (externalManufacturer: Ash, internalManufacturer: null)
Equipment (externalManufacturer: null, internalManufacturer: Victor)

With my code I get the equipments sorted like this:

Equipment (externalManufacturer: null, internalManufacturer: Carlos)
Equipment (externalManufacturer: null, internalManufacturer: Victor)
Equipment (externalManufacturer: Ash, internalManufacturer: null)
Equipment (externalManufacturer: John, internalManufacturer: null)

And I like to get them like this:

Equipment (externalManufacturer: Ash, internalManufacturer: null)
Equipment (externalManufacturer: null, internalManufacturer: Carlos)
Equipment (externalManufacturer: John, internalManufacturer: null)
Equipment (externalManufacturer: null, internalManufacturer: Victor)

In summary, sort both fields as if it was only one field.

How can I do that? Thanks in advance.


Solution

  • You should generate a new field using $addFields, which is the concatenated version of the internal and external manufacturer. Then use this new field for sorting. Something along these lines:

    private void setSorting(String code)
    {
        ...
        List<AggregationOperation> pagedAndSortedQuery= new ArrayList<>(baseQuery);
        SortOperation sortOperation = sort(Sort.Direction.ASC, code);
        if (EquipmentsSortingField.MANUFACTURER.getCode().equals(code))
        {
            AddFieldsOperation addFieldsOperation =
                Aggregation.addFields().addFieldWithValue(
                        "manufacturerString",
                        StringOperators.Concat.valueOf(ConditionalOperators.ifNull("internalManufacturer").then("")).concatValueOf(ConditionalOperators.ifNull("externalManufacturer").then(""))
                ).build();
            sortOperation = sort(sortingDirection, "manufacturerString");
            pagedAnsSortedQuery.add(addFieldsOperation);
            pagedAndSortedQuery.add(sortOperation);
        }
    }
    

    Note that I have used the new field for sorting, and also added the addField stage to the pipeline. Please test it out, I hope you get the idea.