-
Notifications
You must be signed in to change notification settings - Fork 1.2k
DATAREDIS-425 - Add Support for basic CRUD and finder Operations backed by Hashes and Sets. #156
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
Conversation
Cool stuff indeed! Well done Christoph! While it is definetly cool to have those indexing capabilities based on redis, I wonder whether this approach would really scale - I mean yes you can generate index helper structures in redis but does this really mean one should do this? I've seen some systems that stored domain objects for fast access by key in redis (hashes) in combination with indexing the domain objects with a search service like elasticsearch or solr. Lookups by key can then be answered by just querying redis. The bottom line is: it would be cool to be able to plug-in an query / index services on entity / repository level that could be implemented in a completly different technology, e.g. redis and elasticsearch (in this example). Cheers, |
Thanks @thomasdarimont. The idea of having additional structures for data lookups is not to replace a fully fledged search service, but to have a convenient way of finding data by specific attributes. Like having http session data stored in Redis that you do not only want to look up by the session id but also by eg. a users email. Having that said and given the responsiveness of Redis I do think that this approach serves well. But true, the culprit hides in how it's used. |
I totally get that - I was just wondering how far this would go ;-) The basic mechanics are already there (JPA EntitListener, MongoDB events or just a RepositoryInterceptor) etc. - wouldn't it be nice to simply call save(..) on a Repository with a domain object which would then also be automatically indexed with elasticsearch or SOLR (after TX completes)? Currently users have to write additional code to index a saved entity to the search service which could be seen as a bit of boilerplate / cross-cutting concern (searchability?). Perhaps this would make a nice new feature for SD Commons, don't you think? |
@thomasdarimont you're already one step ahead. Though I see the use case for that I'm actually a little torn about it. Care to elaborate that at SpringOne? Maybe an issue would help us remember ;) |
5911db1
to
aaf0cc3
Compare
c0a19c4
to
67ea6da
Compare
67ea6da
to
fdd236f
Compare
d247fba
to
ebce1c2
Compare
3524a47
to
692468f
Compare
07b5a66
to
2a522e5
Compare
…ed by Hashes and Sets. Prepare issue branch.
…ed by Hashes and Sets. We now enable storing domain object as a flat Redis 'HASH' and maintain additional 'SET' structures to enable finder operations on simple properties. @keyspace("persons"); class Person { @id String id; @indexed String firstname; String lastname; Map<String, String> attributes; City city; @reference Person mother; } The above is stored in the HASH with key 'persons:1' as _class = org.example.Person id = 1 firstname = rand lastname = al’thor attributes.[eye-color] = grey attributes.[hair-color] = red city.name = emond's field city.region = two rivers mother = persons:2 Complex types are flattened out to their full property path for each of the values provided. If the properties actual value type does not match the declared one the '_class' type hint is added to the entry. city._class = CityInAndor.class city.name = emond's field city.region = two rivers city.country = andor Map and Collection like structures are stored with their key/index values as part of the property path. If the map/collection value type does not match the actutal objects one the '_class' type hint is added to the entry. list.[0]._class = DomainType.class list.[0].property1 = ... map.[key-1]._class = DomainType.class map.[key-1].property1 = ... Properties marked with '@reference' are stored as semantic references by just storing the key to the referenced object 'HASH' instead of embedding its values. mother = persons:2 Please note that referenced objects are not transitively updated/saved and that lazy loading of references will be part of future development. A 'save' operation therefore executes the following: # flatten domain type and add as hash HMSET persons:1 id 1 firstname rand … # add the newly inserted entry to the list of all entries of that type SADD persons 1 # index the firstname for finder lookup SADD persons.firstname:rand 1 Simple finder operation like 'findByFirstname' use 'SINTER' to find matching SINTER persons.firstname:rand HGETALL persons:1 Besides resolving an index via the '@Index' annotation we also allow to add custom configuration via the 'indexConfiguration' attribute of '@EnableRedisRepositories'. @configuration @EnableRedisRepositories(indexConfiguration = CustomIndexConfiguration.class) class Config { } static class CustomIndexConfiguration extends IndexConfiguration { @OverRide protected Iterable<RedisIndexDefinition> initialConfiguration() { return Arrays.asList( new RedisIndexDefinition("persons", "lastname"), ); } }
Since we depend on changes there.
We need to have a dedicated RepositoryFactory to be able to tweak query creation. Now derived queries are freshly instantiated new for every execution.
…ontext. Added "redisTemplateRef" to @EnableRedisRepositories. The default points to "redisTemplate".
- Introduce Bucket to not operate directly on a Map of byte[]. - Introduce IndexData to not operate directly on Map of byte[] for indexes. - Move index updates to IndexWriter. - Additional tests. - Add hamcrest matcher for newly introduced Bucket.
- Renamed IndexedDataWriter to IndexWriter. - Introduced IndexResolver. - Added unit tests for writing index values. - Align keys for indexes with OHM.
- Introduced RedisHash allowing to define TTL for types. - Add RedisKeySpaceEvents. - Add RedisMessageListeners for Keyspace Events - Trigger helper structures clean up based upon Redis events. - Store phantom key to be able to load expired value after key is actually already gone.
- Introduce RedisPersistentEntity. - Use MappingContext for setting up template and Adapter. - Allow programatic TTL configuration.
Enable custom conversions for more influence on conversions. Still needs some polishing aka tests, factoryBeans, documentation and the such but gives a glimpse on what’s the purpose of it. Important: we also need to check for potential index structures on custom converted objects, which has not been implemented so far.
Introduce annotation that allows to mark a single numeric property on aggregate root level to hold a dynamic timeout value. That supersedes any other configured timeout. This allows to define timeouts dynamically on every put/save operation. @RedisHash class Person { @id String id; @timetolive Long ttl; }
Follow changes introduced in the spring-data-keyvalue module.
…found. Allow fields named "id" to be considered as identifier property even when no explicit "@id" annotation is present. Favor fields with explicit id annotation over others and throw MappingException when ambiguous id declaration is found. Additionally fail fast when repository interfaces reference types that do not declare an id property.
We ship converters for JSR-310 types (LocalDate/Time, ZonedDateTime, Period, Duration and ZoneId) to map between UTF-8-encoded byte[] and JDK 8 date/time types. This change requires to build the project on Java 8.
…tion. Provide property information when resolving index structures inside maps and lists. This avoids conversion problems and allows value lookup inside those types.
We removed the burden of converting raw hashes from ReferenceResolver and delegate this to the RedisConverter.
aefd94b
to
69d42c8
Compare
We now export Redis Repositories in a CDI environment. Repositories can be injected using @Inject. The CDI extension requires at least RedisOperations to be provided. Other beans like RedisKeyValueAdapter and RedisKeyValueTemplate can be provided by the user. If no RedisKeyValueAdapter/RedisKeyValueTemplate beans are found, the CDI extension creates own managed instances.
Redis Repositories require currently |
We now favor shadowing fields over using protected accessor methods.
Default query initialization changed to new, so we can delete some code here. nice!
c8e594f
to
e6a946f
Compare
Use @keyspace as meta annotation on @RedisHash and overwrite value using @AliasFor.
We now preserve the item order when converting list elements. This does not mean that we place elements at the exact position retrieved from the store but rather maintain their order based on the index value.
…ed by Hashes and Sets. We now enable storing domain object as a flat Redis 'HASH' and maintain additional 'SET' structures to enable finder operations on simple properties. @RedisHash("persons"); class Person { @id String id; @indexed String firstname; String lastname; Map<String, String> attributes; City city; @reference Person mother; } The above is stored in the HASH with key 'persons:1' as _class = org.example.Person id = 1 firstname = rand lastname = al’thor attributes.[eye-color] = grey attributes.[hair-color] = red city.name = emond's field city.region = two rivers mother = persons:2 Complex types are flattened out to their full property path for each of the values provided. If the properties actual value type does not match the declared one the '_class' type hint is added to the entry. city._class = CityInAndor.class city.name = emond's field city.region = two rivers city.country = andor Map and Collection like structures are stored with their key/index values as part of the property path. If the map/collection value type does not match the actutal objects one the '_class' type hint is added to the entry. list.[0]._class = DomainType.class list.[0].property1 = ... map.[key-1]._class = DomainType.class map.[key-1].property1 = ... Properties marked with '@reference' are stored as semantic references by just storing the key to the referenced object 'HASH' instead of embedding its values. mother = persons:2 Please note that referenced objects are not transitively updated/saved and that lazy loading of references will be part of future development. A 'save' operation therefore executes the following: # flatten domain type and add as hash HMSET persons:1 id 1 firstname rand … # add the newly inserted entry to the list of all entries of that type SADD persons 1 # index the firstname for finder lookup SADD persons.firstname:rand 1 Simple finder operation like 'findByFirstname' use 'SINTER' to find matching SINTER persons.firstname:rand HGETALL persons:1 Besides resolving an index via the '@Index' annotation we also allow to add custom configuration via the 'indexConfiguration' attribute of '@EnableRedisRepositories'. @configuration @EnableRedisRepositories(indexConfiguration = CustomIndexConfiguration.class) class Config { } static class CustomIndexConfiguration extends IndexConfiguration { @OverRide protected Iterable<RedisIndexDefinition> initialConfiguration() { return Arrays.asList( new SimpleIndexDefinition("persons", "lastname"), ); } } The '@timetolive' annotation allows to define a property or method providing an expiration time when storing the key in redis. @RedisHash class Person { @id String id; @timetolive Long ttl; } Original Pull Request: #156
- Add a composite IndexResolver implementation that iterates over a given collection of delegate IndexResolver instances and collects IndexedData from those. - Break up cycle involving ReferenceResolver and let the resolver just returns the raw hash. - Remove IndexType and use dedicated classes for index definitions. - Fix pagination error and follow up to changes introduced via DATAKV-123. Original Pull Request: #156
…ce documentation. We ship converters for JSR-310 types (LocalDate/Time, ZonedDateTime, Period, Duration and ZoneId) to map between UTF-8-encoded byte[] and JDK 8 date/time types. We also export Redis Repositories in a CDI environment. Repositories can be injected using @Inject. The CDI extension requires at least RedisOperations to be provided. Other beans like RedisKeyValueAdapter and RedisKeyValueTemplate can be provided by the user. If no RedisKeyValueAdapter/RedisKeyValueTemplate beans are found, the CDI extension creates own managed instances. Original Pull Request: #156
We now favor shadowing fields over using protected accessor methods. Original Pull Request: #156
Default query initialization changed to new, so we can delete some code here. Original Pull Request: #156
We now preserve the item order when converting list elements. This does not mean that we place elements at the exact position retrieved from the store but rather maintain their order based on the index value. Additionally applied some documentation polishing and cluster tests. Original Pull Request: #156
We now enable storing domain object as a flat Redis HASH and maintain additional SET structures to enable finder operations on simple properties.
The above is stored in the HASH with key
persons:1
asComplex types are flattened out to their full property path for each of the values provided. If the properties actual value type does not match the declared one the
_class
type hint is added to the entry.Map and Collection like structures are stored with their key/index values as part of the property path.
Properties marked with
@Reference
are stored as semantic references by just storing the key to the referenced object HASH instead of embedding its values.Please note that referenced objects are not transitively updated/saved and that lazy loading of references will be part of future development.
A
save
operation therefore executes the following:Simple finder operation like
findByFirstname
use SINTER to find matchingBesides resolving an index via the
@Index
annotation we also allow to add custom configuration via theindexConfiguration
attribute of@EnableRedisRepositories
.