Spring Data ElasticSearch: Index field must be Keyword and not Text - java

I'm having this issue where I need my index to look like this:
"status": {
"type": "keyword",
}
I create my Java class:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_ABSENT)
#Document(indexName = "#{applicationConfiguration.indexName}")
public class ProductDocument {
#Field(type = FieldType.Keyword)
#NotEmpty
#JsonProperty("status")
private String status;
}
But spring creates the index as follows:
"status": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
How can I make the keyword type the main type instead text?

Related

How to add link to parent object in schema

I have a simple dto
#Getter
#Setter
#Schema(title = "TestDto", description = "Test dto")
public class TestDto {
private Integer id;
private String value;
#ArraySchema(schema = #Schema(implementation = TestDto.class))
private List<TestDto> children;
and when i generate schema i see
"testDto": [
{
"id": 0,
"value": "string",
"children":["string"]
}
but i need something like this
"testDto": [
{
"id": 0,
"value": "string",
"children":[
{"id": 0,
"value": "string",
"children":[{}]}]
}
or like this
"testDto": [
{
"id": 0,
"value": "string",
"children":["testDto"]
}
is there any way to do that?

ElasticSearch returning all Field values as null

My config:
#Configuration
#EnableElasticsearchRepositories(
basePackages = { "com.aj.new.repositories" })
public class ElasticDataSourceConfig {
#Bean
public RestHighLevelClient client() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
#Bean
public ElasticsearchRestTemplate elasticsearchTemplate() {
return new ElasticsearchRestTemplate(client());
}
}
Document:
#Document(indexName = "provider_search")
public class Provider {
#Id #Field
private Long id;
#Field(type = FieldType.Keyword)
private String search;
#Field(name = "ProviderName", type = FieldType.Keyword)
private String providerName;
#Field(name = "Address")
private String address;
#Field(name = "City")
private String city;
#Field(name = "State")
private String state;
...getters...
...setters...
}
Usage:
#Service
public class MySearchService {
#Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
public List<Provider> searchProvidersUsingElastic(final String
providerName, final AddressSearchCriteriaBean addressCriteria) {
final NativeSearchQueryBuilder searchQueryBuilder = new
NativeSearchQueryBuilder();
if (providerName != null) {
final String regex = ".*" + providerName + ".*";
searchQueryBuilder.withQuery(regexpQuery("providerName", regex));
}
if (addressCriteria.getState() != null) {
searchQueryBuilder.withFilter(matchQuery("state",
addressCriteria.getState())
.fuzziness(Fuzziness.ONE));
}
SearchHits<Provider> articles =
elasticsearchRestTemplate.search(searchQueryBuilder.build(),
Provider.class, IndexCoordinates.of("provider_search"));
final List<Provider> providers = articles
.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
return providers;
}
}
When debugging with or without filters, I get providers with only their ID field populated. Every other field like "search", "state", etc. is null.
This is my first venture in ElasticSearch world and I'm not sure what's wrong here. Any help is appreciated.
Edit:
Provider Mappings from Elasticsearch
{
"provider_search": {
"mappings": {
"properties": {
"Address": {
"type": "text",
"analyzer": "autocomplete"
},
"City": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"FaxNumber": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"PhoneNumber": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"ProviderName": {
"type": "text",
"analyzer": "autocomplete"
},
"State": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"Status": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"Zip": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"fac_dbk": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"search": {
"type": "text",
"analyzer": "autocomplete"
}
}
}
}
}
Please note that for testing purposes, I have not mapped every field of Provider on Java side. If that's problematic, let me know.
Second Update:
I have changed the Provider document to map the Field names as is. Still, everything except id is still null.
#Document(indexName = "provider_search")
public class Provider {
#Id
private Long id;
private String search;
private String ProviderName;
private String Address;
private String City;
private String State;
...getters...
...setters...
}
UPDATE:
Turns out the Elasticsearch index had a bug and the fields I had mapped on the Java side were not available on the ES index. It has been fixed and I'm seeing all the values populate correctly.
You have an inconsistency in using the property names (the properties of the Provider entity) and the corresponding field names (the names in Elasticsearch).
Your properties start with a lowercase letter and then are camelcase (providerName, state), for Spring Data Elasticsearch you define them to map to versions starting with a uppercase letter (ProviderName, State) with the #Field annotations. So Spring Data Elasticsearch expects Elasticsearch to return a value for ProviderName and will then store this in the providerName property.
Are the field names in your index starting with an uppercase letter? What does http://localhost:9200/provider_search/_mappings show? This is a guess, as you did not show what Elasticsearch returns, but would explain your error.

JACKSON: How to ignore the POJO name while converting a POJO to JSON using Jackson?

I am using Jackson 2.10.1 library to convert my Java POJOs to JSON and I am getting the below output, I require the output without the POJO name(MyTestPojo here), I have tried various jackson annotations like #JsonIgnoreProperties but those are mostly for the members present in the POJO and not the POJO class name.
{
"MyTestPojo": [
{
"CreatedBy": "user1",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": true,
"Value": "testABC1",
"CreatedDate": "2017-03-13 15:41:54.0",
"Description": "testABC"
},
{
"CreatedBy": "user2",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": false,
"Value": "testABC2",
"CreatedDate": "2017-03-13 15:41:54.0",
"Description": "testABC"
}
]
}
whereas what I require is -
[
{
"CreatedBy": "user1",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": true,
"Value": "testABC1",
"CreatedDate": "2019-03-13 15:41:54.0",
"Description": "testABC"
},
{
"CreatedBy": "user2",
"Name": "testABC",
"UpdatedBy": null,
"UpdatedDate": null,
"IsActive": false,
"Value": "testABC2",
"CreatedDate": "2020-03-10 15:41:54.0",
"Description": "testABC"
}
]
}
Is there a way to handle this with Jackson annotations?
The POJOs that I have used are-
#JacksonXmlRootElement(localName = "ArrayOfTestPojos")
public class GetResponseVO {
#JsonProperty("MyTestPojo")
#JacksonXmlProperty(localName = "MyTestPojo")
#JacksonXmlElementWrapper(useWrapping = false)
private ArrayList<MyTestPojo> MyTestPojoList;
public ArrayList<MyTestPojo> getMyTestPojoList() {
return MyTestPojoList;
}
public void setMyTestPojoList(ArrayList<MyTestPojo> MyTestPojoList) {
this.MyTestPojoList = MyTestPojoList;
}
// standard getters and setters
}
and
#JacksonXmlRootElement(localName = "MyTestPojo")
public class MyTestPojo {
#JsonProperty("Name")
private String name;
#JsonProperty("Description")
private String description;
#JsonProperty("IsActive")
private int isActive;
#JsonProperty("Value")
private String value = null;
#JsonProperty("CreatedBy")
private String createdBy;
#JsonProperty("CreatedDate")
private String createdDate;
#JsonProperty("UpdatedBy")
private String updatedBy;
#JsonProperty("UpdatedDate")
private String updatedDate;
// standard getters and setters.
}
```````````
I am also generating the XML out of this so you can ignore the annotations relevant to XML.
you can use JsonValue annotation for that purpose which basically "use-value of this property instead of serializing the container object". it can be used on getters also
#JsonValue indicates that results of the annotated "getter" method (which means signature must be that of getters; non-void return type, no args) is to be used as the single value to serialize for the instance. Usually value will be of a simple scalar type (String or Number), but it can be any serializable type (Collection, Map or Bean).
#JsonValue
#JacksonXmlProperty(localName = "MyTestPojo")
#JacksonXmlElementWrapper(useWrapping = false)
private ArrayList<MyTestPojo> MyTestPojoList;
But that would wrong practice as it will generate JSON like this, which would not be legal JSON.
{[{"x":"value"}, ...]}
If you want to alter only JSON structure (without affecting xml), you can use MixIn for that purpose.
public interface JsonMixin {
#JsonValue
List<MyTestPojo> getMyTestPojoList();
}
And register it with your object mapper and remove #JsonValue from the main Class.
objectMapper.addMixIn(GetResponseVO.class, JsonMixin.class);

how to mapped java embbeded object with JSON

I build an google Calendar API, and i miss understand a point with my json files.
I succeed to create my java object with my json files but here the issue:
i have two classes :
public class User {
private String email;
private String firstname;
private String lastname;
Entity entity;
``
and my Entity
`` public class Entity {
private String name;
private String entityType;
private Entity rootEntity;``
here my json file :
for user
``[
{
"firstname": "Jean-Marc",
"lastname": "Chevereau",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"BFA",
"entityType":"secteur"
}
},
{
"firstname": "Florent",
"lastname": "Hamlin",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"IT",
"entityType":"secteur"
}
},
{
"firstname": "Benoit",
"lastname": "Micaud",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"EX",
"entityType":"offre",
"rootEntity":{
"name":"BFA"
}
}
}
]``
And a Entity json file
```[
{
"name": "BFA",
"entityType": "secteur",
"rootEntity": "",
},
{
"name": "EX",
"entityType": "Offre",
"rootEntity": "BFA",
}
}
]
But here the trouble. if in my User.json i write theEntity Name, i dont want to write entitytype and rootEntity, because if i write Entity Name is BFA, it will always be the same entitType and the rootEntity.
In others words, my json Entity will be always the same,and if i just put the name we know that refers to an entity object.
For instance, in this user.json file, I will just need to put
[
{
"firstname": "Jean-Marc",
"lastname": "Chevereau",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"BFA",
}
},
{
"firstname": "Florent",
"lastname": "Hamlin",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"IT",
}
},
{
"firstname": "Benoit",
"lastname": "Micaud",
"email": "xxxxxxx#xxxxx.com",
"entity": {
"name":"EX",
}
}
]
In Json-lib you have a JsonConfig to specify the allowed fields:
JsonConfig jsonConfig=new JsonConfig();
jsonConfig.registerPropertyExclusion(Entity.class,"rootEntity");
jsonConfig.registerPropertyExclusion(Entity.class,"entityType");
JSON json = JSONSerializer.toJSON(objectToWrite,jsonConfig);
I suppose com.fasterxml.jackson's #JsonIgnore annotation should help.
public class Entity {
private String name;
#JsonIgnore
private String entityType;
#JsonIgnore
private Entity rootEntity;
}

Java Class to JSON custom Schema by Annotation

I am currently generating my Json Schema like this:
class Item {
public int id;
public string name;
public string description;
}
ObjectMapper mapper = new ObjectMapper();
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(Item.class, visitor);
JsonSchema schema = visitor.finalSchema();
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema));
Which prints:
{
"type": "object",
"id": "urn:jsonschema:de:foo:bar:User",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"description": { "type": "string" }
}
}
Now I want to append additional information to each type from annotations of the Item class. I want this to provide information for how to display an input for that field.
class User {
public int id;
public string name;
#MyJsonSchemaAnnotation(fieldName = "input", value = "textarea")
public string description;
}
Which will give me:
{
"type": "object",
"id": "urn:jsonschema:de:foo:bar:User",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"description": { "type": "string", "input": "textarea" }
}
}
I think this is similar to #JsonDescription or the JSR 303 Annotations. I'm a bit lost if this is possible at all and if so which way i have to implement it. So if anyone could give me a hint where to look at it would be much appreciated!
The mbknor-jackson-jsonSchema package converts java POJOs to json schema. It has #JsonPropertyDescription annotation which could be used to customize schema.
Note that it only supports till draft 4 of json schema, till now.

Categories