Skip to content

Use a customized typehint attribute name #2029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jinwg opened this issue Dec 16, 2021 · 6 comments · Fixed by #2039
Closed

Use a customized typehint attribute name #2029

jinwg opened this issue Dec 16, 2021 · 6 comments · Fixed by #2039
Labels
type: enhancement A general enhancement

Comments

@jinwg
Copy link

jinwg commented Dec 16, 2021

is there any way to customize the default type hit attributes filed?

	String DEFAULT_TYPE_KEY = "_class";
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Dec 16, 2021
@sothawo
Copy link
Collaborator

sothawo commented Dec 16, 2021

This is not possible at the moment. What would be the use case to have a customized name for this field name, which is a field internally by Spring Data Elasticsearch ?

@sothawo sothawo added the status: waiting-for-feedback We need additional information before we can continue label Dec 16, 2021
@jinwg
Copy link
Author

jinwg commented Dec 16, 2021

i am working on a search only project which need to read a big index stores more than 10 years data. we already have a filed play similar document type role. it is really a overkill for me to re-index all old documents to add the new filed. either a customized filed name or a callback hook could create sub-classes both works for my case, but neither of them are available in current release.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 16, 2021
@sothawo sothawo changed the title Use a customized typehit attribute name Use a customized typehint attribute name Dec 17, 2021
@sothawo sothawo added status: under investigation and removed status: feedback-provided Feedback has been provided labels Dec 17, 2021
@sothawo
Copy link
Collaborator

sothawo commented Dec 17, 2021

So you have an index that contains documents that correspond to different entity classes in your application, and you have a field in your index that contains the full qualified name of the respective class? Do this classes have a common base class that you will use in Spring Data Elasticsearch as entity class when reading the data?

One thing to solve this would be to allow the configuration of the type hint field name as you suggest. A better way would be to have a lifecycle callback that is invoked after the data has been read from Elasticsearch and before it is mapped into an entity. In this callback you could add a _class field that contains the value of your field that has the class name.

There is an open issue to add that callback (#2009).

@jinwg
Copy link
Author

jinwg commented Dec 17, 2021

@sothawo, yes we have a base entity class. i am using mapstruct with customized converter function to do this right now. but i think it will be great to have the native feature in this library.

@sothawo sothawo added status: blocked An issue that's blocked on an external project change type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged status: under investigation labels Dec 19, 2021
@sothawo
Copy link
Collaborator

sothawo commented Dec 19, 2021

I think this should be solved with a callback that invoked just before the conversion; this is in #2009. If there is no feedback on that ticket in the next 7 days, I'll gonna implement that.

@sothawo sothawo removed the status: blocked An issue that's blocked on an external project change label Dec 27, 2021
@sothawo
Copy link
Collaborator

sothawo commented Dec 27, 2021

PR #2039 that closed #2009 now adds the possiblity to use an AfterLoadCallback (will be in the next snapshot build
4.4.0-SNAPSHOT). This can be used to set the _class attribute to the value of some other field when reading data
from Elasticsearch.

I have tried this in the following setup:

An abstract base class:

@Document(indexName = "type-hints", writeTypeHint = WriteTypeHint.FALSE)
public abstract class BaseClass {

	@Id
	private String id;

	@Field(type = FieldType.Keyword, name = "my-class-name-field")
	private String myClassNameField;

	public BaseClass() {
		this.myClassNameField = getClass().getName();
	}

	// getter+setter ...
}

I set the writeTypeHint property to false to make sure that no _class field is written. The myClassNameField
property is set in the constructor, for the test I have a different name for the Elasticsearch field and the property.
I have two derived classes with different properties, don't need to show them here.

After saving two entities I have the following in my index:

http :9200/type-hints/_search
{
  "_shards": {
    "failed": 0,
    "skipped": 0,
    "successful": 1,
    "total": 1
  },
  "hits": {
    "hits": [
      {
        "_id": "one",
        "_index": "type-hints",
        "_score": 1.0,
        "_source": {
          "base-text": "baseOne",
          "derived-one": "derivedOne",
          "id": "one",
          "my-class-name-field": "com.sothawo.springdataelastictest.typehints.DerivedOne"
        },
        "_type": "_doc"
      },
      {
        "_id": "two",
        "_index": "type-hints",
        "_score": 1.0,
        "_source": {
          "base-text": "baseTwo",
          "derived-two": "derivedTwo",
          "id": "two",
          "my-class-name-field": "com.sothawo.springdataelastictest.typehints.DerivedTwo"
        },
        "_type": "_doc"
      }
    ],
    "max_score": 1.0,
    "total": {
      "relation": "eq",
      "value": 2
    }
  },
  "timed_out": false,
  "took": 21
}

So the class informaiton is stored in a custom field. To be able to read this in I have defined the following
calllback in a configuration class:

@Configuration
public class TypeHintConfiguration {

	@Component
	static class TypeHintAfterLoadCallback implements AfterLoadCallback<BaseClass> {

		@Override
		public Document onAfterLoad(Document document, Class<BaseClass> type, IndexCoordinates indexCoordinates) {

			document.put(ElasticsearchTypeMapper.DEFAULT_TYPE_KEY, document.get("my-class-name-field"));
			return document;
		}
	}
}

This sets the value of the _class field to the same as that of the custom class information field. With this
callback in place, the ElasticsearchMapper can instantiate the correct entities from the returned data.

Note: You may have noticed that I am using the value ElasticsearchTypeMapper.DEFAULT_TYPE_KEY here, so it is
possible to use a different value than _class like you suggested in the first place, but this is

  • currently not configurable
  • it would set the type attribute globally for the whole application. The approach by using the callback allows to
    have additional checks for example by using the type and indexCoordinates parameters in the callback. And the
    callback is only used when trying to read entities that derive from BaseClass, other entities and repositories
    are unaffected.

@sothawo sothawo closed this as completed Dec 27, 2021
@sothawo sothawo added this to the 4.4 M1 (2021.2.0) milestone Dec 27, 2021
@sothawo sothawo linked a pull request Dec 27, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants