Skip to content

Commit

Permalink
HSEARCH-4827 Don't allow some projections within a nested context
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta authored and yrodiere committed May 1, 2023
1 parent f646ae1 commit fb76c0a
Show file tree
Hide file tree
Showing 39 changed files with 630 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -803,4 +803,8 @@ SearchException cannotHighlightInNestedContext(String currentNestingField,
@Message(id = ID_OFFSET + 171,
value = "The highlight projection cannot be applied to a field from an object using `ObjectStructure.NESTED` structure.")
SearchException cannotHighlightFieldFromNestedObjectStructure(@Param EventContext eventContext);

@Message(id = ID_OFFSET + 172, value = "'%1$s' cannot be nested in an object projection. "
+ "%2$s")
SearchException cannotUseProjectionInNestedContext(String projection, String hint, @Param EventContext eventContext);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.elasticsearch.reporting.impl;

import org.hibernate.search.engine.backend.reporting.spi.BackendSearchHints;
import org.hibernate.search.util.common.logging.impl.MessageConstants;

import org.jboss.logging.Messages;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageBundle;

@MessageBundle(projectCode = MessageConstants.PROJECT_CODE)
public interface ElasticsearchSearchHints extends BackendSearchHints {

ElasticsearchSearchHints INSTANCE = Messages.getBundle( ElasticsearchSearchHints.class );

@Message(value = "A JSON hit projection represents a root hit object and adding it as a part of the nested object projection might produce misleading results.")
String jsonHitProjectionNestingNotSupportedHint();

@Message(value = "A source projection represents a root source object and adding it as a part of the nested object projection might produce misleading results.")
String sourceProjectionNestingNotSupportedHint();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.backend.common.DocumentReference;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

Expand All @@ -32,6 +34,10 @@ public String toString() {

@Override
public Extractor<?, DocumentReference> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.DOCUMENT_REFERENCE,
ElasticsearchSearchHints.INSTANCE.documentReferenceProjectionNestingNotSupportedHint()
);
helper.request( requestBody, context );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

public class ElasticsearchEntityCompositeProjection<E> extends AbstractElasticsearchProjection<E> {
private final ElasticsearchSearchProjection<E> delegate;

public ElasticsearchEntityCompositeProjection(ElasticsearchSearchIndexScope<?> scope,
ElasticsearchSearchProjection<E> delegate) {
super( scope );
this.delegate = delegate;
}

@Override
public Extractor<?, E> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.ENTITY,
ElasticsearchSearchHints.INSTANCE.entityProjectionNestingNotSupportedHint()
);
return delegate.request( requestBody, context );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

Expand All @@ -30,6 +32,10 @@ public String toString() {

@Override
public Extractor<?, E> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.ENTITY,
ElasticsearchSearchHints.INSTANCE.entityProjectionNestingNotSupportedHint()
);
helper.request( requestBody, context );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.backend.common.DocumentReference;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

Expand All @@ -30,6 +32,10 @@ public String toString() {

@Override
public Extractor<?, R> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.ENTITY_REFERENCE,
ElasticsearchSearchHints.INSTANCE.entityReferenceProjectionNestingNotSupportedHint()
);
helper.request( requestBody, context );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.hibernate.search.backend.elasticsearch.gson.impl.JsonAccessor;
import org.hibernate.search.backend.elasticsearch.gson.impl.JsonObjectAccessor;
import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
Expand All @@ -31,6 +32,10 @@ public String toString() {

@Override
public Extractor<?, JsonObject> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ElasticsearchProjectionTypeKeys.EXPLANATION,
ElasticsearchSearchHints.INSTANCE.explanationProjectionNestingNotSupportedHint()
);
REQUEST_EXPLAIN_ACCESSOR.set( requestBody, true );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.backend.types.converter.spi.ProjectionConverter;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

Expand All @@ -33,7 +35,11 @@ public String toString() {
}

@Override
public Extractor<String, I> request(JsonObject requestBody, ProjectionRequestContext context) {
public Extractor<?, I> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.ID,
ElasticsearchSearchHints.INSTANCE.idProjectionNestingNotSupportedHint()
);
extractionHelper.request( requestBody, context );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
Expand All @@ -26,6 +27,10 @@ public String toString() {

@Override
public Extractor<?, JsonObject> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ElasticsearchProjectionTypeKeys.JSON_HIT,
ElasticsearchSearchHints.INSTANCE.jsonHitProjectionNestingNotSupportedHint()
);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import static org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys.key;

import org.hibernate.search.engine.search.common.spi.SearchQueryElementTypeKey;

public final class ElasticsearchProjectionTypeKeys {

private ElasticsearchProjectionTypeKeys() {
}

public static final SearchQueryElementTypeKey<?> JSON_HIT = key( "json-hit" );
public static final SearchQueryElementTypeKey<?> SOURCE = key( "source" );
public static final SearchQueryElementTypeKey<?> EXPLANATION = key( "explanation" );

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.backend.elasticsearch.gson.impl.JsonAccessor;
import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
import org.hibernate.search.engine.search.projection.spi.ProjectionTypeKeys;

import com.google.gson.JsonObject;

Expand All @@ -30,6 +32,10 @@ public String toString() {

@Override
public Extractor<?, Float> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ProjectionTypeKeys.SCORE,
ElasticsearchSearchHints.INSTANCE.scoreProjectionNestingNotSupportedHint()
);
TRACK_SCORES_ACCESSOR.set( requestBody, true );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ public <T> SearchProjection<T> constant(T value) {
return new ElasticsearchConstantProjection<>( scope, value );
}

@Override
public <T> SearchProjection<T> entityComposite(SearchProjection<T> delegate) {
return new ElasticsearchEntityCompositeProjection<>(
scope, ElasticsearchSearchProjection.from( scope, delegate ) );
}

@Override
public <T> SearchProjection<T> throwing(Supplier<SearchException> exceptionSupplier) {
return new ElasticsearchThrowingProjection<>( scope, exceptionSupplier );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.hibernate.search.backend.elasticsearch.gson.impl.JsonAccessor;
import org.hibernate.search.backend.elasticsearch.gson.impl.JsonArrayAccessor;
import org.hibernate.search.backend.elasticsearch.reporting.impl.ElasticsearchSearchHints;
import org.hibernate.search.backend.elasticsearch.search.common.impl.ElasticsearchSearchIndexScope;
import org.hibernate.search.engine.search.loading.spi.LoadingResult;
import org.hibernate.search.engine.search.loading.spi.ProjectionHitMapper;
Expand All @@ -32,6 +33,10 @@ public String toString() {

@Override
public Extractor<?, JsonObject> request(JsonObject requestBody, ProjectionRequestContext context) {
context.checkNotNested(
ElasticsearchProjectionTypeKeys.SOURCE,
ElasticsearchSearchHints.INSTANCE.sourceProjectionNestingNotSupportedHint()
);
REQUEST_SOURCE_ACCESSOR.addElementIfAbsent( requestBody, WILDCARD_ALL );
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import org.hibernate.search.backend.elasticsearch.logging.impl.Log;
import org.hibernate.search.engine.backend.common.spi.FieldPaths;
import org.hibernate.search.engine.reporting.spi.EventContexts;
import org.hibernate.search.engine.search.common.spi.SearchQueryElementTypeKey;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

public class FieldProjectionRequestContext implements ProjectionRequestContext {
Expand Down Expand Up @@ -45,6 +47,17 @@ public void checkValidField(String absoluteFieldPath) {
}
}

@Override
public void checkNotNested(SearchQueryElementTypeKey<?> projectionKey, String hint) {
if ( absoluteCurrentFieldPath() != null ) {
throw log.cannotUseProjectionInNestedContext(
projectionKey.toString(),
hint,
EventContexts.fromIndexFieldAbsolutePath( absoluteCurrentFieldPath() )
);
}
}

@Override
public ProjectionRequestRootContext root() {
return root;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
*/
package org.hibernate.search.backend.elasticsearch.search.projection.impl;

import org.hibernate.search.engine.search.common.spi.SearchQueryElementTypeKey;

public interface ProjectionRequestContext {

void checkValidField(String absoluteFieldPath);

void checkNotNested(SearchQueryElementTypeKey<?> projectionKey, String hint);

ProjectionRequestRootContext root();

ProjectionRequestContext forField(String absoluteFieldPath, String[] absoluteFieldPathComponents);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.hibernate.search.backend.elasticsearch.search.projection.impl.ProjectionRequestContext;
import org.hibernate.search.backend.elasticsearch.search.projection.impl.ProjectionRequestRootContext;
import org.hibernate.search.engine.backend.session.spi.BackendSessionContext;
import org.hibernate.search.engine.search.common.spi.SearchQueryElementTypeKey;
import org.hibernate.search.engine.search.loading.spi.SearchLoadingContext;
import org.hibernate.search.engine.spatial.GeoPoint;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;
Expand Down Expand Up @@ -92,6 +93,11 @@ public void checkValidField(String absoluteFieldPath) {
// All fields are valid at the root.
}

@Override
public void checkNotNested(SearchQueryElementTypeKey<?> projectionKey, String hint) {
// root is not nested
}

@Override
public ProjectionRequestRootContext root() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,4 +709,8 @@ SearchException cannotHighlightInNestedContext(String currentNestingField,
@Message(id = ID_OFFSET + 173,
value = "The highlight projection cannot be applied to a field from an object using `ObjectStructure.NESTED` structure.")
SearchException cannotHighlightFieldFromNestedObjectStructure(@Param EventContext eventContext);

@Message(id = ID_OFFSET + 174, value = "'%1$s' cannot be nested in an object projection. "
+ "%2$s")
SearchException cannotUseProjectionInNestedContext(String projection, String hint, @Param EventContext eventContext);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.backend.lucene.reporting.impl;

import org.hibernate.search.engine.backend.reporting.spi.BackendSearchHints;
import org.hibernate.search.util.common.logging.impl.MessageConstants;

import org.jboss.logging.Messages;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageBundle;

@MessageBundle(projectCode = MessageConstants.PROJECT_CODE)
public interface LuceneSearchHints extends BackendSearchHints {

LuceneSearchHints INSTANCE = Messages.getBundle( LuceneSearchHints.class );

@Message("A document projection represents a root document and adding it as a part of the nested object projection might produce misleading results.")
String documentProjectionNestingNotSupportedHint();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.search.backend.lucene.search.projection.impl;

import org.hibernate.search.backend.lucene.reporting.impl.LuceneSearchHints;
import org.hibernate.search.backend.lucene.lowlevel.collector.impl.StoredFieldsValuesDelegate;
import org.hibernate.search.backend.lucene.lowlevel.collector.impl.Values;
import org.hibernate.search.backend.lucene.search.common.impl.LuceneSearchIndexScope;
Expand All @@ -28,6 +29,10 @@ public String toString() {

@Override
public Extractor<?, Document> request(ProjectionRequestContext context) {
context.checkNotNested(
LuceneProjectionTypeKeys.DOCUMENT,
LuceneSearchHints.INSTANCE.documentProjectionNestingNotSupportedHint()
);
context.requireAllStoredFields();
return this;
}
Expand Down

0 comments on commit fb76c0a

Please sign in to comment.