Spring Data Solr: как установить для multiValue значение false при объявлении поля

Я играю с Spring Data Solr с помощью Schemaless Solr. Есть некоторые моменты, которые я не могу связать: когда и как создаются поля схемы?

На следующей странице указано

Автоматическое заполнение схемы будет проверять типы ваших доменов при каждом обновлении контекста приложений и заполнять новые поля в вашем индексе на основе конфигурации свойств. Для этого требуется, чтобы solr работал в режиме без схемы.

Используйте @Indexed, чтобы предоставить дополнительные сведения, такие как конкретные типы solr для использования.

Он также показывает запрос на завивание:

// curl ../solr/collection1/schema/fields -X POST -H 'Тип содержимого: приложение/json'

Однако, когда я запускаю простой пример с полем, аннотированным @Indexed, я не вижу, чтобы API /schema/fields вызывался в SOLR. Как создаются эти поля?

Причина, по которой я спрашиваю, заключается в том, что они автоматически создаются с multiValued=true. Я не видел, чтобы аннотация @Indexed принимала в качестве параметра multiValued. Как я могу заставить Spring Data Solr объявлять поля как не многозначные при их создании?

Теперь все это действительно для устранения этого исключения, которое я вижу.

java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.util.Assert.notNull(Assert.java:126)
at org.springframework.data.solr.core.convert.MappingSolrConverter$SolrPropertyValueProvider.readValue(MappingSolrConverter.java:426)
at org.springframework.data.solr.core.convert.MappingSolrConverter$SolrPropertyValueProvider.readCollection(MappingSolrConverter.java:601)
at org.springframework.data.solr.core.convert.MappingSolrConverter$SolrPropertyValueProvider.readValue(MappingSolrConverter.java:440)
at org.springframework.data.solr.core.convert.MappingSolrConverter$SolrPropertyValueProvider.readValue(MappingSolrConverter.java:412)
at org.springframework.data.solr.core.convert.MappingSolrConverter$SolrPropertyValueProvider.getPropertyValue(MappingSolrConverter.java:395)
at org.springframework.data.solr.core.convert.MappingSolrConverter.getValue(MappingSolrConverter.java:206)
at org.springframework.data.solr.core.convert.MappingSolrConverter$1.doWithPersistentProperty(MappingSolrConverter.java:194)
at org.springframework.data.solr.core.convert.MappingSolrConverter$1.doWithPersistentProperty(MappingSolrConverter.java:186)
at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:309)
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:186)
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:174)
at org.springframework.data.solr.core.convert.MappingSolrConverter.read(MappingSolrConverter.java:149)
at org.springframework.data.solr.core.SolrTemplate.convertSolrDocumentListToBeans(SolrTemplate.java:560)
at org.springframework.data.solr.core.SolrTemplate.convertQueryResponseToBeans(SolrTemplate.java:552)
at org.springframework.data.solr.core.SolrTemplate.createSolrResultPage(SolrTemplate.java:369)
at org.springframework.data.solr.core.SolrTemplate.doQueryForPage(SolrTemplate.java:300)
at org.springframework.data.solr.core.SolrTemplate.queryForPage(SolrTemplate.java:308)
at org.springframework.data.solr.repository.support.SimpleSolrRepository.findAll(SimpleSolrRepository.java:111)
at org.springframework.data.solr.repository.support.SimpleSolrRepository.findAll(SimpleSolrRepository.java:106)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at 

Я предполагаю, что исключение происходит из-за того, что значения возвращаются как коллекция?

Я попытался пройтись по коду, чтобы понять, что происходит. Поле, вызывающее проблему, — имя, значение — [продукт-1]. Исключение возникает при попытке преобразовать документ solr в POJO.

Сначала мы заходим внутрь следующего метода:

    private <T> T readValue(Object value, TypeInformation<?> type, Object parent) {
        if (value == null) {
            return null;
        }

        Assert.notNull(type);
        Class<?> rawType = type.getType();
        if (hasCustomReadTarget(value.getClass(), rawType)) {
            return (T) convert(value, rawType);
        }

        Object documentValue = null;
        if (value instanceof SolrInputField) {
            documentValue = ((SolrInputField) value).getValue();
        } else {
            documentValue = value;
        }

        if (documentValue instanceof Collection) {
            return (T) readCollection((Collection<?>) documentValue, type, parent);
        } else if (canConvert(documentValue.getClass(), rawType)) {
            return (T) convert(documentValue, rawType);
        }

        return (T) documentValue;

    }

При вызове этого метода значением является коллекция, а тип — java.lang.String. Это приводит к выбору if(documentValue instanceof Collection), что приводит к выполнению следующего метода:

        private Object readCollection(Collection<?> source, TypeInformation<?> type, Object parent) {
        Assert.notNull(type);

        Class<?> collectionType = type.getType();
        if (CollectionUtils.isEmpty(source)) {
            return source;
        }

        collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class;

        Collection<Object> items;
        if (type.getType().isArray()) {
            items = new ArrayList<Object>();
        } else {
            items = CollectionFactory.createCollection(collectionType, source.size());
        }

        TypeInformation<?> componentType = type.getComponentType();

        Iterator<?> it = source.iterator();
        while (it.hasNext()) {
            items.add(readValue(it.next(), componentType, parent));
        }

        return type.getType().isArray() ? convertItemsToArrayOfType(type, items) : items;
    }

В этом методе мы заканчиваем вызовом type.getComponentType(), который возвращает null и в конечном итоге приводит к сбою Assert.notNull().

Чего мне не хватает во всем этом?

Мой код выглядит следующим образом. Запустить и настроить класс:

@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableSolrRepositories(schemaCreationSupport=true, basePackages = { "com.example.solrdata" }, multicoreSupport = true)
public class Application {

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}
}

Класс модели:

@SolrDocument(solrCoreName = "collection1")
public class Product {

@Id
String id;
@Indexed
String name;

public Product(String id, String name) {
    this.id = id;
    this.name = name;
}

public Product() {
    super();
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}}

Репозиторий:

public interface ProductRepository extends SolrCrudRepository<Product, String> {}

Тестовый класс:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class SolrProductRepositoryTest  {

@Autowired
private ProductRepository repo;

@Before @After
public void setup(){
    repo.deleteAll();
}

@Test
public void testCRUD() {
    assertEquals(0, repo.count());
    Product product = new Product("1","product-1");
    repo.save(product);
    assertEquals(1, repo.count());
    Product product2 = repo.findOne(product.getId());
    assertEquals(product2.getName(), product.getName());
}}

И, наконец, мой ПОМ:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.3.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-solr</artifactId>
    </dependency>
</dependencies>

person Klaus    schedule 11.03.2016    source источник
comment
sd-solr будет создавать новые поля (многозначные в случае, если свойство похоже на коллекцию), если они еще не существуют, и для этого требуется, чтобы сервер работал в режиме без схемы. Тем не менее на сервере присутствует схема. Можете ли вы добавить это, пожалуйста.   -  person Christoph Strobl    schedule 11.03.2016
comment
Вы имеете в виду, что я должен добавить схему? Я запускаю solr в режиме без схемы, и это одно из моих требований. Таким образом, добавление схемы не является предпочтительным путем. Теперь, если многозначное = истинное поведение жестко закодировано, как мне избежать вышеуказанного исключения?   -  person Klaus    schedule 11.03.2016
comment
хотя запуск solr в режиме без схемы не означает, что схемы нет. он есть и, возможно, уже был инициализирован со значениями по умолчанию. просто загляните в каталог conf и откройте файл managed-schema хотя нет файла xml, он содержит информацию о вашей схеме   -  person Christoph Strobl    schedule 11.03.2016
comment
Я взял совершенно новое развертывание solr, разархивировал из solr-4.10.4.zip, убедился, что управляемой схемы нет, запустил solr в режиме без схемы и выполнил вышеуказанный тест. В результате генерируется управляемая схема и выдается указанное выше исключение.   -  person Klaus    schedule 11.03.2016


Ответы (1)


К сожалению, при использовании Solr 4.10 со значениями по умолчанию JSONResponseWriter в solrconfig.xml использует text/pain в качестве типа контента.

<queryResponseWriter name="json" class="solr.JSONResponseWriter">
  <str name="content-type">text/plain; charset=UTF-8</str>
</queryResponseWriter>

Это приводит к тому, что согласование типа контента SolrSchemaRequest автоматически завершается ошибкой и пропускает шаг обновления схемы, и в этом месте срабатывает угадывание типа поля solr по умолчанию.

Установка content-type на application/json позволяет добавлять поля в соответствии с определением компонента.

@SolrDocument(solrCoreName = "collection1")
public static class SomeDomainType {

    @Indexed @Id 
    String id;

    @Indexed 
    String indexedStringWithoutType;

    @Indexed(name = "namedField", type = "string", searchable = false) 
    String justAStoredField;

    @Indexed 
    List<String> listField;

    @Indexed(type = "tdouble") 
    Double someDoubleValue;
}

До

{
    responseHeader: {
        status: 0,
        QTime: 86
    },
    fields: [
        {
            name: "_version_",
            type: "long",
            indexed: true,
            stored: true
        },
        {
            name: "id",
            type: "string",
            multiValued: false,
            indexed: true,
            required: true,
            stored: true,
            uniqueKey: true
        }
    ]
}

После обновления схемы

{
    responseHeader: {
        status: 0,
        QTime: 1
    },
    fields: [
        {
            name: "_version_",
            type: "long",
            indexed: true,
            stored: true
        },
        {
            name: "id",
            type: "string",
            multiValued: false,
            indexed: true,
            required: true,
            stored: true,
            uniqueKey: true
        },
        {
            name: "indexedStringWithoutType",
            type: "string",
            multiValued: false,
            indexed: true,
            required: false,
            stored: true
        },
        {
            name: "listField",
            type: "string",
            multiValued: true,
            indexed: true,
            required: false,
            stored: true
        },
        {
            name: "namedField",
            type: "string",
            multiValued: false,
            indexed: false,
            required: false,
            stored: true
        },
        {
            name: "someDoubleValue",
            type: "tdouble",
            multiValued: false,
            indexed: true,
            required: false,
            stored: true
        }
    ]
}   
person Christoph Strobl    schedule 14.03.2016
comment
Спасибо за обновление Кристоф. Кажется, это не работает для меня. Совершенно новый Solr-4.10.4. Изменен следующий файл solrconfig.xml: ./solr-4.10.4/example/example-schemaless/solr/collection1/conf/solrconfig.xml. Я запускаю solr с помощью bin/solr -e schemaless. JSONresponseWriter настроен с помощью: ‹str name=content-type›application/json; charset=UTF-8‹/str› Исключение такое же. - person Klaus; 14.03.2016
comment
у вас есть работающий образец с тестовыми примерами, которые я могу запустить? Мой проход с изменениями в solrconfig.xml, описанный выше. - person Christoph Strobl; 14.03.2016
comment
Я загрузил свой пример проекта (с тестовым примером) по адресу: expirebox.com/download/cdf6def72b988528f0d2818047ff559d.html. Он в основном состоит из частей, которые я вставил выше. - person Klaus; 14.03.2016
comment
Есть идеи? Должен ли я открыть билет? - person Klaus; 18.03.2016