索引父文档时的 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,但是查看提供的模型,没有 属性 用那个名字。看起来您不需要在属性中指定此 属性,而是可以依赖应用于 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<T>
,我们可以完全删除 ElasticsearchType
属性,因为连接设置的默认映射将优先。
我正在尝试在 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,但是查看提供的模型,没有 属性 用那个名字。看起来您不需要在属性中指定此 属性,而是可以依赖应用于 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<T>
,我们可以完全删除 ElasticsearchType
属性,因为连接设置的默认映射将优先。