I'm quite new to elastic search, I'm not able to set mapping via field annotation. I'm using spring data elastic 4.3.4. I'm adding settings via #Setting annotation which is actually working. But if I set Field type to Keyword it is not getting updated, and elastic dynamically maps the field type to text. My requirement is to add a normaliser to enable alphabetic sort on specific fields. Please find my set-up below, I really appreciate your help.
Configuration:
Elastic Search version :"7.11.1",
Spring data elastic 4.3.4.
Sample code
#Document(indexName = "#{#environment.getProperty('elastic.index.prefix')}-test")
#Setting(settingPath = "/elasticsearch/analyzer.json")
public class ElasticTest {
#Id
String id;
#Field(type = FieldType.Keyword, normalizer="sort_normalizer")
private String name;
#Field
private String createdDate;
#Field(type=FieldType.Object)
private CustomerType customerType;
=============================================================================
So once the index is created, I can see the settings added
"creation_date" : "1664385255792",
"analysis" : {
"normalizer" : {
"sort_normalizer" : {
"filter" : [
"lowercase",
"asciifolding"
],
"type" : "custom",
"char_filter" : [ ]
}
},
"analyzer" : {
"custom_pattern_analyzer" : {
"lowercase" : "true",
"pattern" : """\W|_""",
"type" : "pattern"
}
}
},
Mapping:
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
Note: Since I'm working locally I have to drop and recreate the index multiple times. I'm able to set these field types via curl/kibana.
Update: Here we create the index by:
if (!searchTemplate.indexOps(ElasticContract.class).exists()) {
searchTemplate.indexOps(ElasticContract.class).create();
}
And we also use ElasticsearchRepository for querying.
How are the index and the mapping created?
If you use Spring Data Elasticsearch repositories, then the index with the setting and mapping will be created if it does not yet exist on application startup.
If you do not use a repository, but use ElasticsearchOperations, you will need to create the index by yourself:
ElasticsearchOperations operations;
IndexOperations indexOps = operations.indexOps(ElasticTest.class);
indexOps.createWithMapping();
If you do not create the index but just insert some data, then Elasticsearch will automatically create the index and the mapping. The mapping you show is the typical autocreated one for a String field.
The #Field annotation you use is correct, we have a similar setup in one of the tests that tests exactly this behaviour, see https://github.com/spring-projects/spring-data-elasticsearch/blob/main/src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java#L126-L145
Related
Supposing we have documents in a Mongo collection with the following format:
{
"_id" : "5fb3c5ce9997c61e15a9108c",
"stages" : {
"stage1" : {
"type" : "RandomType"
},
"stage2" : {
"type" : "RandomType2"
},
"arbitaryStage" : {
"type" : "RandomType3"
},
// Possibly many other stages
},
// Fields omitted
}
How can I query a collection of such documents where any stages.X.type is equal to a predefined value? My application doesn't know what X is and doesn't care about it, it should only know that the type of at least one of the stages is equal to a given value. I am trying to do that in Morphia however plain JS would guide me if that's possible to do with the given data format.
For reference the Class from which this entity is originated from is the following
#Entity(value = "stages_collection", noClassnameStored = true)
public class StackOverflowQ {
#Id
private ObjectId id;
#Embedded
private Map<String, Stage> stages;
// Rest of fields/setters/getters omitted
}
public class Stage {
private String type;
// Rest of fields/setters/getters omitted
}
Even if you employ a blanket check, if the system cannot pick the right field then basically the best we can do is search the whole collection, even if you find someway to do it it won't be a very efficient.
A simple change in schema would be better when you expect random fields in the data. In your case.
{
stageName : "Stage 1",
type : "RandomType"
}
You will be able to utilise indexes properly here as well when you scale and the flexibility remains in your hands for future additions. No need to change things in the code when a new stage is required.
I'm having some trouble properly indexing dynamic numeric fields, it seems they are always indexed as strings.
In my understanding, when indexing dynamic numeric fields I must use dynamic templates:
PUT /com.product.product
{
"mappings": {
"com.product.Product": {
"dynamic_templates": [
{
"numeric_sort": {
"match_mapping_type": "*",
"match_pattern": "regex",
"match": "^sort_num_.*",
"mapping": {
"type": "double"
}
}
}
]
}
}
}
That i'm uploading in an event listener:
#Configuration
#Transactional
public abstract class DynamicTemplateConfig {
#EventListener
public void addDynamicTemplates(ContextRefreshedEvent event) {
if (this.searchIndexingIsActive) {
this.addDynamicTemplates();
}
}
...
}
And i'm indexing the properties in a field bridge:
public class PropertyValueFieldBridge implements FieldBridge {
...
private void indexBigDecimalProperties(Document document, LuceneOptions luceneOptions, PropertyBigDecimal property) {
String fieldName = PREFIX_SORT + NUMERIC + DELIMITER + property.getProperty().getCode();
Double indexedValue = property.getValue().doubleValue();
luceneOptions.addNumericFieldToDocument(
fieldName,
indexedValue,
document);
}
}
After indexing these BigDecimal properties I always end with a string property indexed:
"_source": {
"id": "1",
"sort_id": 1,
"filter_id": 1,
"sort_num_quantity": "115.0"
}
And when i try to sort through this property i have the following exception:
org.hibernate.search.exception.SearchException: Cannot automatically determine the field type for field 'sort_num_quantity'. Use byField(String, Sort.Type) to provide the sort type explicitly.
at org.hibernate.search.query.dsl.sort.impl.SortFieldStates.getCurrentSortFieldTypeFromMetamodel(SortFieldStates.java:177) ~[hibernate-search-engine-5.11.5.Final.jar:5.11.5.Final]
at org.hibernate.search.query.dsl.sort.impl.SortFieldStates.determineCurrentSortFieldTypeAutomaticaly(SortFieldStates.java:150) ~[hibernate-search-engine-5.11.5.Final.jar:5.11.5.Final]
at org.hibernate.search.query.dsl.sort.impl.ConnectedSortContext.byField(ConnectedSortContext.java:42) ~[hibernate-search-engine-5.11.5.Final.jar:5.11.5.Final]
I'm trying to avoid using the byField(String, Sort.Type) since it requires the explicity verification of every property, which i might not know the name and type.
Am i doing something wrong in the indexing process?
Thanks in advance
I don't think you're doing anything wrong. The experimental Elasticsearch integration in Hibernate Search 5 does not really support dynamic fields. You can't specify the type of fields in advance, and it apparently defaults to the String type for dynamic fields.
Upgrading to Hibernate Search 6 (currently in the Candidate Release phase) would be a solution, since it supports dynamic fields through field templates
The Hibernate Search 6 API is different, however, so migrating may require significant work.
This is the Object of CcStorePartnerclass, There is an another Object of Partner class inside this class.
Here i want to make filter on attributes of CcStorePartner obj and attributes of Partner Class,
This is the web service body tag. where i have decleared the filter on the objects which are placed in Mongodb. I'm using MongoTemplate
{
"target":"stores",
"filter":
[
{
"storeId" : "a487c" ,
"Type": "contains"
},
{
"partner.partnerCode": "ucb",
"Type":"contains"
}
]
}
What will be the mongo query. which will provide the List
Here m using this..
query.addCriteria(Criteria.where("storeId").regex(".*a487c.*","i"));
query.addCriteria(Criteria.where("partner.partnerCode").regex(".*ucb.*","i"));
I'm receiving this error
Pojo Class
and this is the Mongo Template Code which m using- Dynamic Query.
MongoItemReader provided by spring batch has method setFields:
public void setFields(java.lang.String fields)
JSON defining the fields to be returned from the matching documents by
MongoDB.
Parameters:
fields - JSON string that identifies the fields to sort by.
I have a class:
public class Raw {
private String id;
private String version;
private String client;
private String appName;
private String os;
// getters & setters
}
And I have data in mongodb like that:
{
"_id" : ObjectId("58a3373e1e041a1191cd5d6d"),
"Version" : "123",
"Client" : "SomeClient",
"MobilePlatform" : "iphoneos",
"AppName" : "MyAppName",
"Os" : "Windows 10"
}
- so as you can see all fields names start with capital letter.
Now I need to read data from mongo with spring batch.
And I need to map somehow fields in my Raw class to data in mongo DB so I will be able to fetch data.
I suspect that setFields method is just for such cases.
But I am relatively new to mongo and spring batch also,
so I would like to ask how to do that?
Which JSON should I put into setFields method?
Or probably there are some other options?
Any help is greatly appreciated.
I found the anwer by myself in documentation: http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html
#Field annotation can be used and it works.
I've a database and some classes. These classes are linked with OneToMany, and so on.
If I print the object itself with spring it contains everything. But if I print it with the Resource feature, it contains only the variables, which are no collections or linked otherwise with an other class.
How can I add the collections to the output?
By default Spring Data REST does not show associated resources except as links. If you want that you have to define projections that describe the fields you want to see, whether they're simple fields like the ones you describe or associated resources. See
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts
For example say you have a Service resource with associations to resources like serviceType, serviceGroup, owner, serviceInstances and docLinks. If you want those to show up in the response body you can create a projection:
package my.app.entity.projection;
import org.springframework.data.rest.core.config.Projection;
...
#Projection(name = "serviceDetails", types = Service.class)
public interface ServiceDetails {
String getKey();
String getName();
ServiceType getType();
ServiceGroup getGroup();
Person getOwner();
List<ServiceInstance> getServiceInstances();
List<DocLink> getDocLinks();
String getPlatform();
}
Then GET your URL with the projection:
http://localhost:8080/api/services/15?projection=serviceDetails
The result will include the projected properties:
{
"name" : "MegaphoneService",
"key" : "megaphone",
"type" : {
"key" : "application",
"name" : "User Application",
"description" : "A service that allows users to use a megaphone."
},
"owner" : null,
"serviceInstances" : [ {
"key" : "megaphone-a-dr",
"description" : null,
"loadBalanced" : true,
"minCapacityDeploy" : null,
"minCapacityOps" : 50
}, ... ],
...
}