【问题标题】:Elasticsearch null pointer when indexing parent document索引父文档时的Elasticsearch空指针
【发布时间】:2018-07-24 11:22:59
【问题描述】:

我正在尝试在 Elasticsearch 中建立父子关系,并且在尝试索引父级时始终从服务器收到空指针异常。

我在 6.2 版本中都使用 Elasticsearch 和 NEST。

我基本上一直在遵循文档中的本指南:https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/parent-child-relationships.html

对象:(ESDocument是基类,ESVendor是父类,ESLocation是子类)

    [ElasticsearchType(Name = "Doc", IdProperty = "Id")]
    public abstract class ESDocument
    {
        public int Id { get; set; }
        public JoinField MyJoinField { get; set; }
    }

    [ElasticsearchType(Name = "Vendor", IdProperty = "ConsigneeID")]
        public class ESVendor: ESDocument
        {
            public int VendorID { get; set; }
            public int VendorTypeID { get; set; }
            public int ClientID { get; set; }
            public int CompanyID { get; private set; }
            public int LocationID { get; set; }
            public bool Active { get; set; }
            public TimeSpan? OpenTime { get; set; }
            public TimeSpan? CloseTime { get; set; }
            public bool AppointmentRequired { get; set; }
            public decimal? Latitude { get; set; }
            public decimal? Longitude { get; set; }
            public bool PositionRequested { get; set; }
            public bool PositionFailed { get; set; }
            public int? NoteID { get; set; }
            public int? OldID { get; set; }


            public ESVendor(Vendor vendor)
            {
                if (vendor == null)
                {
                    return;
                }
                Id = vendor.VendorID;
                CompanyID = vendor.CompanyID;
                Active = vendor.Active;
                VendorID = vendor.VendorID;
                Latitude = vendor.Latitude;
                Longitude = vendor.Longitude;
                VendorTypeID = vendor.VendorTypeID;
                ClientID = vendor.ClientID;
                LocationID = vendor.LocationID;
                Latitude = vendor.Latitude;
                OpenTime = vendor.OpenTime;
                CloseTime = vendor.CloseTime;
                AppointmentRequired = vendor.AppointmentRequired;
                PositionRequested = vendor.PositionRequested;
                PositionFailed = vendor.PositionFailed;
                NoteID = vendor.NoteID;
                OldID = vendor.OldID;

            }

            public ESVendor()
            {
            }
        }

    [ElasticsearchType(Name = "Location", IdProperty = "LocationID")]
        public class ESLocation: ESDocument
        {
            public int LocationID { get; set; }
            public int CompanyID { get; private set; }
            public string Name { get; set; }
            public string Address { get; set; }
            public string Address2 { get; set; }
            public string CityStateZipCountry { get; set; }
            public string Note { get; set; }
            public string City { get; set; }
            public string State { get; set; }
            public string Zip { get; set; }
            public string Country { get; set; }

            public ESLocation(Location location)
            {
                if (location == null)
                {
                    return;
                }
                Id = location.LocationID;
                CompanyID = location.CompanyID;
                Address = location.Address;
                Address2 = location.Address2;
                LocationID = location.LocationID;
                Name = location.Name;
                Note = location.Note;
                City = location.City;
                State = location.State;
                Zip = location.Zip;
                Country = location.Country;
                CityStateZipCountry = location.Country + " " + location.State + " " + location.Zip + " " + location.Country;
            }

            public ESLocation()
            {
            }
        }

映射+索引:

    StaticConnectionPool connectionPool = new StaticConnectionPool(_nodes);
ConnectionSettings connectionSettings = new ConnectionSettings(connectionPool, sourceSerializer: SourceSerializer)
    .DisableDirectStreaming()
    .DefaultMappingFor<ESDocument>(m => m.IndexName("vendors").TypeName("doc"))
    .DefaultMappingFor<ESVendor>(m => m.IndexName("vendors").TypeName("doc").RelationName("Vendor_Location"))
    .DefaultMappingFor<ESLocation>(m => m.IndexName("vendors").TypeName("doc"));
ElasticClient esClient = new ElasticClient(connectionSettings);


IExistsResponse existsResponse = await esClient.IndexExistsAsync(new IndexExistsRequest(Indices.Parse("vendors")));
if (!existsResponse.Exists)
{
    esClient.CreateIndex("vendors", c => c
        .Index<ESVendor>()
        .Mappings(ms => ms
            .Map<ESVendor>(m => m
                .RoutingField(r => r.Required())
                .AutoMap<ESVendor>()
                .AutoMap<ESLocation>()
                .Properties(props => props
                    .Join(j => j
                        .Name(p => p.MyJoinField)
                        .Relations(r => r
                            .Join<ESVendor, ESLocation>()
                        )
                    )
                )
            )
        ));

}


using (Entities dbContext = new Entities())
{
    foreach (Vendor vendor in dbContext.Vendors)
    {
        if (!(await esClient.DocumentExistsAsync(new DocumentExistsRequest("vendors", typeof (Vendor), vendor.VendorID))).Exists)
        {
            ESVendor parent = new ESVendor(vendor)
            {
                MyJoinField = JoinField.Root<ESVendor>()
            };
            var result = esClient.IndexDocument<ESDocument>(parent);


            ESLocation child = new ESLocation(vendor.Location)
            {
                MyJoinField = JoinField.Link<ESLocation, ESVendor>(parent)
            };
            result = esClient.IndexDocument<ESDocument>(child);
        }
    }
}

每次尝试索引父文档时,它都会始终返回以下错误。

    Invalid NEST response built from a unsuccessful low level call on POST: /vendors/doc
# Audit trail of this API call:
 - [1] BadResponse: Node: http://192.168.50.240:9200/ Took: 00:00:00.2060485
# OriginalException: Elasticsearch.Net.ElasticsearchClientException: The remote server returned an error: (500) Internal Server Error.. Call: Status code 500 from: POST /vendors/doc. ServerError: Type: null_pointer_exception Reason: "id must not be null" ---> System.Net.WebException: The remote server returned an error: (500) Internal Server Error.
   at System.Net.HttpWebRequest.GetResponse()
   at Elasticsearch.Net.HttpConnection.Request[TResponse](RequestData requestData)
   --- End of inner exception stack trace ---
# Request:
{"vendorID":1,"vendorTypeID":1,"clientID":349,"companyID":1,"locationID":4994,"active":true,"openTime":null,"closeTime":null,"appointmentRequired":false,"latitude":null,"longitude":null,"positionRequested":false,"positionFailed":false,"noteID":null,"oldID":1626,"id":1,"myJoinField":"Vendor_Location"}
# Response:
{"error":{"root_cause":[{"type":"null_pointer_exception","reason":"id must not be null"}],"type":"null_pointer_exception","reason":"id must not be null"},"status":500}

我已经为此奋斗了好几天,本来应该是 30 分钟的任务,但我已经束手无策了。

提前感谢您,非常感谢您的帮助!

【问题讨论】:

  • 你使用的是什么版本的 NEST 包,你的目标是什么版本的 Elasticsearch?您能否将详细信息添加到您的问题中
  • 我在 6.3 都使用 Elasticsearch 和 NEST。我在 6.2 中遇到了同样的问题,所以我更新了想法,也许这会有所帮助。
  • 最新的 NEST 版本是 6.2.0 - 你在使用 Elasticsearch 6.3.0 吗?
  • 抱歉,它们都是 6.2,最近从 6.1 更新。我不在办公室,从记忆中提取了版本。

标签: c# .net elasticsearch nest


【解决方案1】:

问题是应用于EsVendor的属性

[ElasticsearchType(Name = "Vendor", IdProperty = "ConsigneeID")]
public class ESVendor: ESDocument
{
     // ...
}

IdProperty 告诉 NEST 从 ConsigneeID 属性推断 EsVendor 类型的 id,但是查看提供的模型,没有具有该名称的属性。看起来您不需要在属性中指定此属性,而是可以依赖应用于ESDocument 的ElasticsearchTypeAttribute 中的IdProperty,这将从Id 属性中推断出Id。

因此,从应用于EsVendor 的属性中删除IdProperty

[ElasticsearchType(Name = "Vendor")]
public class ESVendor : ESDocument
{
    // ...
}

现在将发送一个类似于EsVendor的索引请求(其他属性为简洁而略过)

PUT http://localhost:9200/vendors/doc/1?routing=1 
{
  "id": 1,
  "vendorID": 1,
  "myJoinField": "Vendor_Location"
}

对于引用属性的字符串值,我们可能还想依赖编译器,并使用nameof(Id)

[ElasticsearchType(Name = "Doc", IdProperty = nameof(Id))]
public abstract class ESDocument
{
    public int Id { get; set; }
    public JoinField MyJoinField { get; set; }
}

事实上,如果我们愿意,我们可以完全删除IdProperty,因为使用Id 属性来推断文档的ID 是默认的NEST 行为。更进一步,因为类型在ConnectionSettings 上有一个DefaultMappingFor&lt;T&gt;,我们可以完全删除ElasticsearchType 属性,因为连接设置的默认映射将优先。

【讨论】:

  • 100% 修复它。 IdProperty 是我完全错过的重构的遗留物。非常感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
  • 2012-12-12
  • 2020-02-23
相关资源
最近更新 更多