0

So I have a collection like so:

{
    id: ...,
    description: {
        "english": ...,  // The values here are just strings
        "spanish": ...,
        ...
    },
    ...
}

And I want to query for documents where any of the description's values match a given input. I know you can query on a specific one like so:

Criteria.where("description.english").is("Test")

but I want to check all fields, something like this (which doesn't work):

Criteria.where("description.*").is("Test")

I also don't see a way to get elemMatch to work as the value is just a string and not a document, and there isn't a predefined list of valid keys (these can be "translated" into any language).

What am I missing?


I have seen this answer, but unfortunately I cannot change the collection.

Ruckus T-Boom
  • 4,566
  • 1
  • 28
  • 42

1 Answers1

1

You can use AggregationExpression($map operator) & AggregationOperation($redact stage) to build below aggregation in 3.4.

$objectToArray to convert the description into key value pairs and $map string values and use $in to check if test is in array of values, if matched $$KEEP else $$PRUNE document inside $redact stage.

AggregationExpression mapExpression = aggregationOperationContext -> {
    Map<String, Object> map = new LinkedHashMap<>();
    map.put("input", new BasicDBObject("$objectToArray", "$description"));
    map.put("as", "result");
    map.put("in", "$$result.v");
    return new BasicDBObject("$map", map);
};

AggregationOperation redactOperation = aggregationOperationContext -> {
    Map<String, Object> map = new LinkedHashMap<>();
    map.put("if", new BasicDBObject("$in", Arrays.asList("test", mapExpression.toDbObject(Aggregation.DEFAULT_CONTEXT))));
    map.put("then", "$$KEEP");
    map.put("else", "$$PRUNE");
    return new BasicDBObject("$redact", new BasicDBObject("$cond", map));
};

Aggregation aggregation= Aggregation.newAggregation(redactOperation);
AggregationResults<BasicDBObject> result =  mongoTemplate.aggregate(Aggregation.newAggregation(redactOperation),collection_name, BasicDBObject.class);

Shell Query for your reference:

db.collection_name.aggregate([
  {
    "$redact": {
      "$cond": {
        "if": {
          "$in": [
            "test",
            {
              "$map": {
                "input": {
                  "$objectToArray": "$description"
                },
                "as": "result",
                "in": "$$result.v"
              }
            }
          ]
        },
        "then": "$$KEEP",
        "else": "$$PRUNE"
      }
    }
  }
])
s7vr
  • 73,656
  • 11
  • 106
  • 127