DATACOUCH-169 - Automatic index creation
Main indexes (ie indexes that can be used by repositories to retrieve ALL the documents of a particular entity type) are covered. Repository interfaces can be annotated with an annotation for each type of main index that can be automatically created. The IndexManager is responsible for creating said indexes (potentially skipping creation if an index is already in place, or if the IndexManager has been configured to skip it, see constructor). Since IndexManager is created as a bean (either in JavaConfig or xml), there can be a different instance eg. in Prod vs Dev. Added doc and integration test. Renamed package core.view to core.query since it now contains various non-view related annotations and classes.
This commit is contained in:
@@ -44,9 +44,13 @@ import org.springframework.data.couchbase.core.convert.translation.JacksonTransl
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.view.Consistency;
|
||||
import org.springframework.data.couchbase.core.query.Consistency;
|
||||
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.ViewIndexed;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.support.IndexManager;
|
||||
import org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
||||
@@ -227,6 +231,20 @@ public abstract class AbstractCouchbaseConfiguration {
|
||||
return new CustomConversions(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an {@link IndexManager} bean that will be used to process {@link ViewIndexed},
|
||||
* {@link N1qlPrimaryIndexed} and {@link N1qlSecondaryIndexed} annotations on repositories
|
||||
* to automatically create indexes.
|
||||
* <p/>
|
||||
* If this configuration is used in a context where such automatic creations are not desired (eg.
|
||||
* you want automatic index creation in Dev but not in Prod, and this configuration is the Prod one),
|
||||
* override the bean and use the {@link IndexManager#IndexManager(boolean, boolean, boolean)} constructor.
|
||||
*/
|
||||
@Bean(name = BeanNames.COUCHBASE_INDEX_MANAGER)
|
||||
public IndexManager indexManager() {
|
||||
return new IndexManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the mapping base package for classes annotated with {@link Document}.
|
||||
*
|
||||
|
||||
@@ -57,4 +57,9 @@ public class BeanNames {
|
||||
* The bean that stores custom mapping between repositories and their backing couchbaseOperations.
|
||||
*/
|
||||
public static final String REPO_OPERATIONS_MAPPING = "repositoryOperationsMapping";
|
||||
|
||||
/**
|
||||
* The bean that drives how some indexes are automatically created.
|
||||
*/
|
||||
public static final String COUCHBASE_INDEX_MANAGER = "couchbaseIndexManager";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.couchbase.repository.support.IndexManager;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* The XML parser for a {@link IndexManager} definition.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class CouchbaseIndexManagerParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Resolve the bean ID and assign a default if not set.
|
||||
*
|
||||
* @param element the XML element which contains the attributes.
|
||||
* @param definition the bean definition to work with.
|
||||
* @param parserContext encapsulates the parsing state and configuration.
|
||||
* @return the ID to work with.
|
||||
*/
|
||||
@Override
|
||||
protected String resolveId(final Element element, final AbstractBeanDefinition definition, final ParserContext parserContext) {
|
||||
String id = super.resolveId(element, definition, parserContext);
|
||||
return StringUtils.hasText(id) ? id : BeanNames.COUCHBASE_INDEX_MANAGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the bean class that will be constructed.
|
||||
*
|
||||
* @param element the XML element which contains the attributes.
|
||||
* @return the class type to instantiate.
|
||||
*/
|
||||
@Override
|
||||
protected Class getBeanClass(final Element element) {
|
||||
return IndexManager.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the bean definition and build up the bean.
|
||||
*
|
||||
* @param element the XML element which contains the attributes.
|
||||
* @param bean the builder which builds the bean.
|
||||
*/
|
||||
@Override
|
||||
protected void doParse(final Element element, final BeanDefinitionBuilder bean) {
|
||||
boolean ignoreViews = Boolean.parseBoolean(element.getAttribute("ignoreViews"));
|
||||
boolean ignorePrimary = Boolean.parseBoolean(element.getAttribute("ignorePrimary"));
|
||||
boolean ignoreSecondary = Boolean.parseBoolean(element.getAttribute("ignoreSecondary"));
|
||||
|
||||
bean.addConstructorArgValue(ignoreViews);
|
||||
bean.addConstructorArgValue(ignorePrimary);
|
||||
bean.addConstructorArgValue(ignoreSecondary);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -44,6 +44,7 @@ public class CouchbaseNamespaceHandler extends NamespaceHandlerSupport {
|
||||
registerBeanDefinitionParser("jmx", new CouchbaseJmxParser());
|
||||
registerBeanDefinitionParser("template", new CouchbaseTemplateParser());
|
||||
registerBeanDefinitionParser("translation-service", new CouchbaseTranslationServiceParser());
|
||||
registerBeanDefinitionParser("indexManager", new CouchbaseIndexManagerParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.view.Consistency;
|
||||
import org.springframework.data.couchbase.core.query.Consistency;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.couchbase.client.java.view.ViewResult;
|
||||
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.view.Consistency;
|
||||
import org.springframework.data.couchbase.core.query.Consistency;
|
||||
|
||||
/**
|
||||
* Defines common operations on the Couchbase data source, most commonly implemented by {@link CouchbaseTemplate}.
|
||||
|
||||
@@ -38,8 +38,6 @@ import com.couchbase.client.java.error.TranscodingException;
|
||||
import com.couchbase.client.java.query.N1qlQuery;
|
||||
import com.couchbase.client.java.query.N1qlQueryResult;
|
||||
import com.couchbase.client.java.query.N1qlQueryRow;
|
||||
import com.couchbase.client.java.query.N1qlParams;
|
||||
import com.couchbase.client.java.query.consistency.ScanConsistency;
|
||||
import com.couchbase.client.java.util.features.CouchbaseFeature;
|
||||
import com.couchbase.client.java.view.SpatialViewQuery;
|
||||
import com.couchbase.client.java.view.SpatialViewResult;
|
||||
@@ -70,7 +68,7 @@ import org.springframework.data.couchbase.core.mapping.event.BeforeConvertEvent;
|
||||
import org.springframework.data.couchbase.core.mapping.event.BeforeDeleteEvent;
|
||||
import org.springframework.data.couchbase.core.mapping.event.BeforeSaveEvent;
|
||||
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
|
||||
import org.springframework.data.couchbase.core.view.Consistency;
|
||||
import org.springframework.data.couchbase.core.query.Consistency;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.view;
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import com.couchbase.client.java.query.consistency.ScanConsistency;
|
||||
import com.couchbase.client.java.view.Stale;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.view;
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
|
||||
/**
|
||||
* This annotation is targeted at {@link CouchbaseRepository Repository} interfaces, indicating that the framework
|
||||
* should ensure a N1QL Primary Index is present on the repository's associated bucket when the repository is created.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface N1qlPrimaryIndexed {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
|
||||
/**
|
||||
* This annotation is targeted at {@link CouchbaseRepository Repository} interfaces, indicating that
|
||||
* the framework should ensure a N1QL Secondary Index is present when the repository is instantiated.
|
||||
* <p/>
|
||||
* Said index will relate to the "type" field (the one bearing type information) and restrict on documents
|
||||
* that match the repository's entity class.
|
||||
* <p/>
|
||||
* Be sure to also use {@link N1qlPrimaryIndexed} to make sure the PRIMARY INDEX is there as well.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface N1qlSecondaryIndexed {
|
||||
|
||||
/**
|
||||
* the name of the index to be created, in the repository's associated bucket namespace.
|
||||
*/
|
||||
String indexName();
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.view;
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.view;
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -22,8 +22,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
|
||||
/**
|
||||
* Annotation to support the use of Views with Couchbase.
|
||||
*
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation is targeted at {@link CouchbaseRepository Repository} interfaces, indicating that
|
||||
* the framework should ensure a View is present when the repository is instantiated.
|
||||
* <p/>
|
||||
* The view must at least be described as a design document name and view name. Default map function
|
||||
* will filter documents on the type associated to the repository, and default reduce function is "_count".
|
||||
* <p/>
|
||||
* One can specify a custom reduce function as well as a non-default map function. This can be done on methods,
|
||||
* allowing for multiple views per repository to be created.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ViewIndexed {
|
||||
|
||||
/**
|
||||
* The design document in which to create/look for the view.
|
||||
*/
|
||||
String designDoc();
|
||||
|
||||
/**
|
||||
* The name of the view.
|
||||
*/
|
||||
String viewName();
|
||||
|
||||
/**
|
||||
* The map function to use (default is empty, which will trigger a default map function filtering on the
|
||||
* repository's associated entity type).
|
||||
*/
|
||||
String mapFunction() default "";
|
||||
|
||||
/**
|
||||
* The reduce function to use (default is built in "_count" reduce function).
|
||||
*/
|
||||
String reduceFunction() default "_count";
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory;
|
||||
import org.springframework.data.couchbase.repository.support.IndexManager;
|
||||
import org.springframework.data.repository.cdi.CdiRepositoryBean;
|
||||
import org.springframework.data.repository.config.CustomRepositoryImplementationDetector;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -63,7 +64,8 @@ public class CouchbaseRepositoryBean<T> extends CdiRepositoryBean<T> {
|
||||
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType, Object customImplementation) {
|
||||
CouchbaseOperations couchbaseOperations = getDependencyInstance(couchbaseOperationsBean, CouchbaseOperations.class);
|
||||
RepositoryOperationsMapping couchbaseOperationsMapping = new RepositoryOperationsMapping(couchbaseOperations);
|
||||
return new CouchbaseRepositoryFactory(couchbaseOperationsMapping).getRepository(repositoryType, customImplementation);
|
||||
IndexManager indexManager = new IndexManager();
|
||||
return new CouchbaseRepositoryFactory(couchbaseOperationsMapping, indexManager).getRepository(repositoryType, customImplementation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,8 +31,12 @@ import org.springframework.data.repository.config.XmlRepositoryConfigurationSour
|
||||
*/
|
||||
public class CouchbaseRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
/** The reference property to use in xml configuration to specify the template to use with a repository. */
|
||||
private static final String COUCHBASE_TEMPLATE_REF = "couchbase-template-ref";
|
||||
|
||||
/** The reference property to use in xml configuration to specify the index manager bean to use with a repository. */
|
||||
private static final String COUCHBASE_INDEX_MANAGER_REF = "couchbase-index-manager-ref";
|
||||
|
||||
@Override
|
||||
protected String getModulePrefix() {
|
||||
return "couchbase";
|
||||
@@ -46,11 +50,14 @@ public class CouchbaseRepositoryConfigurationExtension extends RepositoryConfigu
|
||||
public void postProcess(final BeanDefinitionBuilder builder, final XmlRepositoryConfigurationSource config) {
|
||||
Element element = config.getElement();
|
||||
ParsingUtils.setPropertyReference(builder, element, COUCHBASE_TEMPLATE_REF, "couchbaseOperations");
|
||||
ParsingUtils.setPropertyReference(builder, element, COUCHBASE_INDEX_MANAGER_REF, "indexManager");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(final BeanDefinitionBuilder builder, final AnnotationRepositoryConfigurationSource config) {
|
||||
builder.addDependsOn(BeanNames.REPO_OPERATIONS_MAPPING);
|
||||
builder.addDependsOn(BeanNames.COUCHBASE_INDEX_MANAGER);
|
||||
builder.addPropertyReference("couchbaseOperationsMapping", BeanNames.REPO_OPERATIONS_MAPPING);
|
||||
builder.addPropertyReference("indexManager", BeanNames.COUCHBASE_INDEX_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import java.lang.reflect.Method;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.view.Dimensional;
|
||||
import org.springframework.data.couchbase.core.view.Query;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.Dimensional;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.view.Dimensional;
|
||||
import org.springframework.data.couchbase.core.query.Dimensional;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
@@ -20,18 +20,13 @@ import java.util.Iterator;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.view.SpatialViewQuery;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.view.Dimensional;
|
||||
import org.springframework.data.couchbase.core.query.Dimensional;
|
||||
import org.springframework.data.couchbase.repository.query.support.GeoUtils;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.data.geo.Polygon;
|
||||
import org.springframework.data.geo.Shape;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
@@ -52,7 +52,7 @@ public class StringN1qlBasedQuery extends AbstractN1qlBasedQuery {
|
||||
public static final String SPEL_PREFIX = "n1ql";
|
||||
|
||||
/**
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.view.Query @Query}
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
|
||||
* annotation's inline statement. This will be replaced by the correct <code>SELECT x FROM y</code> clause needed
|
||||
* for entity mapping. Eg. <code>"${{@value StringN1qlBasedQuery#SPEL_SELECT_FROM_CLAUSE}} WHERE test = true"</code>.
|
||||
* Note this only makes sense once, as the beginning of the statement.
|
||||
@@ -60,21 +60,21 @@ public class StringN1qlBasedQuery extends AbstractN1qlBasedQuery {
|
||||
public static final String SPEL_SELECT_FROM_CLAUSE = "#" + SPEL_PREFIX + ".selectEntity";
|
||||
|
||||
/**
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.view.Query @Query}
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
|
||||
* annotation's inline statement. This will be replaced by the (escaped) bucket name corresponding to the repository's
|
||||
* entity. Eg. <code>"SELECT * FROM ${{@value StringN1qlBasedQuery#SPEL_BUCKET}} LIMIT 3"</code>.
|
||||
*/
|
||||
public static final String SPEL_BUCKET = "#" + SPEL_PREFIX + ".bucket";
|
||||
|
||||
/**
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.view.Query @Query}
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
|
||||
* annotation's inline statement. This will be replaced by the fields allowing to construct the repository's entity
|
||||
* (SELECT clause). Eg. <code>"SELECT ${{@value StringN1qlBasedQuery#SPEL_ENTITY}} FROM test"</code>.
|
||||
*/
|
||||
public static final String SPEL_ENTITY = "#" + SPEL_PREFIX + ".fields";
|
||||
|
||||
/**
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.view.Query @Query}
|
||||
* Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
|
||||
* annotation's inline statement WHERE clause. This will be replaced by the expression allowing to only select
|
||||
* documents matching the entity's class. Eg. <code>"SELECT * FROM test WHERE test = true AND ${{@value StringN1qlBasedQuery#SPEL_FILTER}}"</code>.
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ import com.couchbase.client.java.document.json.JsonObject;
|
||||
import com.couchbase.client.java.view.ViewQuery;
|
||||
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
|
||||
@@ -26,8 +26,11 @@ import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.UnsupportedCouchbaseFeatureException;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.view.Query;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
import org.springframework.data.couchbase.core.query.ViewIndexed;
|
||||
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
|
||||
@@ -51,6 +54,7 @@ import org.springframework.util.Assert;
|
||||
* Factory to create {@link SimpleCouchbaseRepository} instances.
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
@@ -61,6 +65,11 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
*/
|
||||
private final RepositoryOperationsMapping couchbaseOperationsMapping;
|
||||
|
||||
/**
|
||||
* Holds the reference to the {@link IndexManager}.
|
||||
*/
|
||||
private final IndexManager indexManager;
|
||||
|
||||
/**
|
||||
* Holds the mapping context.
|
||||
*/
|
||||
@@ -76,10 +85,12 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
*
|
||||
* @param couchbaseOperationsMapping the template for the underlying actions.
|
||||
*/
|
||||
public CouchbaseRepositoryFactory(final RepositoryOperationsMapping couchbaseOperationsMapping) {
|
||||
public CouchbaseRepositoryFactory(final RepositoryOperationsMapping couchbaseOperationsMapping, final IndexManager indexManager) {
|
||||
Assert.notNull(couchbaseOperationsMapping);
|
||||
Assert.notNull(indexManager);
|
||||
|
||||
this.couchbaseOperationsMapping = couchbaseOperationsMapping;
|
||||
this.indexManager = indexManager;
|
||||
mappingContext = this.couchbaseOperationsMapping.getDefault().getConverter().getMappingContext();
|
||||
viewPostProcessor = ViewPostProcessor.INSTANCE;
|
||||
|
||||
@@ -117,7 +128,14 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
protected Object getTargetRepository(final RepositoryInformation metadata) {
|
||||
CouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(metadata.getRepositoryInterface(), metadata.getDomainType());
|
||||
boolean isN1qlAvailable = couchbaseOperations.getCouchbaseClusterInfo().checkAvailable(CouchbaseFeature.N1QL);
|
||||
checkFeatures(metadata, isN1qlAvailable);
|
||||
|
||||
ViewIndexed viewIndexed = AnnotationUtils.findAnnotation(metadata.getRepositoryInterface(), ViewIndexed.class);
|
||||
N1qlPrimaryIndexed n1qlPrimaryIndexed = AnnotationUtils.findAnnotation(metadata.getRepositoryInterface(), N1qlPrimaryIndexed.class);
|
||||
N1qlSecondaryIndexed n1qlSecondaryIndexed = AnnotationUtils.findAnnotation(metadata.getRepositoryInterface(), N1qlSecondaryIndexed.class);
|
||||
|
||||
checkFeatures(metadata, isN1qlAvailable, n1qlPrimaryIndexed, n1qlSecondaryIndexed);
|
||||
|
||||
indexManager.buildIndexes(metadata, viewIndexed, n1qlPrimaryIndexed, n1qlSecondaryIndexed, couchbaseOperations);
|
||||
|
||||
CouchbaseEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
|
||||
if (isN1qlAvailable) {
|
||||
@@ -132,10 +150,14 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFeatures(RepositoryInformation metadata, boolean isN1qlAvailable) {
|
||||
boolean needsN1ql = metadata.isPagingRepository();
|
||||
//paging repo will need N1QL, other repos might also if they don't have only @View methods
|
||||
private void checkFeatures(RepositoryInformation metadata, boolean isN1qlAvailable,
|
||||
N1qlPrimaryIndexed n1qlPrimaryIndexed, N1qlSecondaryIndexed n1qlSecondaryIndexed) {
|
||||
//paging repo will always need N1QL, also check if the repository requires a N1QL index
|
||||
boolean needsN1ql = metadata.isPagingRepository() || n1qlPrimaryIndexed != null || n1qlSecondaryIndexed != null;
|
||||
|
||||
//for other repos, they might also need N1QL if they don't have only @View methods
|
||||
if (!needsN1ql) {
|
||||
|
||||
for (Method method : metadata.getQueryMethods()) {
|
||||
|
||||
boolean hasN1ql = AnnotationUtils.findAnnotation(method, Query.class) != null;
|
||||
|
||||
@@ -38,6 +38,11 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
*/
|
||||
private RepositoryOperationsMapping operationsMapping;
|
||||
|
||||
/**
|
||||
* Contains the reference to the IndexManager.
|
||||
*/
|
||||
private IndexManager indexManager;
|
||||
|
||||
/**
|
||||
* Set the template reference.
|
||||
*
|
||||
@@ -52,6 +57,15 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
setMappingContext(operationsMapping.getDefault().getConverter().getMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IndexManager reference.
|
||||
*
|
||||
* @param indexManager the IndexManager to use.
|
||||
*/
|
||||
public void setIndexManager(final IndexManager indexManager) {
|
||||
this.indexManager = indexManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a factory instance.
|
||||
*
|
||||
@@ -59,25 +73,28 @@ public class CouchbaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID e
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
return getFactoryInstance(operationsMapping);
|
||||
return getFactoryInstance(operationsMapping, indexManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factory instance for the operations.
|
||||
*
|
||||
* @param operationsMapping the reference to the template.
|
||||
* @param indexManager the reference to the {@link IndexManager}.
|
||||
* @return the factory instance.
|
||||
*/
|
||||
private RepositoryFactorySupport getFactoryInstance(final RepositoryOperationsMapping operationsMapping) {
|
||||
return new CouchbaseRepositoryFactory(operationsMapping);
|
||||
private RepositoryFactorySupport getFactoryInstance(final RepositoryOperationsMapping operationsMapping,
|
||||
IndexManager indexManager) {
|
||||
return new CouchbaseRepositoryFactory(operationsMapping, indexManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the template is set and not null.
|
||||
* Make sure that the dependencies are set and not null.
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
Assert.notNull(operationsMapping, "operationsMapping must not be null!");
|
||||
Assert.notNull(indexManager, "indexManager must not be null!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.repository.support;
|
||||
|
||||
import static com.couchbase.client.java.query.dsl.Expression.s;
|
||||
import static com.couchbase.client.java.query.dsl.Expression.x;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import com.couchbase.client.java.bucket.BucketManager;
|
||||
import com.couchbase.client.java.document.json.JsonObject;
|
||||
import com.couchbase.client.java.query.AsyncN1qlQueryResult;
|
||||
import com.couchbase.client.java.query.Index;
|
||||
import com.couchbase.client.java.query.Statement;
|
||||
import com.couchbase.client.java.query.dsl.path.index.IndexType;
|
||||
import com.couchbase.client.java.view.DefaultView;
|
||||
import com.couchbase.client.java.view.DesignDocument;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import rx.Observable;
|
||||
import rx.exceptions.CompositeException;
|
||||
import rx.functions.Action1;
|
||||
import rx.functions.Func1;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
|
||||
import org.springframework.data.couchbase.core.query.N1qlPrimaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.N1qlSecondaryIndexed;
|
||||
import org.springframework.data.couchbase.core.query.ViewIndexed;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
|
||||
/**
|
||||
* {@link IndexManager} is responsible for automatic index creation according to the provided metadata and
|
||||
* various index annotations (if not null).
|
||||
* <p/>
|
||||
* Index creation will be attempted in parallel using the asynchronous APIs, but the overall process is still blocking.
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class IndexManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(IndexManager.class);
|
||||
|
||||
private static final String TEMPLATE_MAP_FUNCTION = "function (doc, meta) { if(doc.%s == \"%s\") { emit(null, null); } }";
|
||||
|
||||
private static final JsonObject SUCCESS_MARKER = JsonObject.empty();
|
||||
|
||||
/** True if this index manager should ignore view creation annotations */
|
||||
private boolean ignoreViews;
|
||||
/** True if this index manager should ignore N1QL PRIMARY creation annotations */
|
||||
private boolean ignoreN1qlPrimary;
|
||||
/** True if this index manager should ignore N1QL SECONDARY creation annotations */
|
||||
private boolean ignoreN1qlSecondary;
|
||||
|
||||
|
||||
/**
|
||||
* Construct an IndexManager that can be used as a Bean in a {@link Profile @Profile} annotated configuration
|
||||
* in order to ignore all or part of automatic index creations in some contexts (like activating it in Dev but
|
||||
* not in Prod).
|
||||
*
|
||||
* @param ignoreViews true to ignore {@link ViewIndexed} annotations.
|
||||
* @param ignoreN1qlPrimary true to ignore {@link N1qlPrimaryIndexed} annotations.
|
||||
* @param ignoreN1qlSecondary true to ignore {@link N1qlSecondaryIndexed} annotations.
|
||||
*/
|
||||
public IndexManager(boolean ignoreViews, boolean ignoreN1qlPrimary, boolean ignoreN1qlSecondary) {
|
||||
this.ignoreViews = ignoreViews;
|
||||
this.ignoreN1qlPrimary = ignoreN1qlPrimary;
|
||||
this.ignoreN1qlSecondary = ignoreN1qlSecondary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a default IndexManager that process all three types of automatic index creations.
|
||||
*/
|
||||
public IndexManager() {
|
||||
this(false, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this IndexManager ignores {@link ViewIndexed} annotations.
|
||||
*/
|
||||
public boolean isIgnoreViews() {
|
||||
return ignoreViews;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this IndexManager ignores {@link N1qlPrimaryIndexed} annotations.
|
||||
*/
|
||||
public boolean isIgnoreN1qlPrimary() {
|
||||
return ignoreN1qlPrimary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this IndexManager ignores {@link N1qlSecondaryIndexed} annotations.
|
||||
*/
|
||||
public boolean isIgnoreN1qlSecondary() {
|
||||
return ignoreN1qlSecondary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the relevant indexes according to the provided annotation and repository metadata, in parallel but blocking
|
||||
* until all relevant indexes are created. Existing indexes will be detected and skipped.
|
||||
* <p/>
|
||||
* Note that this IndexManager could be configured to ignore some of the annotation types.
|
||||
* In case of multiple errors, a {@link CompositeException} can be raised with up to 3 causes (one per type of index).
|
||||
*
|
||||
* @param metadata the repository's metadata (allowing to find out the type of entity stored, the key under which type
|
||||
* information is stored, etc...).
|
||||
* @param viewIndexed the annotation for creation of a View-based index.
|
||||
* @param n1qlPrimaryIndexed the annotation for creation of a N1QL-based primary index (generic).
|
||||
* @param n1qlSecondaryIndexed the annotation for creation of a N1QL-based secondary index (specific to the repository
|
||||
* stored entity).
|
||||
* @param couchbaseOperations the template to use for index creation.
|
||||
* @throws CompositeException when several errors (for multiple index types) have been raised.
|
||||
*/
|
||||
public void buildIndexes(RepositoryInformation metadata, ViewIndexed viewIndexed, N1qlPrimaryIndexed n1qlPrimaryIndexed,
|
||||
N1qlSecondaryIndexed n1qlSecondaryIndexed, CouchbaseOperations couchbaseOperations) {
|
||||
Observable<Void> viewAsync = Observable.empty();
|
||||
Observable<Void> n1qlPrimaryAsync = Observable.empty();
|
||||
Observable<Void> n1qlSecondaryAsync = Observable.empty();
|
||||
|
||||
if (viewIndexed != null && !ignoreViews) {
|
||||
viewAsync = buildAllView(viewIndexed, metadata, couchbaseOperations);
|
||||
}
|
||||
|
||||
if (n1qlPrimaryIndexed != null && !ignoreN1qlPrimary) {
|
||||
n1qlPrimaryAsync = buildN1qlPrimary(metadata, couchbaseOperations);
|
||||
}
|
||||
|
||||
if (n1qlSecondaryIndexed != null && !ignoreN1qlSecondary) {
|
||||
n1qlSecondaryAsync = buildN1qlSecondary(n1qlSecondaryIndexed, metadata, couchbaseOperations);
|
||||
}
|
||||
|
||||
//trigger the builds, wait for the last one, throw CompositeException if errors
|
||||
Observable.mergeDelayError(viewAsync, n1qlPrimaryAsync, n1qlSecondaryAsync)
|
||||
.toBlocking()
|
||||
.lastOrDefault(null);
|
||||
}
|
||||
|
||||
private Observable<Void> buildN1qlPrimary(final RepositoryInformation metadata, CouchbaseOperations couchbaseOperations) {
|
||||
final String bucketName = couchbaseOperations.getCouchbaseBucket().name();
|
||||
Statement createPrimary = Index.createPrimaryIndex()
|
||||
.on(bucketName)
|
||||
.using(IndexType.GSI);
|
||||
|
||||
LOGGER.debug("Creating N1QL primary index for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
return couchbaseOperations.getCouchbaseBucket().async().query(createPrimary)
|
||||
.flatMap(new Func1<AsyncN1qlQueryResult, Observable<JsonObject>>() {
|
||||
@Override
|
||||
public Observable<JsonObject> call(AsyncN1qlQueryResult asyncN1qlQueryResult) {
|
||||
return asyncN1qlQueryResult.errors();
|
||||
}
|
||||
})
|
||||
.defaultIfEmpty(SUCCESS_MARKER)
|
||||
.flatMap(new Func1<JsonObject, Observable<Void>>() {
|
||||
@Override
|
||||
public Observable<Void> call(JsonObject json) {
|
||||
if (json == SUCCESS_MARKER) {
|
||||
LOGGER.debug("N1QL primary index created for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
return Observable.empty();
|
||||
} else if (json.getString("msg").contains("Index #primary already exist")) {
|
||||
LOGGER.debug("Primary index already exist, skipping");
|
||||
return Observable.empty(); //ignore, the index already exist
|
||||
} else {
|
||||
return Observable.error(new CouchbaseQueryExecutionException(
|
||||
"Cannot create N1QL primary index on " + bucketName + ": " + json));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Observable<Void> buildN1qlSecondary(N1qlSecondaryIndexed config, final RepositoryInformation metadata, CouchbaseOperations couchbaseOperations) {
|
||||
final String bucketName = couchbaseOperations.getCouchbaseBucket().name();
|
||||
final String indexName = config.indexName();
|
||||
String typeKey = couchbaseOperations.getConverter().getTypeKey();
|
||||
final String type = metadata.getDomainType().getName();
|
||||
|
||||
Statement createIndex = Index.createIndex(indexName)
|
||||
.on(bucketName, x(typeKey))
|
||||
.where(x(typeKey).eq(s(type)))
|
||||
.using(IndexType.GSI);
|
||||
|
||||
LOGGER.debug("Creating N1QL secondary index for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
return couchbaseOperations.getCouchbaseBucket().async().query(createIndex)
|
||||
.flatMap(new Func1<AsyncN1qlQueryResult, Observable<JsonObject>>() {
|
||||
@Override
|
||||
public Observable<JsonObject> call(AsyncN1qlQueryResult asyncN1qlQueryResult) {
|
||||
return asyncN1qlQueryResult.errors();
|
||||
}
|
||||
})
|
||||
.defaultIfEmpty(SUCCESS_MARKER)
|
||||
.flatMap(new Func1<JsonObject, Observable<Void>>() {
|
||||
@Override
|
||||
public Observable<Void> call(JsonObject json) {
|
||||
if (json == SUCCESS_MARKER) {
|
||||
LOGGER.debug("N1QL secondary index created for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
return Observable.empty();
|
||||
} else if (json.getString("msg").contains("Index " + indexName + " already exist")) {
|
||||
LOGGER.debug("Secondary index already exist, skipping");
|
||||
return Observable.empty(); //ignore, the index already exist
|
||||
} else {
|
||||
return Observable.error(new CouchbaseQueryExecutionException(
|
||||
"Cannot create N1QL secondary index " + bucketName + "." + indexName + " for " + type + ": " + json));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Observable<Void> buildAllView(ViewIndexed config, final RepositoryInformation metadata, CouchbaseOperations couchbaseOperations) {
|
||||
if (config == null) return Observable.empty();
|
||||
LOGGER.debug("Creating View index index for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
|
||||
BucketManager manager = couchbaseOperations.getCouchbaseBucket().bucketManager();
|
||||
String viewName = config.viewName();
|
||||
String mapFunction = config.mapFunction();
|
||||
if (mapFunction.isEmpty()) {
|
||||
String typeKey = couchbaseOperations.getConverter().getTypeKey();
|
||||
String type = metadata.getDomainType().getName();
|
||||
|
||||
mapFunction = String.format(TEMPLATE_MAP_FUNCTION, typeKey, type);
|
||||
}
|
||||
String reduceFunction = config.reduceFunction();
|
||||
if ("".equals(reduceFunction)) {
|
||||
reduceFunction = null;
|
||||
}
|
||||
|
||||
com.couchbase.client.java.view.View view = DefaultView.create(viewName, mapFunction, reduceFunction);
|
||||
DesignDocument doc = manager.getDesignDocument(config.designDoc());
|
||||
if (doc != null) {
|
||||
for (com.couchbase.client.java.view.View existingView : doc.views()) {
|
||||
if (existingView.name().equals(viewName)) {
|
||||
LOGGER.debug("View index {}/{} already exist, skipping", config.designDoc(), viewName);
|
||||
return Observable.empty(); //abort, the view already exist
|
||||
}
|
||||
}
|
||||
doc.views().add(view);
|
||||
} else {
|
||||
doc = DesignDocument.create(config.designDoc(), Collections.singletonList(view));
|
||||
}
|
||||
return manager.async().upsertDesignDocument(doc)
|
||||
.map(new Func1<DesignDocument, Void>() {
|
||||
@Override
|
||||
public Void call(DesignDocument designDocument) {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.doOnNext(new Action1<Void>() {
|
||||
@Override
|
||||
public void call(Void aVoid) {
|
||||
LOGGER.debug("View index created for repository {}", metadata.getRepositoryInterface().getSimpleName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,9 @@ import com.couchbase.client.java.view.ViewResult;
|
||||
import com.couchbase.client.java.view.ViewRow;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
import org.springframework.data.couchbase.repository.CouchbaseRepository;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.repository.support;
|
||||
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
|
||||
/**
|
||||
* Interface to abstract {@link ViewMetadataProvider} that provides {@link View}s to be used for query execution.
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.couchbase.core.view.View;
|
||||
import org.springframework.data.couchbase.core.query.View;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user