diff --git a/builtin-adapter/BuiltinAdapter.cpp b/builtin-adapter/BuiltinAdapter.cpp index 1a1b7cc1..30119c22 100644 --- a/builtin-adapter/BuiltinAdapter.cpp +++ b/builtin-adapter/BuiltinAdapter.cpp @@ -162,6 +162,32 @@ JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinA return res; } +JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_updateIndex(JNIEnv *env, jobject, + jlong rx, jlong ctxId, + jlong timeout, + jstring namespaceName, + jstring indexJson) { + reindexer_string nsName = rx_string(env, namespaceName); + reindexer_string indexDefJson = rx_string(env, indexJson); + jobject res = j_res(env, reindexer_update_index(rx, nsName, indexDefJson, rx_ctx(ctxId, timeout))); + env->ReleaseStringUTFChars(namespaceName, reinterpret_cast(nsName.p)); + env->ReleaseStringUTFChars(indexJson, reinterpret_cast(indexDefJson.p)); + return res; +} + +JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_dropIndex(JNIEnv *env, jobject, + jlong rx, jlong ctxId, + jlong timeout, + jstring namespaceName, + jstring indexName) { + reindexer_string nsName = rx_string(env, namespaceName); + reindexer_string index = rx_string(env, indexName); + jobject res = j_res(env, reindexer_drop_index(rx, nsName, index, rx_ctx(ctxId, timeout))); + env->ReleaseStringUTFChars(namespaceName, reinterpret_cast(nsName.p)); + env->ReleaseStringUTFChars(indexName, reinterpret_cast(index.p)); + return res; +} + JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_modifyItem(JNIEnv *env, jobject, jlong rx, jlong ctxId, jlong timeout, diff --git a/builtin-adapter/BuiltinAdapter.h b/builtin-adapter/BuiltinAdapter.h index 0e6c07e4..0575c38e 100644 --- a/builtin-adapter/BuiltinAdapter.h +++ b/builtin-adapter/BuiltinAdapter.h @@ -47,6 +47,14 @@ JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinA jlong, jlong, jlong, jstring, jstring); +JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_updateIndex(JNIEnv *, jobject, + jlong, jlong, jlong, + jstring, jstring); + +JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_dropIndex(JNIEnv *, jobject, + jlong, jlong, jlong, + jstring, jstring); + JNIEXPORT jobject JNICALL Java_ru_rt_restream_reindexer_binding_builtin_BuiltinAdapter_modifyItem(JNIEnv *, jobject, jlong, jlong, jlong, jbyteArray, diff --git a/pom.xml b/pom.xml index e5affc01..f3c57c55 100644 --- a/pom.xml +++ b/pom.xml @@ -274,6 +274,12 @@ 2.2 test + + org.projectlombok + lombok + 1.18.34 + provided + diff --git a/src/main/java/ru/rt/restream/reindexer/Reindexer.java b/src/main/java/ru/rt/restream/reindexer/Reindexer.java index eefd16a0..6213d536 100644 --- a/src/main/java/ru/rt/restream/reindexer/Reindexer.java +++ b/src/main/java/ru/rt/restream/reindexer/Reindexer.java @@ -282,6 +282,32 @@ public Query query(String namespaceName, Class clazz) { return new Query<>(this, namespace, null); } + /** + * ONLY FOR TEST PURPOSES! + */ + @Deprecated + public void addIndex(String namespaceName, ReindexerIndex index) { + IndexDefinition indexDefinition = IndexDefinition.fromIndex(index); + binding.addIndex(namespaceName, indexDefinition); + } + + /** + * ONLY FOR TEST PURPOSES! + */ + @Deprecated + public void updateIndex(String namespaceName, ReindexerIndex index) { + IndexDefinition indexDefinition = IndexDefinition.fromIndex(index); + binding.updateIndex(namespaceName, indexDefinition); + } + + /** + * ONLY FOR TEST PURPOSES! + */ + @Deprecated + public void dropIndex(String namespaceName, String indexName) { + binding.dropIndex(namespaceName, indexName); + } + private ReindexerNamespace getNamespace(String namespaceName, Class itemClass) { ReindexerNamespace namespace = namespaceMap.get(namespaceName); if (namespace == null) { diff --git a/src/main/java/ru/rt/restream/reindexer/ReindexerIndex.java b/src/main/java/ru/rt/restream/reindexer/ReindexerIndex.java index eea86a4d..c0ac7970 100644 --- a/src/main/java/ru/rt/restream/reindexer/ReindexerIndex.java +++ b/src/main/java/ru/rt/restream/reindexer/ReindexerIndex.java @@ -15,17 +15,32 @@ */ package ru.rt.restream.reindexer; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import ru.rt.restream.reindexer.fulltext.FullTextConfig; import java.util.List; /** * Contains the reindexer index configuration. + * + * `equals()` is used to compare index configuration without the jsonPaths field. */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@EqualsAndHashCode public class ReindexerIndex { private String name; + @EqualsAndHashCode.Exclude private List jsonPaths; private IndexType indexType; @@ -36,12 +51,23 @@ public class ReindexerIndex { private String sortOrder; + /** + * Full text search config for current index. + * Type of index must be TEXT. + */ private FullTextConfig fullTextConfig; + /** + * Precept is a special reindexer embedded function, such as serial(), now(). + * {@link ru.rt.restream.reindexer.annotations.Serial} + */ private String precept; private boolean isArray; + /** + * Indication, that the current index is a primary key. + */ private boolean isPk; private boolean isDense; @@ -50,123 +76,7 @@ public class ReindexerIndex { private boolean isUuid; - /** - * Get the current index name. - * - * @return the current index name - */ - public String getName() { - return name; - } - - /** - * Set the current index name. - * - * @param name the index name - */ - public void setName(String name) { - this.name = name; - } - - /** - * Get the current index json paths. - * - * @return the current index json paths - */ - public List getJsonPaths() { - return jsonPaths; - } - - /** - * Set the current index json paths. - * - * @param jsonPaths the index json paths - */ - public void setJsonPaths(List jsonPaths) { - this.jsonPaths = jsonPaths; - } - - /** - * Get the current index type. {@link IndexType} - * - * @return the current index type - */ - public IndexType getIndexType() { - return indexType; - } - - /** - * Set the current index type. {@link IndexType} - * - * @param indexType the index type - */ - public void setIndexType(IndexType indexType) { - this.indexType = indexType; - } - - /** - * Get the current index field type. {@link FieldType} - * - * @return the current index field type - */ - public FieldType getFieldType() { - return fieldType; - } - - /** - * Set the current index field type. {@link FieldType} - * - * @param fieldType the index field type - */ - public void setFieldType(FieldType fieldType) { - this.fieldType = fieldType; - } - - /** - * Get the current index collate mode. {@link CollateMode} - * - * @return the current index collate mode - */ - public CollateMode getCollateMode() { - return collateMode; - } - - /** - * Set the current index collate mode. {@link CollateMode} - * - * @param collateMode the index collate mode - */ - public void setCollateMode(CollateMode collateMode) { - this.collateMode = collateMode; - } - - /** - * Get the current index sort order. - * - * @return the current index sort order string - */ - public String getSortOrder() { - return sortOrder; - } - - /** - * Set the current index sort order. - * - * @param sortOrder the sequence of letters, which defines the index sort order - */ - public void setSortOrder(String sortOrder) { - this.sortOrder = sortOrder; - } - - /** - * Get full text search config for current index. - * Type of index must be TEXT. - * - * @return full text search config - */ - public FullTextConfig getFullTextConfig() { - return fullTextConfig; - } + private boolean isAppendable; /** * Set full text search config for current index, if the index is text index. @@ -181,113 +91,4 @@ public void setFullTextConfig(FullTextConfig fullTextConfig) { this.fullTextConfig = fullTextConfig; } - /** - * Get the current index precept. Precept is a special reindexer embedded function, such as serial(), now(). - * {@link ru.rt.restream.reindexer.annotations.Serial} - * - * @return the current index precept - */ - public String getPrecept() { - return precept; - } - - /** - * Set the current index precepts. Precept is a special reindexer embedded function, such as serial(), now(). - * {@link ru.rt.restream.reindexer.annotations.Serial} - * - * @param precept the index precept - */ - public void setPrecept(String precept) { - this.precept = precept; - } - - /** - * Get the indication, that the current index is array. - * - * @return true, if the current index is array - */ - public boolean isArray() { - return isArray; - } - - /** - * Set the indication, that the current index is array. - * - * @param array true, if the current index is array - */ - public void setArray(boolean array) { - isArray = array; - } - - /** - * Get the indication, that the current index is a primary key. - * - * @return true, if the current index is a primary key - */ - public boolean isPk() { - return isPk; - } - - /** - * Set the indication, that the current index is a primary key. - * - * @param pk true, if the current index is a primary key - */ - public void setPk(boolean pk) { - isPk = pk; - } - - /** - * Get the indication, that the current index is dense. - * - * @return true, if the current index is dense - */ - public boolean isDense() { - return isDense; - } - - /** - * Set the indication, that the current index is dense. - * - * @param dense true, if the current index is dense - */ - public void setDense(boolean dense) { - isDense = dense; - } - - /** - * Get the indication, that the current index is sparse. - * - * @return true, if the current index is sparse - */ - public boolean isSparse() { - return isSparse; - } - - /** - * Set the indication, that the current index is sparse. - * - * @param sparse true, if the current index is sparse - */ - public void setSparse(boolean sparse) { - isSparse = sparse; - } - - /** - * Get the indication, that the current index is for UUID. - * - * @return true, if the current index is for UUID - */ - public boolean isUuid() { - return isUuid; - } - - /** - * Set the indication, that the current index is for UUID. - * - * @param uuid true, if the current index is for UUID - */ - public void setUuid(boolean uuid) { - isUuid = uuid; - } } diff --git a/src/main/java/ru/rt/restream/reindexer/annotations/ReindexAnnotationScanner.java b/src/main/java/ru/rt/restream/reindexer/annotations/ReindexAnnotationScanner.java index 4523dc74..4ca5a0f4 100644 --- a/src/main/java/ru/rt/restream/reindexer/annotations/ReindexAnnotationScanner.java +++ b/src/main/java/ru/rt/restream/reindexer/annotations/ReindexAnnotationScanner.java @@ -32,10 +32,8 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import static ru.rt.restream.reindexer.FieldType.BOOL; @@ -53,11 +51,6 @@ public class ReindexAnnotationScanner implements ReindexScanner { private static final Map, FieldType> MAPPED_TYPES; - /** - * The maximum number of indexes in a namespace. - * If the number is more, then IllegalAnnotationException will be thrown. - */ - private static final int INDEXES_MAX_COUNT = 63; static { MAPPED_TYPES = new HashMap<>(); @@ -89,14 +82,7 @@ public class ReindexAnnotationScanner implements ReindexScanner { @Override public List parseIndexes(Class itemClass) { - List indexes = parseIndexes(itemClass, false, "", "", new HashMap<>()); - if (indexes.size() > INDEXES_MAX_COUNT) { - throw new IndexConflictException(String.format( - "Too many indexes in the class %s: %s", - itemClass.getName(), - indexes.size())); - } - return indexes; + return parseIndexes(itemClass, false, "", "", new HashMap<>()); } List parseIndexes(Class itemClass, boolean subArray, String reindexBasePath, String jsonBasePath, @@ -110,7 +96,7 @@ List parseIndexes(Class itemClass, boolean subArray, String r } List indexes = new ArrayList<>(); - Set indexNames = new HashSet<>(); + Map nameToIndexMap = new HashMap<>(); List fields = BeanPropertyUtils.getInheritedFields(itemClass); for (Field field : fields) { Reindex reindex = field.getAnnotation(Reindex.class); @@ -119,20 +105,17 @@ List parseIndexes(Class itemClass, boolean subArray, String r if (reindex == null || "-".equals(reindex.name()) || field.isAnnotationPresent(Transient.class)) { continue; } - if (!indexNames.add(reindex.name())) { - throw new IndexConflictException(String.format( - "Non-unique name index name in class %s: %s", - itemClass.getName(), - reindex.name())); - } + String reindexPath = reindexBasePath + reindex.name(); Json json = field.getAnnotation(Json.class); String jsonPath = jsonBasePath + (json == null ? field.getName() : json.value()); FieldInfo fieldInfo = getFieldInfo(field); // If at least one array (collection) is encountered on a nested path for some field, - // or the field itself is an array, then the index on it must also be an array. - if (subArray) { + // or the field itself is an array, + // or the field is appendable + // then the index on it must also be an array. + if (subArray || reindex.isAppendable()) { fieldInfo.isArray = true; } if (COMPOSITE == fieldInfo.fieldType && !fieldInfo.isArray) { @@ -160,8 +143,31 @@ && getFieldTypeByClass(fieldInfo.componentType) == COMPOSITE) { FieldType fieldType = isUuid ? UUID : fieldInfo.fieldType; ReindexerIndex index = createIndex(reindexPath, Collections.singletonList(jsonPath), indexType, fieldType, reindex.isDense(), reindex.isSparse(), reindex.isPrimaryKey(), - fieldInfo.isArray, collateMode, sortOrder, precept, fullTextConfig, isUuid); - indexes.add(index); + fieldInfo.isArray, collateMode, sortOrder, precept, fullTextConfig, isUuid, reindex.isAppendable()); + + ReindexerIndex sameNameIndex = nameToIndexMap.get(reindex.name()); + if (sameNameIndex == null) { + indexes.add(index); + nameToIndexMap.put(reindex.name(), index); + } else if (sameNameIndex.isAppendable() && index.isAppendable() && sameNameIndex.equals(index)) { + sameNameIndex.getJsonPaths().add(jsonPath); + } else { + String errorMessage; + if (reindex.isAppendable() && sameNameIndex.isAppendable()) { + errorMessage = String.format("Appendable indexes with name '%s' in class '%s' " + + "must have the same configuration", + reindex.name(), itemClass.getName()); + } else if (reindex.isAppendable() ^ sameNameIndex.isAppendable()) { + errorMessage = String.format("Multiple indexes with name '%s' in class '%s'," + + " but at least one of them is not marked as appendable", + reindex.name(), itemClass.getName()); + } else { + errorMessage = String.format( + "Non-unique index name in class %s: %s", + itemClass.getName(), reindex.name()); + } + throw new IndexConflictException(errorMessage); + } } } @@ -172,7 +178,7 @@ && getFieldTypeByClass(fieldInfo.componentType) == COMPOSITE) { String sortOrder = getSortOrder(collateMode, collate); ReindexerIndex compositeIndex = createIndex(String.join("+", composite.subIndexes()), Arrays.asList(composite.subIndexes()), composite.type(), COMPOSITE, composite.isDense(), - composite.isSparse(), composite.isPrimaryKey(), false, collateMode, sortOrder, null, null, false); + composite.isSparse(), composite.isPrimaryKey(), false, collateMode, sortOrder, null, null, false, false); indexes.add(compositeIndex); } @@ -217,22 +223,22 @@ private CollateMode getCollateMode(String collate) { private ReindexerIndex createIndex(String reindexPath, List jsonPath, IndexType indexType, FieldType fieldType, boolean isDense, boolean isSparse, boolean isPk, boolean isArray, CollateMode collateMode, String sortOrder, String precept, - FullTextConfig textConfig, boolean isUuid) { + FullTextConfig textConfig, boolean isUuid, boolean isAppendable) { ReindexerIndex index = new ReindexerIndex(); index.setName(reindexPath); index.setSortOrder(sortOrder); index.setCollateMode(collateMode); - index.setJsonPaths(Collections.singletonList(reindexPath)); + index.setJsonPaths(new ArrayList<>(jsonPath)); index.setDense(isDense); index.setSparse(isSparse); index.setPk(isPk); index.setArray(isArray); - index.setJsonPaths(jsonPath); index.setIndexType(indexType); index.setFieldType(fieldType); index.setPrecept(precept); index.setFullTextConfig(textConfig); index.setUuid(isUuid); + index.setAppendable(isAppendable); return index; } diff --git a/src/main/java/ru/rt/restream/reindexer/binding/Binding.java b/src/main/java/ru/rt/restream/reindexer/binding/Binding.java index 543661ca..d66b7691 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/Binding.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/Binding.java @@ -98,6 +98,22 @@ public interface Binding { */ void addIndex(String namespace, IndexDefinition index); + /** + * Update an index based on passed definition. + * + * @param namespace the namespace name + * @param index an index definition to add + */ + void updateIndex(String namespace, IndexDefinition index); + + /** + * Drop an index by name/ + * + * @param namespace the namespace name + * @param indexName name of index + */ + void dropIndex(String namespace, String indexName); + /** * Modifies namespace item data. * diff --git a/src/main/java/ru/rt/restream/reindexer/binding/builtin/Builtin.java b/src/main/java/ru/rt/restream/reindexer/binding/builtin/Builtin.java index 033e6e46..ba8d0df7 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/builtin/Builtin.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/builtin/Builtin.java @@ -105,6 +105,20 @@ public void addIndex(String namespace, IndexDefinition index) { checkResponse(response); } + @Override + public void updateIndex(String namespace, IndexDefinition index) { + ReindexerResponse response = adapter.updateIndex(rx, next.getAndIncrement(), timeout.toMillis(), namespace, + gson.toJson(index)); + checkResponse(response); + } + + @Override + public void dropIndex(String namespace, String indexName) { + ReindexerResponse response = adapter.dropIndex(rx, next.getAndIncrement(), timeout.toMillis(), namespace, + indexName); + checkResponse(response); + } + @Override public void modifyItem(String namespaceName, byte[] data, int format, int mode, String[] precepts, int stateToken) { ByteBuffer args = new ByteBuffer() diff --git a/src/main/java/ru/rt/restream/reindexer/binding/builtin/BuiltinAdapter.java b/src/main/java/ru/rt/restream/reindexer/binding/builtin/BuiltinAdapter.java index c18b9b17..93b28391 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/builtin/BuiltinAdapter.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/builtin/BuiltinAdapter.java @@ -188,6 +188,30 @@ public native ReindexerResponse openNamespace(long rx, long ctxId, long timeout, */ public native ReindexerResponse addIndex(long rx, long ctxId, long timeout, String namespaceName, String indexJson); + /** + * Adds an index. + * + * @param rx the Reindexer instance pointer + * @param ctxId the context id + * @param timeout the execution timeout + * @param namespaceName the namespace name + * @param indexJson the index JSON string + * @return the {@link ReindexerResponse} to use + */ + public native ReindexerResponse updateIndex(long rx, long ctxId, long timeout, String namespaceName, String indexJson); + + /** + * Adds an index. + * + * @param rx the Reindexer instance pointer + * @param ctxId the context id + * @param timeout the execution timeout + * @param namespaceName the namespace name + * @param indexName the index JSON string + * @return the {@link ReindexerResponse} to use + */ + public native ReindexerResponse dropIndex(long rx, long ctxId, long timeout, String namespaceName, String indexName); + /** * Modifies an item. * diff --git a/src/main/java/ru/rt/restream/reindexer/binding/builtin/server/BuiltinServer.java b/src/main/java/ru/rt/restream/reindexer/binding/builtin/server/BuiltinServer.java index 887d638a..d6882f66 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/builtin/server/BuiltinServer.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/builtin/server/BuiltinServer.java @@ -131,6 +131,16 @@ public void addIndex(String namespace, IndexDefinition index) { builtin.addIndex(namespace, index); } + @Override + public void updateIndex(String namespace, IndexDefinition index) { + builtin.updateIndex(namespace, index); + } + + @Override + public void dropIndex(String namespace, String indexName) { + builtin.dropIndex(namespace, indexName); + } + @Override public void modifyItem(String namespaceName, byte[] data, int format, int mode, String[] precepts, int stateToken) { builtin.modifyItem(namespaceName, data, format, mode, precepts, stateToken); diff --git a/src/main/java/ru/rt/restream/reindexer/binding/cproto/Cproto.java b/src/main/java/ru/rt/restream/reindexer/binding/cproto/Cproto.java index cef3eb34..db56291b 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/cproto/Cproto.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/cproto/Cproto.java @@ -79,6 +79,22 @@ public void addIndex(String namespace, IndexDefinition index) { rpcCallNoResults(ADD_INDEX, namespace, toJson(index)); } + /** + * {@inheritDoc} + */ + @Override + public void updateIndex(String namespace, IndexDefinition index) { + rpcCallNoResults(UPDATE_INDEX, namespace, toJson(index)); + } + + /** + * {@inheritDoc} + */ + @Override + public void dropIndex(String namespace, String indexName) { + rpcCallNoResults(DROP_INDEX, namespace, indexName); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/ru/rt/restream/reindexer/binding/definition/IndexDefinition.java b/src/main/java/ru/rt/restream/reindexer/binding/definition/IndexDefinition.java index 79182137..2d4272ac 100644 --- a/src/main/java/ru/rt/restream/reindexer/binding/definition/IndexDefinition.java +++ b/src/main/java/ru/rt/restream/reindexer/binding/definition/IndexDefinition.java @@ -72,7 +72,7 @@ public static IndexDefinition fromIndex(ReindexerIndex index) { indexDefinition.setJsonPaths(index.getJsonPaths()); indexDefinition.setSparse(index.isSparse()); indexDefinition.setLinear(false); - indexDefinition.setAppendable(false); + indexDefinition.setAppendable(index.isAppendable()); indexDefinition.setConfig(index.getFullTextConfig()); indexDefinition.setUuid(index.isUuid()); return indexDefinition; diff --git a/src/main/java/ru/rt/restream/reindexer/fulltext/FullTextConfig.java b/src/main/java/ru/rt/restream/reindexer/fulltext/FullTextConfig.java index f38124ed..9c170370 100644 --- a/src/main/java/ru/rt/restream/reindexer/fulltext/FullTextConfig.java +++ b/src/main/java/ru/rt/restream/reindexer/fulltext/FullTextConfig.java @@ -15,6 +15,11 @@ */ package ru.rt.restream.reindexer.fulltext; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import ru.rt.restream.reindexer.annotations.FullText; import java.util.Arrays; @@ -30,6 +35,10 @@ * Does not check values for out of bounds, if you used too small or too big value, * you have exception when you add or update the index. */ +@Setter +@Getter +@EqualsAndHashCode +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class FullTextConfig { /** @@ -167,14 +176,6 @@ public class FullTextConfig { */ private String extraWordSymbols; - /** - * For use in method of() only. - * Don't make public the constructor! - */ - private FullTextConfig() { - } - - public static FullTextConfig of(FullText annotation) { FullTextConfig config = new FullTextConfig(); config.setEnableNumbersSearch(annotation.enableNumbersSearch()); @@ -200,8 +201,8 @@ public static FullTextConfig of(FullText annotation) { config.setMaxTypoLen(annotation.maxTypoLen()); config.setMergeLimit(annotation.mergeLimit()); config.setPartialMatchDecrease(annotation.partialMatchDecrease()); - config.setStemmers(annotation.stemmers()); - config.setStopWords(annotation.stopWords()); + config.setStemmers(Arrays.asList(annotation.stemmers())); + config.setStopWords(Arrays.asList(annotation.stopWords())); config.setSynonyms(getSynonymsList(annotation.synonyms())); config.setExtraWordSymbols(annotation.extraWordSymbols()); return config; @@ -212,221 +213,4 @@ private static List getSynonymsList(FullText.Synonym[] synonyms) { .map(a -> new Synonym(a.tokens(), a.alternatives())) .collect(Collectors.toList()); } - - public boolean isEnableKbLayout() { - return enableKbLayout; - } - - public void setEnableKbLayout(boolean enableKbLayout) { - this.enableKbLayout = enableKbLayout; - } - - public boolean isEnableNumbersSearch() { - return enableNumbersSearch; - } - - public void setEnableNumbersSearch(boolean enableNumbersSearch) { - this.enableNumbersSearch = enableNumbersSearch; - } - - public boolean isEnableTranslit() { - return enableTranslit; - } - - public void setEnableTranslit(boolean enableTranslit) { - this.enableTranslit = enableTranslit; - } - - public boolean isEnableWarmupOnNsCopy() { - return enableWarmupOnNsCopy; - } - - public void setEnableWarmupOnNsCopy(boolean enableWarmupOnNsCopy) { - this.enableWarmupOnNsCopy = enableWarmupOnNsCopy; - } - - public double getBm25Boost() { - return bm25Boost; - } - - public void setBm25Boost(double bm25Boost) { - this.bm25Boost = bm25Boost; - } - - public double getBm25Weight() { - return bm25Weight; - } - - public void setBm25Weight(double bm25Weight) { - this.bm25Weight = bm25Weight; - } - - public double getPositionBoost() { - return positionBoost; - } - - public void setPositionBoost(double positionBoost) { - this.positionBoost = positionBoost; - } - - public double getPositionWeight() { - return positionWeight; - } - - public void setPositionWeight(double positionWeight) { - this.positionWeight = positionWeight; - } - - public double getDistanceBoost() { - return distanceBoost; - } - - public void setDistanceBoost(double distanceBoost) { - this.distanceBoost = distanceBoost; - } - - public double getDistanceWeight() { - return distanceWeight; - } - - public void setDistanceWeight(double distanceWeight) { - this.distanceWeight = distanceWeight; - } - - public double getFullMatchBoost() { - return fullMatchBoost; - } - - public void setFullMatchBoost(double fullMatchBoost) { - this.fullMatchBoost = fullMatchBoost; - } - - public double getMinRelevancy() { - return minRelevancy; - } - - public void setMinRelevancy(double minRelevancy) { - this.minRelevancy = minRelevancy; - } - - public double getSumRanksByFieldsRatio() { - return sumRanksByFieldsRatio; - } - - public void setSumRanksByFieldsRatio(double sumRanksByFieldsRatio) { - this.sumRanksByFieldsRatio = sumRanksByFieldsRatio; - } - - public double getTermLenBoost() { - return termLenBoost; - } - - public void setTermLenBoost(double termLenBoost) { - this.termLenBoost = termLenBoost; - } - - public double getTermLenWeight() { - return termLenWeight; - } - - public void setTermLenWeight(double termLenWeight) { - this.termLenWeight = termLenWeight; - } - - public int getLogLevel() { - return logLevel; - } - - public void setLogLevel(int logLevel) { - this.logLevel = logLevel; - } - - public int getMaxRebuildSteps() { - return maxRebuildSteps; - } - - public void setMaxRebuildSteps(int maxRebuildSteps) { - this.maxRebuildSteps = maxRebuildSteps; - } - - public int getMaxStepSize() { - return maxStepSize; - } - - public void setMaxStepSize(int maxStepSize) { - this.maxStepSize = maxStepSize; - } - - public int getMaxTypoLen() { - return maxTypoLen; - } - - public void setMaxTypoLen(int maxTypoLen) { - this.maxTypoLen = maxTypoLen; - } - - public int getMaxTypos() { - return maxTypos; - } - - public void setMaxTypos(int maxTypos) { - this.maxTypos = maxTypos; - } - - public int getMergeLimit() { - return mergeLimit; - } - - public void setMergeLimit(int mergeLimit) { - this.mergeLimit = mergeLimit; - } - - public int getPartialMatchDecrease() { - return partialMatchDecrease; - } - - public void setPartialMatchDecrease(int partialMatchDecrease) { - this.partialMatchDecrease = partialMatchDecrease; - } - - public List getStemmers() { - return stemmers; - } - - public void setStemmers(List stemmers) { - this.stemmers = stemmers; - } - - public void setStemmers(String... stemmers) { - this.stemmers = Arrays.asList(stemmers); - } - - public List getStopWords() { - return stopWords; - } - - public void setStopWords(List stopWords) { - this.stopWords = stopWords; - } - - public void setStopWords(String... stopWords) { - this.stopWords = Arrays.asList(stopWords); - } - - public List getSynonyms() { - return synonyms; - } - - public void setSynonyms(List synonyms) { - this.synonyms = synonyms; - } - - public String getExtraWordSymbols() { - return extraWordSymbols; - } - - public void setExtraWordSymbols(String extraWordSymbols) { - this.extraWordSymbols = extraWordSymbols; - } - } diff --git a/src/main/java/ru/rt/restream/reindexer/util/BeanPropertyUtils.java b/src/main/java/ru/rt/restream/reindexer/util/BeanPropertyUtils.java index e384be69..cb87627d 100644 --- a/src/main/java/ru/rt/restream/reindexer/util/BeanPropertyUtils.java +++ b/src/main/java/ru/rt/restream/reindexer/util/BeanPropertyUtils.java @@ -16,6 +16,7 @@ package ru.rt.restream.reindexer.util; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.FieldUtils; import java.beans.IntrospectionException; import java.beans.Introspector; @@ -132,9 +133,19 @@ private static Function getCachedGetter(Class itemClass, String property) { } private static BiConsumer createSetter(Class itemClass, String property) { + final boolean isBooleanField = property.length() > 2 + && property.startsWith("is") + && FieldUtils.getField(itemClass, property, true).getType() == boolean.class; return Stream.of(itemClass.getMethods()) .filter(BeanPropertyUtils::isSetterMethod) - .filter(method -> method.getName().equals(SETTER_PREFIX + StringUtils.capitalize(property))) + .filter(method -> method.getName().equals(SETTER_PREFIX + StringUtils.capitalize(property)) + // lombok setter for Boolean field 'isValue' is 'setIsValue', + // but for boolean field 'isValue' setter is 'setValue' + || (isBooleanField + && method.getName().equals(SETTER_PREFIX + StringUtils.capitalize(property.substring(2))) + ) + ) + .findFirst() .map(method -> { try { return createSetter(LOOKUP.unreflect(method)); @@ -142,7 +153,6 @@ private static BiConsumer createSetter(Class itemClass, String property) { throw new RuntimeException(e); } }) - .findFirst() .orElseThrow(() -> new IllegalStateException( "Public setter is not found for the field: '" + property + "'")); } diff --git a/src/test/java/ru/rt/restream/reindexer/annotations/ReindexTest.java b/src/test/java/ru/rt/restream/reindexer/annotations/ReindexTest.java index 55ade126..e360ec38 100644 --- a/src/test/java/ru/rt/restream/reindexer/annotations/ReindexTest.java +++ b/src/test/java/ru/rt/restream/reindexer/annotations/ReindexTest.java @@ -37,27 +37,7 @@ public void testThrownExceptionWhenNonUniqueIndexes() { ItemWithNonUniqueIndexes.class), "Expected IndexConflictException() to throw, but it didn't" ); - assertTrue(thrown.getMessage().startsWith("Non-unique name index name in class")); - } - - @Test - public void testNoExceptionWhen63Indexes() { - db.openNamespace("someItems63", - NamespaceOptions.defaultOptions(), - ItemWith63Indexes.class); - - } - - @Test - public void testThrownExceptionWhen64Indexes() { - IndexConflictException thrown = assertThrows( - IndexConflictException.class, - () -> db.openNamespace("someItems64", - NamespaceOptions.defaultOptions(), - ItemWith64Indexes.class), - "Expected IndexConflictException() to throw, but it didn't" - ); - assertTrue(thrown.getMessage().startsWith("Too many indexes in the class")); + assertTrue(thrown.getMessage().startsWith("Non-unique index name in class")); } static class ItemWithNonUniqueIndexes { @@ -71,276 +51,4 @@ static class ItemWithNonUniqueIndexes { private String description; } - static class ItemWith63Indexes { - @Reindex(name = "id", isPrimaryKey = true) - private Integer id; - - @Reindex(name = "name0") - private String name0; - @Reindex(name = "name1") - private String name1; - @Reindex(name = "name2") - private String name2; - @Reindex(name = "name3") - private String name3; - @Reindex(name = "name4") - private String name4; - @Reindex(name = "name5") - private String name5; - @Reindex(name = "name6") - private String name6; - @Reindex(name = "name7") - private String name7; - @Reindex(name = "name8") - private String name8; - @Reindex(name = "name9") - private String name9; - - @Reindex(name = "name10") - private String name10; - @Reindex(name = "name11") - private String name11; - @Reindex(name = "name12") - private String name12; - @Reindex(name = "name13") - private String name13; - @Reindex(name = "name14") - private String name14; - @Reindex(name = "name15") - private String name15; - @Reindex(name = "name16") - private String name16; - @Reindex(name = "name17") - private String name17; - @Reindex(name = "name18") - private String name18; - @Reindex(name = "name19") - private String name19; - - @Reindex(name = "name20") - private String name20; - @Reindex(name = "name21") - private String name21; - @Reindex(name = "name22") - private String name22; - @Reindex(name = "name23") - private String name23; - @Reindex(name = "name24") - private String name24; - @Reindex(name = "name25") - private String name25; - @Reindex(name = "name26") - private String name26; - @Reindex(name = "name27") - private String name27; - @Reindex(name = "name28") - private String name28; - @Reindex(name = "name29") - private String name29; - - @Reindex(name = "name30") - private String name30; - @Reindex(name = "name31") - private String name31; - @Reindex(name = "name32") - private String name32; - @Reindex(name = "name33") - private String name33; - @Reindex(name = "name34") - private String name34; - @Reindex(name = "name35") - private String name35; - @Reindex(name = "name36") - private String name36; - @Reindex(name = "name37") - private String name37; - @Reindex(name = "name38") - private String name38; - @Reindex(name = "name39") - private String name39; - - @Reindex(name = "name40") - private String name40; - @Reindex(name = "name41") - private String name41; - @Reindex(name = "name42") - private String name42; - @Reindex(name = "name43") - private String name43; - @Reindex(name = "name44") - private String name44; - @Reindex(name = "name45") - private String name45; - @Reindex(name = "name46") - private String name46; - @Reindex(name = "name47") - private String name47; - @Reindex(name = "name48") - private String name48; - @Reindex(name = "name49") - private String name49; - - @Reindex(name = "name50") - private String name50; - @Reindex(name = "name51") - private String name51; - @Reindex(name = "name52") - private String name52; - @Reindex(name = "name53") - private String name53; - @Reindex(name = "name54") - private String name54; - @Reindex(name = "name55") - private String name55; - @Reindex(name = "name56") - private String name56; - @Reindex(name = "name57") - private String name57; - @Reindex(name = "name58") - private String name58; - @Reindex(name = "name59") - private String name59; - @Reindex(name = "name60") - private String name60; - @Reindex(name = "name61") - private String name61; - } - - static class ItemWith64Indexes { - @Reindex(name = "id", isPrimaryKey = true) - private Integer id; - - @Reindex(name = "name0") - private String name0; - @Reindex(name = "name1") - private String name1; - @Reindex(name = "name2") - private String name2; - @Reindex(name = "name3") - private String name3; - @Reindex(name = "name4") - private String name4; - @Reindex(name = "name5") - private String name5; - @Reindex(name = "name6") - private String name6; - @Reindex(name = "name7") - private String name7; - @Reindex(name = "name8") - private String name8; - @Reindex(name = "name9") - private String name9; - - @Reindex(name = "name10") - private String name10; - @Reindex(name = "name11") - private String name11; - @Reindex(name = "name12") - private String name12; - @Reindex(name = "name13") - private String name13; - @Reindex(name = "name14") - private String name14; - @Reindex(name = "name15") - private String name15; - @Reindex(name = "name16") - private String name16; - @Reindex(name = "name17") - private String name17; - @Reindex(name = "name18") - private String name18; - @Reindex(name = "name19") - private String name19; - - @Reindex(name = "name20") - private String name20; - @Reindex(name = "name21") - private String name21; - @Reindex(name = "name22") - private String name22; - @Reindex(name = "name23") - private String name23; - @Reindex(name = "name24") - private String name24; - @Reindex(name = "name25") - private String name25; - @Reindex(name = "name26") - private String name26; - @Reindex(name = "name27") - private String name27; - @Reindex(name = "name28") - private String name28; - @Reindex(name = "name29") - private String name29; - - @Reindex(name = "name30") - private String name30; - @Reindex(name = "name31") - private String name31; - @Reindex(name = "name32") - private String name32; - @Reindex(name = "name33") - private String name33; - @Reindex(name = "name34") - private String name34; - @Reindex(name = "name35") - private String name35; - @Reindex(name = "name36") - private String name36; - @Reindex(name = "name37") - private String name37; - @Reindex(name = "name38") - private String name38; - @Reindex(name = "name39") - private String name39; - - @Reindex(name = "name40") - private String name40; - @Reindex(name = "name41") - private String name41; - @Reindex(name = "name42") - private String name42; - @Reindex(name = "name43") - private String name43; - @Reindex(name = "name44") - private String name44; - @Reindex(name = "name45") - private String name45; - @Reindex(name = "name46") - private String name46; - @Reindex(name = "name47") - private String name47; - @Reindex(name = "name48") - private String name48; - @Reindex(name = "name49") - private String name49; - - @Reindex(name = "name50") - private String name50; - @Reindex(name = "name51") - private String name51; - @Reindex(name = "name52") - private String name52; - @Reindex(name = "name53") - private String name53; - @Reindex(name = "name54") - private String name54; - @Reindex(name = "name55") - private String name55; - @Reindex(name = "name56") - private String name56; - @Reindex(name = "name57") - private String name57; - @Reindex(name = "name58") - private String name58; - @Reindex(name = "name59") - private String name59; - @Reindex(name = "name60") - private String name60; - @Reindex(name = "name61") - private String name61; - @Reindex(name = "name62") - private String name62; - } - } diff --git a/src/test/java/ru/rt/restream/reindexer/connector/BuiltinIndexOperationTest.java b/src/test/java/ru/rt/restream/reindexer/connector/BuiltinIndexOperationTest.java new file mode 100644 index 00000000..6f57c7ff --- /dev/null +++ b/src/test/java/ru/rt/restream/reindexer/connector/BuiltinIndexOperationTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Restream + * + * 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 ru.rt.restream.reindexer.connector; + +import ru.rt.restream.category.BuiltinTest; + +/** + * Tests for Builtin implementation. + */ +@BuiltinTest +public class BuiltinIndexOperationTest extends IndexOperationTest { + +} diff --git a/src/test/java/ru/rt/restream/reindexer/connector/CprotoIndexOperationTest.java b/src/test/java/ru/rt/restream/reindexer/connector/CprotoIndexOperationTest.java new file mode 100644 index 00000000..cf518b6c --- /dev/null +++ b/src/test/java/ru/rt/restream/reindexer/connector/CprotoIndexOperationTest.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Restream + * + * 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 ru.rt.restream.reindexer.connector; + +import ru.rt.restream.category.CprotoTest; + +/** + * Tests for Cproto implementation. + */ +@CprotoTest +public class CprotoIndexOperationTest extends IndexOperationTest { + +} diff --git a/src/test/java/ru/rt/restream/reindexer/connector/IndexOperationTest.java b/src/test/java/ru/rt/restream/reindexer/connector/IndexOperationTest.java new file mode 100644 index 00000000..7a33c35d --- /dev/null +++ b/src/test/java/ru/rt/restream/reindexer/connector/IndexOperationTest.java @@ -0,0 +1,201 @@ +/* + * Copyright 2020 Restream + * + * 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 ru.rt.restream.reindexer.connector; + +import lombok.Getter; +import lombok.Setter; +import org.junit.jupiter.api.Test; +import ru.rt.restream.reindexer.CollateMode; +import ru.rt.restream.reindexer.FieldType; +import ru.rt.restream.reindexer.IndexType; +import ru.rt.restream.reindexer.Namespace; +import ru.rt.restream.reindexer.NamespaceOptions; +import ru.rt.restream.reindexer.ReindexerIndex; +import ru.rt.restream.reindexer.annotations.Json; +import ru.rt.restream.reindexer.annotations.Reindex; +import ru.rt.restream.reindexer.db.DbBaseTest; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.is; + +/** + * Base index operation Test. + */ +public abstract class IndexOperationTest extends DbBaseTest { + + @Test + public void testOriginalIndexes() { + String nameSpaceName = "items"; + createNamespaceWithRecords(nameSpaceName, 10); + + List indexes = getNameSpaceIndexes(nameSpaceName); + + assertThat(indexes.size(), is(2)); + assertThat(indexes.get(0).getName(), is("description1")); + assertThat(indexes.get(1).getName(), is("id")); + } + + @Test + public void testDropIndex() { + String nameSpaceName = "drop_index_field_items"; + createNamespaceWithRecords(nameSpaceName, 10); + + db.dropIndex(nameSpaceName, "description1"); + List indexes = getNameSpaceIndexes(nameSpaceName); + + assertThat(indexes.size(), is(1)); + assertThat(indexes.get(0).getName(), is("id")); + } + + @Test + public void testAddIndex() { + String nameSpaceName = "added_index_field_items"; + createNamespaceWithRecords(nameSpaceName, 10); + ReindexerIndex addedIndex = createIndex("description2", "description2", IndexType.HASH, FieldType.STRING); + + db.addIndex(nameSpaceName, addedIndex); + List indexes = getNameSpaceIndexes(nameSpaceName); + + assertThat(indexes.size(), is(3)); + assertThat(indexes.get(0).getName(), is("description1")); + assertThat(indexes.get(1).getName(), is("description2")); + assertThat(indexes.get(2).getName(), is("id")); + + assertThat(indexes.get(1).getJsonPaths().get(0), is("description2")); + assertThat(indexes.get(1).getIndexType(), equalToIgnoringCase("hash")); + assertThat(indexes.get(1).getFieldType(), equalToIgnoringCase("string")); + + } + + @Test + public void testUpdateIndex() { + String nameSpaceName = "updated_index_field_items"; + createNamespaceWithRecords(nameSpaceName, 10); + + List beforeUpdate = getNameSpaceIndexes(nameSpaceName); + // update index with name 'description1': target on field 'description2' and change index type + ReindexerIndex updIndex = createIndex("description1", "description2", IndexType.TEXT, FieldType.STRING); + db.updateIndex(nameSpaceName, updIndex); + List afterUpdated = getNameSpaceIndexes(nameSpaceName); + + assertThat(beforeUpdate.size(), is(2)); + assertThat(beforeUpdate.get(1).getName(), is("id")); + assertThat(beforeUpdate.get(0).getName(), is("description1")); + assertThat(beforeUpdate.get(0).getJsonPaths().get(0), is("description1")); + assertThat(beforeUpdate.get(0).getIndexType(), equalToIgnoringCase("hash")); + assertThat(beforeUpdate.get(0).getFieldType(), equalToIgnoringCase("string")); + + assertThat(afterUpdated.size(), is(2)); + assertThat(afterUpdated.get(1).getName(), is("id")); + assertThat(afterUpdated.get(0).getName(), is("description1")); + assertThat(afterUpdated.get(0).getJsonPaths().get(0), is("description2")); + assertThat(afterUpdated.get(0).getIndexType(), equalToIgnoringCase("text")); + assertThat(afterUpdated.get(0).getFieldType(), equalToIgnoringCase("string")); + } + + private ReindexerIndex createIndex(String name, String jsonPath, IndexType indexType, FieldType fieldType) { + return ReindexerIndex.builder() + .name(name) + .jsonPaths(Collections.singletonList(jsonPath)) + .indexType(indexType) + .fieldType(fieldType) + .collateMode(CollateMode.NONE) + .build(); + } + + private void createNamespaceWithRecords(String nameSpaceName, int recordsCnt) { + Namespace itemNamespace = db.openNamespace(nameSpaceName, NamespaceOptions.defaultOptions(), Item.class); + for (int i = 0; i < recordsCnt; i++) { + Item item = new Item(); + item.setId(i); + item.setDescription1("IndexedDescription" + i); + item.setDescription2("NotIndexedDescription" + i); + itemNamespace.insert(item); + } + } + + private List getNameSpaceIndexes(String nameSpaceName) { + Namespace serviceNamespace = db.openNamespace("#namespaces", NamespaceOptions.defaultOptions(), NamespaceResponse.class); + String query = String.format("select * from #namespaces where name = '%s'", nameSpaceName); + Iterator iterator = serviceNamespace.execSql(query); + if (iterator.hasNext()) { + List indexes = iterator.next().getIndexes(); + indexes.sort(Comparator.comparing(IndexResponse::getName)); + return indexes; + } + return Collections.emptyList(); + } + + @Getter + @Setter + public static class Item { + @Reindex(name = "id", isPrimaryKey = true) + private int id; + @Reindex(name = "description1", type = IndexType.HASH) + private String description1; + private String description2; + } + + @Getter + @Setter + public static class NamespaceResponse { + private String name; + private StorageResponse storage; + private List indexes; + } + + @Getter + @Setter + public static class StorageResponse { + private boolean enabled; + } + + @Getter + @Setter + public static class IndexResponse { + private String name; + @Json("json_paths") + private List jsonPaths; + @Json("field_type") + private String fieldType; + @Json("index_type") + private String indexType; + @Json("is_pk") + private boolean isPk; + @Json("is_array") + private boolean isArray; + @Json("is_dense") + private boolean isDense; + @Json("is_sparse") + private boolean isSparse; + @Json("is_linear") + private boolean isLinear; + @Json("is_appendable") + private boolean isAppendable; + @Json("is_simple_tag") + private boolean isSimpleTag; + @Json("collate_mode") + private String collateMode; + @Json("sort_order_letters") + private String sortOrderLetters; + } +} diff --git a/src/test/java/ru/rt/restream/reindexer/connector/ReindexerTest.java b/src/test/java/ru/rt/restream/reindexer/connector/ReindexerTest.java index 88b1f2c7..afeae439 100644 --- a/src/test/java/ru/rt/restream/reindexer/connector/ReindexerTest.java +++ b/src/test/java/ru/rt/restream/reindexer/connector/ReindexerTest.java @@ -16,6 +16,11 @@ package ru.rt.restream.reindexer.connector; import com.google.gson.Gson; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.junit.jupiter.api.Test; import ru.rt.restream.reindexer.Namespace; import ru.rt.restream.reindexer.NamespaceOptions; @@ -59,6 +64,7 @@ import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static ru.rt.restream.reindexer.IndexType.TEXT; import static ru.rt.restream.reindexer.Query.Condition.EQ; import static ru.rt.restream.reindexer.Query.Condition.LE; import static ru.rt.restream.reindexer.Query.Condition.RANGE; @@ -2736,22 +2742,48 @@ public void testUuidItem() { assertThat(testItem, is(foundOnStrUuidByString)); } - public static class SerialIdTestItem { + @Test + public void testIsAppendableIndexItem() { + String namespaceName = "items"; + db.openNamespace(namespaceName, NamespaceOptions.defaultOptions(), ItemWithAppendableIndexes.class); + for (int i = 0; i < 10; i++) { + ItemWithAppendableIndexes testItem = new ItemWithAppendableIndexes(i, "TestName" + i, "Description" + i); + db.upsert(namespaceName, testItem); + } - @Serial - @Reindex(name = "id", isPrimaryKey = true) - private int id; + Iterator iterator = db.query("items", ItemWithAppendableIndexes.class) + .where("name", EQ, "TestName3") + .execute(); - public int getId() { - return id; - } + assertThat(iterator.hasNext(), is(true)); + ItemWithAppendableIndexes foundByName = iterator.next(); + assertThat(foundByName.id, is(3)); + assertThat(foundByName.name, is("TestName3")); + assertThat(foundByName.description, is("Description3")); + assertThat(iterator.hasNext(), is(false)); - public void setId(int id) { - this.id = id; - } + iterator = db.query("items", ItemWithAppendableIndexes.class) + .where("name", EQ, "Description3") + .execute(); + assertThat(iterator.hasNext(), is(true)); + ItemWithAppendableIndexes foundByDescription = iterator.next(); + assertThat(foundByDescription.id, is(3)); + assertThat(foundByDescription.name, is("TestName3")); + assertThat(foundByDescription.description, is("Description3")); + assertThat(iterator.hasNext(), is(false)); } + @Getter + @Setter + public static class SerialIdTestItem { + @Serial + @Reindex(name = "id", isPrimaryKey = true) + private int id; + } + + @Getter + @Setter @Reindex(name = "composite", subIndexes = {"id", "name"}) public static class TestItem { @Reindex(name = "id", isPrimaryKey = true) @@ -2769,70 +2801,6 @@ public static class TestItem { private List integers; private Double doubleValue; - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getNonIndex() { - return nonIndex; - } - - public void setNonIndex(String nonIndex) { - this.nonIndex = nonIndex; - } - - public NestedTest getNestedTest() { - return nestedTest; - } - - public void setNestedTest(NestedTest nestedTest) { - this.nestedTest = nestedTest; - } - - public List getListNested() { - return listNested; - } - - public void setListNested(List listNested) { - this.listNested = listNested; - } - - public List getIntegers() { - return integers; - } - - public void setIntegers(List integers) { - this.integers = integers; - } - - public Double getDoubleValue() { - return doubleValue; - } - - public void setDoubleValue(Double doubleValue) { - this.doubleValue = doubleValue; - } - @Override public boolean equals(Object o) { if (this == o) return true; @@ -2862,56 +2830,27 @@ public String toString() { ", integers=" + integers + '}'; } - } + @Getter + @Setter public static class NestedTest { - @Reindex(name = "value") private String value; @Reindex(name = "test") private Integer test; private String nonIndex; - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public Integer getTest() { - return test; - } - - public void setTest(Integer test) { - this.test = test; - } - - public String getNonIndex() { - return nonIndex; - } - - public void setNonIndex(String nonIndex) { - this.nonIndex = nonIndex; - } } + @Getter + @Setter public static class TestItemId { - private Integer id; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - } + @Getter + @Setter + @EqualsAndHashCode public static class UuidItem { @Reindex(name = "id", isPrimaryKey = true) private Integer id; @@ -2922,63 +2861,20 @@ public static class UuidItem { @Reindex(name = "str") private String str; private String noIndexStrUuid; + } - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public UUID getUuid() { - return uuid; - } - - public void setUuid(UUID uuid) { - this.uuid = uuid; - } - - public String getStrUuid() { - return strUuid; - } - - public void setStrUuid(String strUuid) { - this.strUuid = strUuid; - } - - public String getNoIndexStrUuid() { - return noIndexStrUuid; - } - - public void setNoIndexStrUuid(String noIndexStrUuid) { - this.noIndexStrUuid = noIndexStrUuid; - } - - public String getStr() { - return str; - } - - public void setStr(String str) { - this.str = str; - } + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class ItemWithAppendableIndexes { + @Reindex(name = "id", isPrimaryKey = true) + private Integer id; - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - UuidItem uuidItem = (UuidItem) o; - return Objects.equals(id, uuidItem.id) - && Objects.equals(uuid, uuidItem.uuid) - && Objects.equals(strUuid, uuidItem.strUuid) - && Objects.equals(str, uuidItem.str) - && Objects.equals(noIndexStrUuid, uuidItem.noIndexStrUuid); - } + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String name; - @Override - public int hashCode() { - return Objects.hash(id); - } + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String description; } - } diff --git a/src/test/java/ru/rt/restream/reindexer/db/ClearDbReindexer.java b/src/test/java/ru/rt/restream/reindexer/db/ClearDbReindexer.java index 904339c0..a16a260b 100644 --- a/src/test/java/ru/rt/restream/reindexer/db/ClearDbReindexer.java +++ b/src/test/java/ru/rt/restream/reindexer/db/ClearDbReindexer.java @@ -38,6 +38,8 @@ void clear() { Binding binding = getBinding(); namespaceMap.values().stream() .map(ReindexerNamespace::getName) + // skip service namespaces + .filter(name -> !name.startsWith("#")) .distinct() .forEach(binding::dropNamespace); namespaceMap.clear(); diff --git a/src/test/java/ru/rt/restream/reindexer/fast/ReindexAnnotationScannerIsAppendableIndexTest.java b/src/test/java/ru/rt/restream/reindexer/fast/ReindexAnnotationScannerIsAppendableIndexTest.java new file mode 100644 index 00000000..f2025c99 --- /dev/null +++ b/src/test/java/ru/rt/restream/reindexer/fast/ReindexAnnotationScannerIsAppendableIndexTest.java @@ -0,0 +1,136 @@ +/* + * Copyright 2020 Restream + * + * 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 ru.rt.restream.reindexer.fast; + +import org.junit.jupiter.api.Test; +import ru.rt.restream.reindexer.ReindexScanner; +import ru.rt.restream.reindexer.ReindexerIndex; +import ru.rt.restream.reindexer.annotations.Reindex; +import ru.rt.restream.reindexer.annotations.ReindexAnnotationScanner; +import ru.rt.restream.reindexer.exceptions.IndexConflictException; + +import java.util.List; +import java.util.Objects; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static ru.rt.restream.reindexer.IndexType.TEXT; + +public class ReindexAnnotationScannerIsAppendableIndexTest { + private final ReindexScanner scanner = new ReindexAnnotationScanner(); + + @Test + public void testThrownExceptionWhenNonUniqueNonIsAppendableIndexes() { + IndexConflictException thrown = assertThrows( + IndexConflictException.class, + () -> scanner.parseIndexes(ItemWithNonUniqueIndexes.class), + "Expected IndexConflictException() to throw, but it didn't" + ); + assertTrue(thrown.getMessage().startsWith("Non-unique index name in class")); + } + + @Test + public void testIsAppendableIndexes() { + List indexes = scanner.parseIndexes(ItemWithAppendableIndexes.class); + ReindexerIndex appendable = getIndexByName(indexes, "name"); + + assertThat(indexes.size(), is(2)); + assertThat(appendable, notNullValue()); + assertTrue(appendable.isAppendable()); + assertTrue(appendable.isArray()); + assertThat(appendable.getJsonPaths(), hasItems("name", "description")); + } + + @Test + public void testDifferentIsAppendableIndexes() { + IndexConflictException thrown = assertThrows( + IndexConflictException.class, + () -> scanner.parseIndexes(ItemWithDifferentAppendableIndexes.class), + "Expected IndexConflictException() to throw, but it didn't" + ); + assertTrue(thrown.getMessage().matches("Appendable indexes with name .* must have the same configuration")); + } + + @Test + public void testAppendableAndNonAppendableIndexes() { + IndexConflictException thrown = assertThrows( + IndexConflictException.class, + () -> scanner.parseIndexes(ItemWithOnlyOneAppendableIndex.class), + "Expected IndexConflictException() to throw, but it didn't" + ); + assertTrue(thrown.getMessage().matches("Multiple indexes with name .* " + + "but at least one of them is not marked as appendable")); + } + + private ReindexerIndex getIndexByName(List indexes, String indexName) { + for (ReindexerIndex index : indexes) { + if (Objects.equals(index.getName(), indexName)) { + return index; + } + } + return null; + } + + + static class ItemWithNonUniqueIndexes { + @Reindex(name = "id", isPrimaryKey = true) + private Integer id; + + @Reindex(name = "name") + private String name; + + @Reindex(name = "name") + private String description; + } + + static class ItemWithAppendableIndexes { + @Reindex(name = "id", isPrimaryKey = true) + private Integer id; + + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String name; + + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String description; + } + + static class ItemWithDifferentAppendableIndexes { + @Reindex(name = "id", isPrimaryKey = true) + private Integer id; + + @Reindex(name = "name", isAppendable = true, type = TEXT, isDense = true) + private String name; + + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String description; + } + + static class ItemWithOnlyOneAppendableIndex { + @Reindex(name = "id", isPrimaryKey = true) + private Integer id; + + @Reindex(name = "name", isAppendable = true, type = TEXT) + private String name; + + @Reindex(name = "name", type = TEXT) + private String description; + } +}