[FIXED] Rufen Sie eine gefilterte Liste von Objekten ab, die in einem MongoDB-Dokument definiert sind

Ausgabe

Ich arbeite mit Spring Boot und MongoDB. Ich suche nach einer Lösung, um eine Liste von Hobbys zu erhalten, aber nicht alle. Die Hobbys müssen die Bedingungen erfüllen (Name oder Beschreibung müssen Suchwort enthalten).

@Document("persons")
@Data
class Person {
    @Id
    private String personId;
    private String name;
    private List<Hobby> hobbies;
}
@Data
class Hobby {
    private String name;
    private String description;
    private String notImportantField;
}

Beispiel

Ich möchte eine Person mit einer reduzierten Liste von Hobbys erhalten (alle Hobbys müssen die gesuchte Phrase in einem ihrer Felder enthalten).

Personendokument aus der Datenbank

{
    "_id" : ObjectId("id1"),
    "name" : "some person",
    "hobbies" : [
        {
            "name" : "A",
            "description" : "AB",
            "notImportantField" : "ABCDEF"
        },
        {
            "name" : "ABC",
            "description" : "ABCD",
            "notImportantField" : "ABCDEF"
        }
    ]
}

Was ich erhalten möchte:

  • Ich möchte eine Person mit ID id1und suche nach Phrasen abin den Hobbys der Person. Ich sollte eine Liste mit 2 Hobbys erhalten (Beschreibung des ersten Hobbys enthält ab, Name und Beschreibung des zweiten Hobbys enthält ab)
  • Ich möchte eine Person mit ID id1und suche nach Phrasen din den Hobbys der Person. Ich sollte eine Liste mit 1 Hobby erhalten (die Beschreibung des zweiten Hobbys enthält d)

Ich habe so etwas versucht, aber ich bekomme eine Person mit all ihren Hobbys.

@Repository
interface PersonRepository extends MongoRepository<Person, String> {
    @Query("{'$and': [" +
                "{'_id': :#{#personId}}," +
                "{'$or':[" +
                    "{'hobbies.name': {$regex: :#{#searchPhraseRegex}, $options: 'i'}}," +
                    "{'hobbies.description': {$regex: :#{#searchPhraseRegex}, $options: 'i'}}" +
                "]}" +
            "]}")
    List<Person> method(@Param("personId") String personId, @Param("searchPhraseRegex") String searchPhraseRegex);
}

Die Ergebnismethode sollte eine Person mit gefilterten Hobbys oder nur eine Liste von Hobbys zurückgeben. Vielen Dank im Voraus für Hilfe.

UPDATE: BEHOBEN

Danke @user20042973 für die Hilfe 🙂 Ich habe Ihre Abfrage verwendet und sie ein wenig geändert, damit sie mit meiner Abfrage im Mongo-Repository übereinstimmt. Es funktioniert so, wie ich es erwartet habe. Die Ergebnismethode lautet:

@Repository
interface PersonRepository extends MongoRepository<Person, String> {
    @Aggregation(pipeline = {
            "{'$match': {'_id': :#{#personId}}}",
            "{'$addFields':  {'hobbies': {'$filter': {" +
                    "'input': '$hobbies', " +
                    "'cond': " +
                        "{'$or': [" +
                            "{'$regexMatch': {'input': '$$this.name', 'regex': :#{#searchPhraseRegex}, 'options': 'i' }}," +
                            "{'$regexMatch': {'input': '$$this.description', 'regex': :#{#searchPhraseRegex}, 'options': 'i' }}" +
                        "]}" +
                    "}}}}"
    })
    Optional<Person> findPersonByIdAndFilterHobbies(@Param("personId") String personId, @Param("searchPhraseRegex") String searchPhraseRegex);
}

Hinweis: Für die Suche abmüssen wir zB .*ab.*als Methodenargument übergeben searchPhraseRegex.

Lösung

Helpfully, getting a filtered list of objects from an array can be done using the $filter operator. The operator takes an array as input and can process each item individually via an expression. In your situation that expression would include a $regexMatch to look for the value in the string(s) of interest. So the stage would look something like this:

  {
    "$addFields": {
      hobbies: {
        $filter: {
          input: "$hobbies",
          cond: {
            $or: [
              {
                $regexMatch: {
                  input: "$$this.name",
                  regex: "d",
                  options: "i"
                }
              },
              {
                $regexMatch: {
                  input: "$$this.description",
                  regex: "d",
                  options: "i"
                }
              }
            ]
          }
        }
      }
    }
  }

Sample Mongo Playground example is here.

From your question it’s not quite clear if you will separately be using this as selection criteria for the document itself. I’ve left the leading $match stage to just use _id for the moment, but of course you can add any other filters to it (such as 'hobbies.name': /d/i) as needed.

Ich bin persönlich nicht vertraut genug mit Spring Boot, um zu sagen, wie die genaue Syntax zum Erstellen einer solchen Pipeline lautet, aber ich weiß, dass Aggregationen unterstützt werden.


Beantwortet von –
user20042973


Antwort geprüft von –
Jay B. (FixError Admin)

0 Shares:
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like