Пружинная резинка : Как правильно использовать вложенные поля?

0

Вопрос

Я совсем новичок в ElasticSearch. Я работаю над проектом, в котором нам нужно найти объект (Предложение), содержащий набор из двух (перевод предложения). Цель состоит в том, чтобы провести исследование на основе некоторых полей предложений, а также множества полей перевода предложений. Вот уменьшенная версия обоих классов :

Offer.class (обратите внимание, что я аннотировал набор с помощью @Field(тип= FieldType.Nested), чтобы я мог выполнять вложенные запросы, как указано в официальном документе) :

@org.springframework.data.elasticsearch.annotations.Document(indexName = "offer")
@DynamicMapping(DynamicMappingValue.False)
public class Offer implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Field(type = FieldType.Long)
    private Long id;

    @OneToMany(mappedBy = "offer", targetEntity = OfferTranslation.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    @JsonIgnoreProperties(
        value = { "pictures", "videos", "owner", "contexts", "offer", "personOfInterests", "followers" },
        allowSetters = true
    )
    @Field(type = FieldType.Nested, store = true)
    private Set<OfferTranslation> offersTranslations = new HashSet<>();


}

OfferTranslation.class :

@DynamicMapping(DynamicMappingValue.False)
public class OfferTranslation implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @Field(type = FieldType.Long)
    private Long id;

    @NotNull
    @Size(max = 100)
    @Column(name = "title", length = 100, nullable = false)
    @Field(type = FieldType.Text)
    private String title;

    @NotNull
    @Size(max = 2000)
    @Column(name = "summary", length = 2000, nullable = false)
    @Field(type = FieldType.Text)
    private String summary;

    @Size(max = 2000)
    @Column(name = "competitor_context", length = 2000)
    @Field(type = FieldType.Text)
    private String competitorContext;

    @NotNull
    @Size(max = 2000)
    @Column(name = "description", length = 2000, nullable = false)
    @Field(type = FieldType.Text)
    private String description;

    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(name = "maturity", nullable = false)
    @Field(type = FieldType.Auto)
    private RefMaturity maturity;

    @ManyToOne
    @Field(type = FieldType.Object, store = true)
    private RefLanguage language;

    @NotNull
    @Column(name = "created_at", nullable = false)
    @Field(type = FieldType.Date)
    private Instant createdAt;
}

Ожидаемое поведение будет заключаться в том, что я могу создавать вложенные запросы как таковые :

QueryBuilder qBuilder = nestedQuery("offersTranslations",boolQuery().must(termQuery("offersTranslations.language.code",language)), ScoreMode.None);

Но я получаю исключение : не удалось создать запрос: [вложенный] вложенный объект по пути [Предложения] не имеет вложенного типа"

РЕДАКТИРОВАТЬ : Я могу получить доступ к предложениям.язык.код с использованием обычных запросов (что на самом деле меня сейчас не беспокоит). Но я все еще не совсем понимаю.

Мое сопоставление говорит, что поля, предлагающие переводы, не имеют вложенного типа, как вы можете видеть выше, но поскольку я использовал @Field(тип = FieldType.Nested), я действительно не понимаю такого поведения. Может кто-нибудь объяснить?

{
  "offer" : {
    "mappings" : {
      "properties" : {
        "_class" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "categories" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "criteria" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "id" : {
          "type" : "long"
        },
        "keywords" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        },
        "offersTranslations" : {
          "properties" : {
            "competitorContext" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "createdAt" : {
              "type" : "date"
            },
            "description" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "id" : {
              "type" : "long"
            },
            "language" : {
              "properties" : {
                "code" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                },
                "id" : {
                  "type" : "long"
                },
                "name" : {
                  "type" : "text",
                  "fields" : {
                    "keyword" : {
                      "type" : "keyword",
                      "ignore_above" : 256
                    }
                  }
                }
              }
            },
            "maturity" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "state" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "summary" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "title" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "updatedAt" : {
              "type" : "date"
            }
          }
        },
        "units" : {
          "properties" : {
            "id" : {
              "type" : "long"
            }
          }
        }
      }
    }
  }
}
2

Лучший ответ

1

Как было создано сопоставление индексов? Это не похоже на то, что Spring Data Elasticsearch написал это сопоставление. Вложенный тип offerTranslations отсутствует, как вы видели, и текстовые поля имеют .keyword подполе. Это выглядит так, как будто данные были вставлены в индекс без определенного сопоставления, и поэтому Elasticsearch выполнил автоматическое сопоставление. В этом случае значения @Field аннотации не используются.

Вам нужно, чтобы Spring Data Elasticsearch создал индекс с сопоставлением. Это произойдет автоматически, если индекс не существует, и вы используете репозитории Spring Data Elasticsearch, или вам необходимо использовать IndexOperations.createWithMapping функция в вашем приложении.

Еще одна вещь, которую я заметил: похоже, вы используете один и тот же класс сущностей для разных хранилищ данных Spring, сильно перемешивая аннотации. Вы должны использовать разные сущности для разных магазинов.

2021-11-22 17:12:04

Я попытался удалить сопоставление с помощью kibana : УДАЛИТЬ предложение/_mapping и переиндексировать мои предложения. Но, возможно, использование явного "createWithMapping" может работать лучше, я дам вам знать, если это поможет ! В любом случае, спасибо тебе
Aymane EL Jahrani

Здравствуйте, поэтому я попробовал использовать функцию createWithMapping, но это не кажется простым. Можете ли вы подтвердить, что это правильный способ его использования ? github.com/spring-projects/spring-data-elasticsearch/blob/main/...
Aymane EL Jahrani
0

ШАГИ ДЛЯ РЕШЕНИЯ :

  • Используйте Kibana, чтобы убедиться, что вы УДАЛИЛИ <имя_индекса>/_mapping
  • Поищите в своих классах сущностей нужные вам объекты, которые могут находиться в свойствах @JsonIgnoreProperties
  • Убедитесь, что вы охотно загружаете атрибуты ваших отношений ToMany (в противном случае elastic не создаст сопоставление для данных, которые вы никогда не предоставляли).
  • Исходя из этого небольшого опыта, я бы сказал, что избегайте использования вложенных полей, я не видел в их использовании никаких преимуществ. Так что проверьте, так ли это и для вас !
2021-11-25 23:17:34

Spring Data Elasticsearch ничего не делает с @JsonIgnoreProperties аннотация. Elasticsearch не является реляционной базой данных и не имеет понятия об отношениях между сущностями.
P.J.Meisch

Это правда, но spring делает это при сериализации данных. Вот как я получаю свои сущности ...
Aymane EL Jahrani

Сама весна этого не делает. Например, JPA Spring Data делает это. Spring Data Elasticsearch этого не делает. Это разные модули данных Spring.
P.J.Meisch

Библиотека fasterxml.jackson делает это ,и она работает с использованием аннотаций для объектов JPA/Hibernate. Это то, что я использую в этом проекте, как вы можете видеть в моем коде...
Aymane EL Jahrani

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................