【问题标题】:Spring Data Solr: how to set multiValue to false when declaring a fieldSpring Data Solr:声明字段时如何将multiValue设置为false
【发布时间】:2016-03-11 03:52:02
【问题描述】:

我正在使用带有 Schemaless Solr 的 Spring Data Solr。有些点我无法连接:架构字段是何时以及如何创建的?

The following page states

每当刷新应用程序上下文时,自动模式填充将检查您的域类型,并根据属性配置将新字段填充到您的索引中。这需要 solr 以无模式模式运行。

使用@Indexed 提供额外的详细信息,例如要使用的特定 solr 类型。

它还会显示一个 curl 请求:

// curl ../solr/collection1/schema/fields -X POST -H 'Content-type:application/json'

但是,当我运行一个使用 @Indexed 注释的字段的简单示例时,我没有看到 SOLR 上调用了 /schema/fields API。这些字段是如何创建的?

我问的原因是它们似乎是使用 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 

我的猜测是发生异常是因为值作为集合返回?

我试图单步调试代码以了解发生了什么。导致问题的字段是“名称”,值为 [product-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());
}}

最后,我的 POM:

<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>

【问题讨论】:

  • sd-solr 只会在新字段不存在并且需要服务器以无模式模式运行时创建新字段(如果属性类似于集合,则为多值)。服务器上仍然存在模式。可以请你添加那个吗?
  • 你的意思是我应该添加一个架构吗?我在无模式模式下运行 solr,这是我的要求之一。因此,添加模式不是首选路线。现在,如果 multivalued=true 行为是硬编码的,我该如何避免上述异常?
  • 虽然在无模式模式下运行 solr 并不意味着没有模式。它在那里并且可能已经使用默认值进行了初始化。只需查看 conf 目录并打开文件 managed-schema 尽管没有包含您的架构信息的 xml 文件
  • 我进行了全新的 solr 部署,从 solr-4.10.4.zip 解压,验证没有托管模式,以无模式模式启动 solr,并运行上述测试。结果,生成了托管模式并抛出了上述异常。

标签: spring-data-solr


【解决方案1】:

不幸的是,当使用具有默认值的 Solr 4.10 时,solrconfig.xml 中的 JSONResponseWriter 使用 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 允许根据bean 定义添加字段。

@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
        }
    ]
}   

【讨论】:

  • 感谢克里斯托夫的更新。它似乎对我不起作用。全新的 solr-4.10.4。以下 solrconfig.xml 已更改:./solr-4.10.4/example/example-schemaless/solr/collection1/conf/solrconfig.xml。我使用“bin/solr -e schemaless”启动 solr。 JSONresponseWriter 配置为:application/json; charset=UTF-8 异常是一样的。
  • 你有一个运行样本和我可以运行的测试用例吗?我的通行证与上面概述的 solrconfig.xml 一起使用。
  • 我将我的示例项目(带有测试用例)上传到:expirebox.com/download/cdf6def72b988528f0d2818047ff559d.html。它基本上是由我上面粘贴的部分组成的。
  • 有什么想法吗?我应该开票吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 2015-07-14
  • 1970-01-01
相关资源
最近更新 更多