This part of the reference documentation will show some additional configurations and possibilities to customize the process of mapping your Sophora documents to your Java models.
Repository Options
Repositories (classes that inherit from SophoraDocumentRepository
) can be annotated with @RepositoryOptions
. The annotation contains fields to further configure the behavior of the repository. The following settings can be done:
Setting | Possible Values | Default Value | Description |
---|---|---|---|
liveWorkspace | true , false | false | If true , this repository returns the published documents instead of the working versions. |
liveWorkspaceExpression | placeholders like${property.in.config.file} | "" (empty String ) | Variant of liveWorkspace using a placeholder. Using this configuration overrides liveWorkspace. Note: This option does not support SpEL, just placeholders. |
solrCore | the name of any of your Solr cores or placeholders like${property.in.config.file} | "default", or "default-live" if liveWorkspace is true | The Solr core to use when querying for documents. |
overridingComponents | true , false | false | When set to true , enables overriding of properties and child nodes in referenced documents with properties and child nodes of the referencing node. |
repositoryFilterOptionsProviderClass | A class implementing the IRepositoryFilterOptionsProvider interface | DefaultRepositoryFilterOptionsProvider.class | Configurable class to create the RepositoryFilterOptions for the Sophora repository. |
RepositoryOptions
. If a repository defines its own RepositoryOptions
, they replace inherited options from the parent repository.Custom Entity Class Resolver
Sometimes your model class hierarchy is not linear e.g. if you have several model classes for the same node type. By default Spring Data Sophora will choose the model class with the deepest level in the class hierarchy. This approach will not work when there are classes at the same level within the hierarchy. You might also want to programmatically choose the correct model class depending on some conditions.
For all these cases you can implement your custom IEntityClassResolver
. A rudimentary implementation may look like this.
@Component
public class SpecialEntityClassResolver implements IEntityClassResolver {
@Override
public Optional<Class<? extends SophoraEntity>> resolve(INode sourceNode) {
String primaryType = sourceNode.getPrimaryType();
if ("sophora-example-nt:story".equals(primaryType)) {
String externalId = sourceNode.getString("sophora:externalId");
if ("specialSnowflake".equals(externalId)) {
return Optional.of(SpecialStory.class);
}
return Optional.of(MyStory.class);
}
return Optional.empty();
}
}
The above code will choose the SpecialStory
class whenever a story with the external ID "specialSnowflake" shall be converted. For all other stories the MyStory
class is chosen. If the document is no story, we simply return an empty Optional
and let Spring Data Sophora use its default mechanic as described above.
Your entity class resolver must also be registered at your repository. This can easily be done by also inheriting from IWithEntityClassResolverRepository
:
public interface MyStoryRepository extends SophoraDocumentRepository<MyStory>, IWithEntityClassResolverRepository<MyStoryRepository> {
}
and setting your SpecialEntityClassResolver
like this:
@Component
public class RepositoriesProvider {
@Getter
private final MyStoryRepository base;
@Getter
private final MyStoryRepository special;
public RepositoriesProvider(IEntityClassResolver entityClassResolver, MyStoryRepository repository) {
this.base = repository;
this.special = repository.withEntityClassResolver(entityClassResolver);
}
}
Filtering content
If you need to filter the content of your repository for some reason, you have the following possibilities.
TimeFilteredRepository
The interface TimeFilteredRepository
can be used to retrieve content at a specific time. This might be useful to show a preview of a document in the past or future. The time filtering will consider the state of the retrieved document as well as all its referenced documents at the given time. Example
public interface MyStoryRepository extends SophoraDocumentRepository<MyStory>, TimeFilteredRepository<MyStoryRepository> {
}
@Component
public class RepositoriesProvider {
@Getter
private final MyStoryRepository base;
public RepositoriesProvider(MyStoryRepository repository) {
this.base = repository;
}
public MyStoryRepository getBaseAtTime(Instant filterTime) {
return base.atTime(filterTime);
}
}
IFilteredRepository
If you want to do some additional filtering of the documents retrieved from a repository you might use the IFilteredRepository
interface:
public interface MyStoryRepository extends SophoraDocumentRepository<MyStory>, IFilteredRepository<MyStoryRepository> {
}
The interface provides a method called withFilterOptions
which takes a RepositoryFilterOptions
object as argument. This object contains filter settings that will be applied to the content retrieved from the repository. For more information about supported filtering options see a javadoc of RepositoryFilterOptions
.
@Component
public class RepositoriesProvider {
@Getter
private final MyStoryRepository base;
public RepositoriesProvider(MyStoryRepository repository) {
this.base = repository;
}
public MyStoryRepository getBaseWithFilterOptions(RepositoryFilterOptions filterOptions) {
return documentRepository.withFilterOptions(filterOptions);
}
}
IRepositoryFilterOptionsProvider
As shown above the repository content can be filtered with RepositoryFilterOptions
. In some use cases the used filter criteria can be static for each repository call, for example if you like to restrict your application to get documents only from a specific Sophora site. To avoid setting the same RepositoryFilterOptions
for each repository call you can declare the filter options only once by implementing the interface IRepositoryFilterOptionsProvider
:
@Configuration
@RequiredArgsConstructor
public class MyRepositoryFilterOptionsProvider implements IRepositoryFilterOptionsProvider {
private final ISophoraClient sophoraClient;
@Override
public Optional<RepositoryFilterOptions> createRepositoryFilterOptions() {
// create and return your filter options
}
}
and configuring it at the @RepositoryOptions
of your repository:
@RepositoryOptions(repositoryFilterOptionsProviderClass = MyRepositoryFilterOptionsProvider.class)
public interface MyStoryRepository extends SophoraDocumentRepository<MyStory> {
}
In this case an instance RepositoryFilterOptions
returned from MyRepositoryFilterOptionsProvider#createRepositoryFilterOptions
will be created once with the creation of the repository MyStoryRepository
and take affect each time when the repository MyStoryRepository
delivers the content.
IRepositoryFilterOptionsProvider
will be created with the Spring BeanFactory
, so that dependency injection can be used in your implementation as shown in the example.Spring Injection (Autowiring)
Your models will not be registered as Spring beans. So if you want to inject beans into your models you have to configure your model to be "injectable". This can be done by adding the @PerformSpringAutowiring
annotation to your model class. This will allow you to inject any bean that is known to the Spring context into the model:
@NodeType(value = "sophora-example-nt:story")
@PropertyNamespace("sophora-example")
@Data
@PerformSpringAutowiring
public class MyStory extends SophoraDocumentEntity {
@Autowired
private ISophoraClient sophoraClient;
private String title;
}
Searching
When you want to search for documents you usually have to perform a search query. The SophoraDocumentRepository
already has some convenience methods to perform some standard queries:
Search Method | Explanation |
---|---|
findByExternalId(String externalId) | Tries to find the document with the given external ID and returns the appropriate Java model representation. |
findByUuid(UUID uuid) | Tries to find the document with the given UUID and returns the appropriate Java model representation. |
findBySophoraId(String sophoraId) | Tries to find the document with the given Sophora ID and returns the appropriate Java model representation. |
findAll() | Finds all documents in the repository. |
query(IQuery query, SearchParameters searchParameters) | Finds all documents matching the given query and returns a list of their appropriate Java model representations. |
Embedded properties
Spring Data Sophora provides an @Embedded
annotation to declare that properties of a field of a SophoraEntity
are stored as an intrinsic part of the owning entity. This annotation is comparable to the @Embedded
annotation of the Java Persistence API.
In the following example each of the fields of an embedded object GoogleAdsense
is mapped from/to the corresponding property of the MyStory
while reading/writing the data from/to the Sophora repository.
@NodeType(value = "sophora-example-nt:story")
@Data
public class MyStory extends SophoraDocumentEntity {
private String title;
@Embedded
private GoogleAdsense googleAdsense;
...
}
@PropertyNamespace("sophora-example")
@Data
public class GoogleAdsense {
private String googleAdChannel;
private String googleAdClient;
...
}
The @Embedded
annotation can be used to wrap properties and child nodes of a SophoraEntity
in separate classes or to avoid duplicated declarations of some repetitive properties (e.g. of a mixin) on each SophoraEntity
:
public class MyStory extends SophoraDocumentEntity {
@Embedded
private TeaserInfo teaserInfo;
...
}
public class MyVideo extends SophoraDocumentEntity {
@Embedded
private TeaserInfo teaserInfo;
...
}
@PropertyNamespace("sophora-example")
public class TeaserInfo {
private String teaserTitle;
private String teaserText;
@ChildNode
private List<ReferenceEntity<MyImage>> teaserImages
...
}