Spring Data Sophora 4

Advanced Tips and Tricks

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.

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:

SettingPossible ValuesDefault ValueDescription
liveWorkspacetrue, falsefalseIf true, this repository returns the published documents instead of the working versions.
liveWorkspaceExpressionplaceholders 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.
solrCorethe name of any of your Solr cores or placeholders like
${property.in.config.file}
"default", or "default-live" if liveWorkspace is trueThe Solr core to use when querying for documents.
overridingComponentstrue, falsefalseWhen set to true, enables overriding of properties and child nodes in referenced documents with properties and child nodes of the referencing node.
repositoryFilterOptionsProviderClassA class implementing the IRepositoryFilterOptionsProvider interfaceDefaultRepositoryFilterOptionsProvider.classConfigurable class to create the RepositoryFilterOptions for the Sophora 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.

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 MethodExplanation
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
    ...
}

Last modified on 8/5/21

The content of this page is licensed under the CC BY 4.0 License. Code samples are licensed under the MIT License.

Icon