Поиск в MongoDB с условием получения только одного результата для каждого атрибута с версией higehst

#spring #mongodb #spring-boot

#spring #mongodb #spring-boot

Вопрос:

Извините за мой вопрос новичка, но я не могу в этом разобраться. Это моя коллекция:

 [
  {
    _id: "A",
    uuid: "12345",
    version: 1,
    test: "data1"
  },
  {
    _id: "B",
    uuid: "56566",
    version: 1,
    test: "data2"
  },
  {
    _id: "C",
    uuid: "12345",
    version: 2,
    test: "data3"
  }
]
  

Я ищу запрос с условием UuidContains и с точным условием.

 findByUuidContains(5)
-> Result: [B,C] as Object Array


findByUuidContains(12345)
-> Result: [C] as Object Array


findByUuidContains(66)
-> Result: [B]

  

Возможен ли такой запрос?
В словах:
Выберите все объекты, uuid которых содержит $ {value}, и из результирующего набора выберите только один для каждого uuid с самой высокой версией.

ПРАВКА1:

Я изменил проекцию группы из ответа:

 db.collection.aggregate([
  {
    "$redact": {
      "$cond": [
        {
          "$gt": [
            {
              "$indexOfCP": [
                {
                  "$toLower": "$uuid"
                },
                "5"
              ]
            },
            -1
          ]
        },
        "$$KEEP",
        "$$PRUNE"
      ]
    }
  },
  {
    $sort: {
      version: -1
    }
  },
  {
    $group: {
      _id: {
        uuid: "$uuid"
      },
      version: {
        $first: "$version"
      },
      id: {
        $first: "$_id"
      },
      test: {
        $first: "$test"
      }
    }
  },
  {
    "$project": {
      num: "1",
      id: 1,
      _id: 0,
      version: 1,
      test: 1
    }
  },
  {
    "$group": {
      "_id": "$num",
      "result": {
        "$addToSet": {
          id: "$id",
          version: "$version",
          test: "$test"
        }
      }
    }
  },
  {
    "$project": {
      _id: 0,
      result: 1
    }
  }
])
  

и я добавил некоторые атрибуты тестовых данных в свои документы. Теперь я должен «перевести» его на «язык» spring boot

ПРАВКА2:

В настоящее время я пытаюсь перевести второй ответ, но я не могу понять, как работает групповая операция в Spring. Кто-нибудь знаком с этим? Первая и вторая операции работают так же, как операции запроса mongo, но не удалось выполнить групповую операцию

         String uuidRegexExp = String.format(".*%s.*", uuidSegment);
        Pattern uuidPattern = Pattern.compile(uuidRegexExp);
        MatchOperation match = new MatchOperation(Criteria.where("uuid").regex(uuidPattern));

        SortOperation sort = Aggregation.sort(Sort.Direction.DESC,"version");
        GroupOperation grup = Aggregation.group("version").first("version").as("version"); 
        Aggregation aggregate = Aggregation.newAggregation(
                match, sort, grup
        );

        AggregationResults<Example> aggregate1 = mongoTemplate.aggregate(aggregate, Example.COLLECTION_NAME, Example.class);

        aggregate1.getMappedResults().forEach(er -> log.info(er.toString()));

   

  

Это пример класса:

 
@Data
@Document(Example.COLLECTION_NAME)
public class Example {

    public static final String COLLECTION_NAME = "Example";
    public static final String FIELD_UUID_NAME = "uuid";
    public static final String FIELD_HOST_NAME = "host";
    public static final String FIELD_URL_NAME = "url";
    public static final String FIELD_VERSION_NAME = "version";
    public static final String FIELD_ID_NAME = "_id";


    @Field(FIELD_ID_NAME)
    private ObjectId _id;

    @Field(FIELD_UUID_NAME)
    private String uuid;

    @Field(FIELD_HOST_NAME)
    private String host;

    @Field(FIELD_URL_NAME)
    private String url;

    @Field(FIELD_VERSION_NAME)
    private Long version;
}
  

ПРАВКА3:

Я думаю, что я это сделал. Вот код в не очень красивой версии:

         String uuidRegexExp = String.format(".*%s.*", uuidSegment);
        Pattern uuidPattern = Pattern.compile(uuidRegexExp);
        MatchOperation match = new MatchOperation(Criteria.where("uuid").regex(uuidPattern));

        SortOperation sort = Aggregation.sort(Sort.Direction.DESC,"version");
        GroupOperation grup = Aggregation.group("uuid").first("version").as("version").first("_id").as("id");
        ProjectionOperation project = Aggregation.project().and("_id").as("uuid").and("version").as("version").and("id").as("_id");
        
        Aggregation aggregate = Aggregation.newAggregation(
                match, sort, grup,project
        );


        AggregationResults<Example> aggregate1 = mongoTemplate.aggregate(aggregate, SingleRawArticle.COLLECTION_NAME, Example.class);
  

Комментарии:

1. Согласно вашей проблеме, findByUuidContains(5) -> Result: [B, C] поскольку массив объектов должен выдавать выходные данные как [A,B, C] , потому что A также содержит 5. Верно?

2. А ваше поле UUID является строковым или двойным?

3. Да, это проблема. Результатом будет [A, B, C] (потому что мой запрос неверен), но он должен быть [B, C] . UUID — это строка

4. Я добавил более короткий скрипт. Проверьте, поможет ли это вам

Ответ №1:

Это то, что вы ищете? Я создал для этого игровую площадку mongo. Вы можете проверить запрос, передав разные параметры. Я использовал 5 в примере, как показано ниже. Но я также пробовал с 12345 and 66 , и для меня это выглядит нормально.

 {
              "$indexOfCP": [
                {
                  "$toLower": "$uuid"
                },
                "5"
              ]
            },
  

Игровая площадка Mongo

Вот запрос :

 db.collection.aggregate([
  {
    "$redact": {
      "$cond": [
        {
          "$gt": [
            {
              "$indexOfCP": [
                {
                  "$toLower": "$uuid"
                },
                "5"
              ]
            },
            -1
          ]
        },
        "$$KEEP",
        "$$PRUNE"
      ]
    }
  },
  {
    $sort: {
      version: -1
    }
  },
  {
    $group: {
      _id: {
        uuid: "$uuid"
      },
      version: {
        $first: "$version"
      },
      id: {
        $first: "$_id"
      }
    }
  },
  {
    "$project": {
      num: "1",
      id: 1,
      _id: 0
    }
  },
  {
    "$group": {
      "_id": "$num",
      "result": {
        "$addToSet": "$id"
      }
    }
  },
  {
    "$project": {
      _id: 0,
      result: 1
    }
  }
])

  

Комментарии:

1. Да! Это не совсем то, что я ищу, потому что мне нужны документы с отверстиями B и C, а не только _id. Я не был ясен в своем сообщении с самого начала. Это отличная работа, спасибо, я попробую и исправлю решение для документа с отверстиями на вашей игровой площадке.

Ответ №2:

проверьте приведенный ниже запрос, чтобы получить документы, соответствующие заданной строке. Я использовал регулярное выражение для сопоставления входной строки.

     db.collection.aggregate(
    [
        { 
            "$match" : { 
                "uuid" : { 
                    "$regex" : ".*5.*"
                }
            }
        }, 
        { 
            "$sort" : { 
                "version" : -1.0
            }
        }, 
        { 
            "$group" : { 
                "_id" : { 
                    "uuid" : "$uuid"
                }, 
                "uuid" : { 
                    "$first" : "$uuid"
                }, 
                "id" : { 
                    "$first" : "$_id"
                }, 
                "version" : { 
                    "$first" : "$version"
                }
            }
        }, 
        { 
            "$project" : { 
                "_id" : "$id", 
                "uuid" : 1.0, 
                "version" : 1.0
            }
        }
    ], 
    { 
        "allowDiskUse" : false
    }
);
  

Вывод:

    { 
    "uuid" : "12345", 
    "version" : 2.0, 
    "_id" : "C"
}
{ 
    "uuid" : "56566", 
    "version" : 1.0, 
    "_id" : "B"
}
  

Комментарии:

1. Ваше решение сопоставляет _id с uuid. Есть ли какая-либо причина, почему?

2. но тогда вам нужно спроецировать как первый ответ, чтобы получить исходный формат, или я ошибаюсь?

3. Да, вам нужно добавить этап проекта. Я изменил предыдущий запрос. Посмотрите. Модифицировал запрос, чтобы получить UUID с группового этапа, а затем спроецировать его в исходном формате

Ответ №3:

Java-код, эквивалентный запросу. Изменил редактирование в соответствии с последними изменениями. Изменены имена переменных, чтобы быть более конкретными.

 String uuidRegexExp = String.format(".*%s.*", uuidSegment);
        MatchOperation match = new MatchOperation(Criteria.where("uuid").regex(Pattern.compile(uuidRegexExp)));

        SortOperation sort = Aggregation.sort(Sort.Direction.DESC,"version");
        GroupOperation group = Aggregation.group("uuid").first("version").as("version").first("_id").as("id").first("uuid").as("uuid");
        ProjectionOperation project = Aggregation.project().and("uuid").as("uuid").and("version").as("version").and("id").as("_id");
        
        Aggregation aggregation = Aggregation.newAggregation(
                match, sort, group,project
        );


        AggregationResults<Example> aggregate = mongoTemplate.aggregate(aggregation, SingleRawArticle.COLLECTION_NAME, Example.class);