索引父文档时的 Elasticsearch 空指针

Elasticsearch null pointer when indexing parent document

我正在尝试在 Elasticsearch 中建立父子关系,但在尝试为父项编制索引时,我一直从服务器收到空指针异常。

我正在使用 6.2 版的 Elasticsearch 和 NEST。

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

Objects:(ESDocument 是基础 class,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 分钟的任务,但我已经为此苦苦挣扎了好几天,我已经束手无策了。

提前致谢,非常感谢您的协助!

问题是应用于 EsVendor

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

IdProperty 告诉 NEST 从 ConsigneeID 属性 推断 EsVendor 类型的 id,但是查看提供的模型,没有 属性 用那个名字。看起来您不需要在属性中指定此 属性,而是可以依赖应用于 ESDocumentElasticsearchTypeAttribute 中的 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<T>,我们可以完全删除 ElasticsearchType 属性,因为连接设置的默认映射将优先。