diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a68e42..ff5425d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: java: ['8', '11', '17'] services: typesense: - image: typesense/typesense:28.0.rc36 + image: typesense/typesense:30.0.alpha1 ports: - 8108:8108/tcp volumes: diff --git a/src/main/java/org/typesense/api/AnalyticsEvents.java b/src/main/java/org/typesense/api/AnalyticsEvents.java index 4889aa3..de2e295 100644 --- a/src/main/java/org/typesense/api/AnalyticsEvents.java +++ b/src/main/java/org/typesense/api/AnalyticsEvents.java @@ -1,8 +1,9 @@ package org.typesense.api; +import org.typesense.model.AnalyticsEvent; import org.typesense.model.AnalyticsEventCreateResponse; -import org.typesense.model.AnalyticsEventCreateSchema; - +import org.typesense.model.AnalyticsEventsResponse; +import java.util.Map; public class AnalyticsEvents { private final ApiCall apiCall; @@ -12,7 +13,11 @@ public AnalyticsEvents(ApiCall apiCall) { this.apiCall = apiCall; } - public AnalyticsEventCreateResponse create(AnalyticsEventCreateSchema event) throws Exception { + public AnalyticsEventCreateResponse create(AnalyticsEvent event) throws Exception { return this.apiCall.post(RESOURCE_PATH, event, null, AnalyticsEventCreateResponse.class); } + + public AnalyticsEventsResponse retrieve(Map params) throws Exception { + return this.apiCall.get(RESOURCE_PATH, params, AnalyticsEventsResponse.class); + } } diff --git a/src/main/java/org/typesense/api/AnalyticsRule.java b/src/main/java/org/typesense/api/AnalyticsRule.java index 2ed2f28..cc9ef79 100644 --- a/src/main/java/org/typesense/api/AnalyticsRule.java +++ b/src/main/java/org/typesense/api/AnalyticsRule.java @@ -1,29 +1,42 @@ package org.typesense.api; import org.typesense.api.utils.URLEncoding; -import org.typesense.model.AnalyticsRuleDeleteResponse; -import org.typesense.model.AnalyticsRuleSchema; +import org.typesense.model.AnalyticsRuleUpdate; public class AnalyticsRule { private final ApiCall apiCall; private final String ruleId; + private final AnalyticsRuleSerializer serializer; public AnalyticsRule(String ruleId, ApiCall apiCall) { - this.apiCall = apiCall; + this.apiCall = apiCall; this.ruleId = ruleId; + this.serializer = new AnalyticsRuleSerializer(); + } + + public AnalyticsRule(String ruleId, ApiCall apiCall, AnalyticsRuleSerializer serializer) { + this.apiCall = apiCall; + this.ruleId = ruleId; + this.serializer = serializer; } + public org.typesense.model.AnalyticsRule retrieve() throws Exception { + String response = this.apiCall.get(this.getEndpoint(), null, String.class); + return serializer.parseFromJson(response); + } - public AnalyticsRuleSchema retrieve() throws Exception { - return this.apiCall.get(this.getEndpoint(), null, AnalyticsRuleSchema.class); + public org.typesense.model.AnalyticsRule delete() throws Exception { + String response = this.apiCall.delete(this.getEndpoint(), null, String.class); + org.typesense.model.AnalyticsRule result = new org.typesense.model.AnalyticsRule(); + result.name(this.ruleId); + return result; } - public AnalyticsRuleDeleteResponse delete() throws Exception { - return this.apiCall.delete(this.getEndpoint(), null, AnalyticsRuleDeleteResponse.class); + public org.typesense.model.AnalyticsRule update(AnalyticsRuleUpdate rule) throws Exception { + return this.apiCall.put(this.getEndpoint(), rule, null, org.typesense.model.AnalyticsRule.class); } private String getEndpoint() { return AnalyticsRules.RESOURCE_PATH + "/" + URLEncoding.encodeURIComponent(ruleId); } - } diff --git a/src/main/java/org/typesense/api/AnalyticsRuleSerializer.java b/src/main/java/org/typesense/api/AnalyticsRuleSerializer.java new file mode 100644 index 0000000..c3719e8 --- /dev/null +++ b/src/main/java/org/typesense/api/AnalyticsRuleSerializer.java @@ -0,0 +1,138 @@ +package org.typesense.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.DeserializationFeature; +import org.typesense.model.AnalyticsRule; +import org.typesense.model.AnalyticsRuleCreate; +import org.typesense.model.AnalyticsRuleCreateParams; +import java.util.List; +import java.util.ArrayList; + +/** + * Serializer for AnalyticsRule objects to avoid Jackson type discriminator issues + */ +public class AnalyticsRuleSerializer { + + private final ObjectMapper objectMapper; + + public AnalyticsRuleSerializer() { + this.objectMapper = new ObjectMapper(); + this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + this.objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); + } + + /** + * Parse a single AnalyticsRule from JsonNode + */ + public AnalyticsRule parseFromJsonNode(JsonNode node) { + AnalyticsRule rule = new AnalyticsRule(); + + if (node.has("name")) { + rule.name(node.get("name").asText()); + } + + if (node.has("type")) { + String typeStr = node.get("type").asText(); + if ("counter".equals(typeStr)) { + rule.type(AnalyticsRuleCreate.TypeEnum.COUNTER); + } else if ("popular_queries".equals(typeStr)) { + rule.type(AnalyticsRuleCreate.TypeEnum.POPULAR_QUERIES); + } else if ("nohits_queries".equals(typeStr)) { + rule.type(AnalyticsRuleCreate.TypeEnum.NOHITS_QUERIES); + } else if ("log".equals(typeStr)) { + rule.type(AnalyticsRuleCreate.TypeEnum.LOG); + } + } + + if (node.has("collection")) { + rule.collection(node.get("collection").asText()); + } + + if (node.has("event_type")) { + rule.eventType(node.get("event_type").asText()); + } + + if (node.has("rule_tag")) { + rule.ruleTag(node.get("rule_tag").asText()); + } + + if (node.has("params")) { + JsonNode paramsNode = node.get("params"); + AnalyticsRuleCreateParams params = new AnalyticsRuleCreateParams(); + + if (paramsNode.has("counter_field")) { + params.counterField(paramsNode.get("counter_field").asText()); + } + + if (paramsNode.has("weight")) { + params.weight(paramsNode.get("weight").asInt()); + } + + if (paramsNode.has("destination_collection")) { + params.destinationCollection(paramsNode.get("destination_collection").asText()); + } + + if (paramsNode.has("limit")) { + params.limit(paramsNode.get("limit").asInt()); + } + + if (paramsNode.has("capture_search_requests")) { + params.captureSearchRequests(paramsNode.get("capture_search_requests").asBoolean()); + } + + if (paramsNode.has("meta_fields")) { + List metaFields = new ArrayList<>(); + JsonNode metaFieldsNode = paramsNode.get("meta_fields"); + if (metaFieldsNode.isArray()) { + for (JsonNode metaField : metaFieldsNode) { + metaFields.add(metaField.asText()); + } + } + params.metaFields(metaFields); + } + + if (paramsNode.has("expand_query")) { + params.expandQuery(paramsNode.get("expand_query").asBoolean()); + } + + rule.params(params); + } + + return rule; + } + + /** + * Parse AnalyticsRule from JSON string + */ + public AnalyticsRule parseFromJson(String jsonResponse) throws Exception { + JsonNode rootNode = objectMapper.readTree(jsonResponse); + return parseFromJsonNode(rootNode); + } + + /** + * Parse a list of AnalyticsRule objects from JSON string + */ + public List parseListFromJson(String jsonResponse) throws Exception { + JsonNode rootNode = objectMapper.readTree(jsonResponse); + List rules = new ArrayList<>(); + + if (rootNode.isArray()) { + for (JsonNode item : rootNode) { + if (item.has("error")) { + String error = item.get("error").asText(); + throw new RuntimeException("Analytics rule parsing failed: " + error); + } else { + AnalyticsRule rule = parseFromJsonNode(item); + rules.add(rule); + } + } + } else { + // Single object response + AnalyticsRule rule = parseFromJsonNode(rootNode); + rules.add(rule); + } + + return rules; + } +} \ No newline at end of file diff --git a/src/main/java/org/typesense/api/AnalyticsRules.java b/src/main/java/org/typesense/api/AnalyticsRules.java index f910b05..7c113c2 100644 --- a/src/main/java/org/typesense/api/AnalyticsRules.java +++ b/src/main/java/org/typesense/api/AnalyticsRules.java @@ -1,30 +1,83 @@ package org.typesense.api; -import org.typesense.api.utils.URLEncoding; -import org.typesense.model.AnalyticsRuleSchema; -import org.typesense.model.AnalyticsRuleUpsertSchema; -import org.typesense.model.AnalyticsRulesRetrieveSchema; +import org.typesense.model.AnalyticsRule; +import org.typesense.model.AnalyticsRuleCreate; +import java.util.List; public class AnalyticsRules { private final ApiCall apiCall; + private final AnalyticsRuleSerializer serializer; public final static String RESOURCE_PATH = "/analytics/rules"; public AnalyticsRules(ApiCall apiCall) { this.apiCall = apiCall; + this.serializer = new AnalyticsRuleSerializer(); + } + + public AnalyticsRules(ApiCall apiCall, AnalyticsRuleSerializer serializer) { + this.apiCall = apiCall; + this.serializer = serializer; } - public AnalyticsRuleSchema create(AnalyticsRuleSchema rule) throws Exception { - return this.apiCall.post(RESOURCE_PATH, rule, null, AnalyticsRuleSchema.class); + public AnalyticsRulesResponse create(List rules) throws Exception { + String response = this.apiCall.post(RESOURCE_PATH, rules, null, String.class); + return parseCreateResponse(response); } - public AnalyticsRuleSchema upsert(String name, AnalyticsRuleUpsertSchema rule) throws Exception { - return this.apiCall.put(RESOURCE_PATH + "/" + URLEncoding.encodeURIComponent(name), rule, null, - AnalyticsRuleSchema.class); + public List retrieve() throws Exception { + String response = this.apiCall.get(RESOURCE_PATH, null, String.class); + return parseRetrieveResponse(response); } - public AnalyticsRulesRetrieveSchema retrieve() throws Exception { - return this.apiCall.get(RESOURCE_PATH, null, AnalyticsRulesRetrieveSchema.class); + /** + * Parse the create response which can be either a single AnalyticsRule or an array + */ + private AnalyticsRulesResponse parseCreateResponse(String jsonResponse) throws Exception { + List rules = serializer.parseListFromJson(jsonResponse); + + for (AnalyticsRule rule : rules) { + if (rule.getName() == null) { + throw new RuntimeException("Analytics rule creation failed: rule name is null"); + } + } + + return new AnalyticsRulesResponse(rules); + } + + /** + * Parse the retrieve response which is always an array of AnalyticsRule objects + */ + private List parseRetrieveResponse(String jsonResponse) throws Exception { + List rules = serializer.parseListFromJson(jsonResponse); + + return rules; } + /** + * Response wrapper for analytics rules operations + */ + public static class AnalyticsRulesResponse { + private final List rules; + + public AnalyticsRulesResponse(List rules) { + this.rules = rules; + } + + public List getRules() { + return rules; + } + + public AnalyticsRule getFirstRule() { + return rules.isEmpty() ? null : rules.get(0); + } + + public int getCount() { + return rules.size(); + } + + public boolean isEmpty() { + return rules.isEmpty(); + } + } } diff --git a/src/main/java/org/typesense/api/Client.java b/src/main/java/org/typesense/api/Client.java index 7215485..e662489 100644 --- a/src/main/java/org/typesense/api/Client.java +++ b/src/main/java/org/typesense/api/Client.java @@ -26,6 +26,12 @@ public class Client { private Stopwords stopwords; private Map individualStopwordsSets; + private SynonymSets synonymSets; + private Map individualSynonymSets; + + private CurationSets curationSets; + private Map individualCurationSets; + public Health health; public Operations operations; public Metrics metrics; @@ -50,6 +56,10 @@ public Client(Configuration configuration){ this.stemming = new Stemming(this.apiCall); this.stopwords = new Stopwords(this.apiCall); this.individualStopwordsSets = new HashMap<>(); + this.synonymSets = new SynonymSets(this.apiCall); + this.individualSynonymSets = new HashMap<>(); + this.curationSets = new CurationSets(this.apiCall); + this.individualCurationSets = new HashMap<>(); } public Collection collections(String name){ @@ -121,4 +131,34 @@ public StopwordsSet stopwords(String stopwordsSetId) { retVal = this.individualStopwordsSets.get(stopwordsSetId); return retVal; } + + public SynonymSets synonymSets() { + return this.synonymSets; + } + + public SynonymSet synonymSet(String synonymSetName) { + SynonymSet retVal; + + if (!this.individualSynonymSets.containsKey(synonymSetName)) { + this.individualSynonymSets.put(synonymSetName, new SynonymSet(synonymSetName, this.apiCall)); + } + + retVal = this.individualSynonymSets.get(synonymSetName); + return retVal; + } + + public CurationSets curationSets() { + return this.curationSets; + } + + public CurationSet curationSet(String curationSetName) { + CurationSet retVal; + + if (!this.individualCurationSets.containsKey(curationSetName)) { + this.individualCurationSets.put(curationSetName, new CurationSet(curationSetName, this.apiCall)); + } + + retVal = this.individualCurationSets.get(curationSetName); + return retVal; + } } diff --git a/src/main/java/org/typesense/api/Collection.java b/src/main/java/org/typesense/api/Collection.java index 6f2acab..cc5524f 100644 --- a/src/main/java/org/typesense/api/Collection.java +++ b/src/main/java/org/typesense/api/Collection.java @@ -21,9 +21,6 @@ public class Collection { private Synonyms synonyms; private Map individualSynonyms; - private Overrides overrides; - private Map individualOverrides; - private final String endpoint; Collection(String name, ApiCall apiCall, Configuration configuration) { @@ -35,8 +32,6 @@ public class Collection { this.individualDocuments = new HashMap<>(); this.synonyms = new Synonyms(this.name, this.apiCall); this.individualSynonyms = new HashMap<>(); - this.overrides = new Overrides(this.name, this.apiCall); - this.individualOverrides = new HashMap<>(); } public CollectionResponse retrieve() throws Exception { @@ -66,11 +61,31 @@ public Document documents(String documentId) { return retVal; } + /** + * @deprecated This method is deprecated and will be removed in a future version. + * Use {@link Client#synonymSets()} instead for the new synonym sets API. + * + * Note: The old synonyms API is only available on Typesense v29.0 and below. + * For Typesense v30.0 and above, use the new synonym sets API. + */ + @Deprecated public Synonyms synonyms() { + System.err.println("DEPRECATED: Using deprecated synonyms API. This API is only available on Typesense v29.0 and below. " + + "For Typesense v30.0 and above, use the new synonym sets API via Client.synonymSets()."); return this.synonyms; } + /** + * @deprecated This method is deprecated and will be removed in a future version. + * Use {@link Client#synonymSet(String)} instead for the new synonym sets API. + * + * Note: The old synonyms API is only available on Typesense v29.0 and below. + * For Typesense v30.0 and above, use the new synonym sets API. + */ + @Deprecated public Synonym synonyms(String synonymId) { + System.err.println("DEPRECATED: Using deprecated synonyms API. This API is only available on Typesense v29.0 and below. " + + "For Typesense v30.0 and above, use the new synonym sets API via Client.synonymSet(String)."); Synonym retVal; if (!this.individualSynonyms.containsKey(synonymId)) { @@ -80,19 +95,4 @@ public Synonym synonyms(String synonymId) { retVal = this.individualSynonyms.get(synonymId); return retVal; } - - public Overrides overrides() { - return this.overrides; - } - - public Override overrides(String overrideId) { - Override retVal; - - if (!this.individualOverrides.containsKey(overrideId)) { - this.individualOverrides.put(overrideId, new Override(this.name, overrideId, this.apiCall)); - } - - retVal = this.individualOverrides.get(overrideId); - return retVal; - } } diff --git a/src/main/java/org/typesense/api/CurationSet.java b/src/main/java/org/typesense/api/CurationSet.java new file mode 100644 index 0000000..8d9f8a1 --- /dev/null +++ b/src/main/java/org/typesense/api/CurationSet.java @@ -0,0 +1,33 @@ +package org.typesense.api; + +import org.typesense.api.utils.URLEncoding; +import org.typesense.model.CurationSetCreateSchema; +import org.typesense.model.CurationSetSchema; +import org.typesense.model.CurationSetDeleteSchema; + +public class CurationSet { + + private String curationSetName; + private ApiCall apiCall; + + public CurationSet(String curationSetName, ApiCall apiCall) { + this.curationSetName = curationSetName; + this.apiCall = apiCall; + } + + public CurationSetCreateSchema retrieve() throws Exception { + return this.apiCall.get(this.getEndpoint(), null, CurationSetCreateSchema.class); + } + + public CurationSetSchema upsert(CurationSetCreateSchema curationSetCreateSchema) throws Exception { + return this.apiCall.put(this.getEndpoint(), curationSetCreateSchema, null, CurationSetSchema.class); + } + + public CurationSetDeleteSchema delete() throws Exception { + return this.apiCall.delete(this.getEndpoint(), null, CurationSetDeleteSchema.class); + } + + public String getEndpoint() { + return "/curation_sets/" + URLEncoding.encodeURIComponent(this.curationSetName); + } +} diff --git a/src/main/java/org/typesense/api/CurationSets.java b/src/main/java/org/typesense/api/CurationSets.java new file mode 100644 index 0000000..5bcb197 --- /dev/null +++ b/src/main/java/org/typesense/api/CurationSets.java @@ -0,0 +1,26 @@ +package org.typesense.api; + +import org.typesense.model.CurationSetCreateSchema; +import org.typesense.model.CurationSetSchema; + +public class CurationSets { + + private ApiCall apiCall; + public final static String RESOURCEPATH = "/curation_sets"; + + public CurationSets(ApiCall apiCall) { + this.apiCall = apiCall; + } + + public CurationSetSchema upsert(String curationSetName, CurationSetCreateSchema curationSetCreateSchema) throws Exception { + return this.apiCall.put(getEndpoint(curationSetName), curationSetCreateSchema, null, CurationSetSchema.class); + } + + public CurationSetSchema[] retrieve() throws Exception { + return this.apiCall.get(this.getEndpoint(null), null, CurationSetSchema[].class); + } + + public String getEndpoint(String operation) { + return RESOURCEPATH + "/" + (operation == null ? "" : operation); + } +} diff --git a/src/main/java/org/typesense/api/Override.java b/src/main/java/org/typesense/api/Override.java deleted file mode 100644 index e81fb96..0000000 --- a/src/main/java/org/typesense/api/Override.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.typesense.api; - -import org.typesense.api.utils.URLEncoding; -import org.typesense.model.SearchOverride; - -public class Override { - - private String collectionName; - private String overrideId; - private ApiCall apiCall; - - public Override(String collectionName, String overrideId, ApiCall apiCall) { - this.collectionName = collectionName; - this.overrideId = overrideId; - this.apiCall = apiCall; - } - - public SearchOverride retrieve() throws Exception { - return this.apiCall.get(this.getEndpoint(), null, SearchOverride.class); - } - - public SearchOverride delete() throws Exception { - return this.apiCall.delete(this.getEndpoint(), null, SearchOverride.class); - } - - public String getEndpoint() { - return Collections.RESOURCE_PATH + "/" + URLEncoding.encodeURIComponent(this.collectionName) + "/" - + Overrides.RESOURCEPATH + "/" + URLEncoding.encodeURIComponent(this.overrideId); - } -} diff --git a/src/main/java/org/typesense/api/Overrides.java b/src/main/java/org/typesense/api/Overrides.java deleted file mode 100644 index 5bb9acd..0000000 --- a/src/main/java/org/typesense/api/Overrides.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.typesense.api; - -import org.typesense.api.utils.URLEncoding; -import org.typesense.model.SearchOverride; -import org.typesense.model.SearchOverrideSchema; -import org.typesense.model.SearchOverridesResponse; - -public class Overrides { - - public static String RESOURCEPATH = "/overrides"; - - private String collectionName; - private ApiCall apiCall; - - public Overrides(String collectionName, ApiCall apiCall) { - this.collectionName = collectionName; - this.apiCall = apiCall; - } - - public SearchOverride upsert(String overrideId, SearchOverrideSchema searchOverrideSchema) throws Exception { - return this.apiCall.put(getEndpoint(overrideId), searchOverrideSchema, null, SearchOverride.class); - } - - public SearchOverridesResponse retrieve() throws Exception { - return this.apiCall.get(this.getEndpoint(null), null, SearchOverridesResponse.class); - } - - public String getEndpoint(String operation) { - return Collections.RESOURCE_PATH + "/" + URLEncoding.encodeURIComponent(this.collectionName) - + Overrides.RESOURCEPATH + "/" + (operation == null ? "" - : URLEncoding.encodeURIComponent(operation)); - } -} diff --git a/src/main/java/org/typesense/api/Synonym.java b/src/main/java/org/typesense/api/Synonym.java index d7e0340..1a8affe 100644 --- a/src/main/java/org/typesense/api/Synonym.java +++ b/src/main/java/org/typesense/api/Synonym.java @@ -3,6 +3,15 @@ import org.typesense.api.utils.URLEncoding; import org.typesense.model.SearchSynonym; +/** + * @deprecated This class is deprecated and will be removed in a future version. + * Use {@link SynonymSet} instead for the new synonym sets API. + * + * Note: The old synonyms API is only available on Typesense v29.0 and below. + * For Typesense v30.0 and above, use the new synonym sets API. + */ +@Deprecated + public class Synonym { private String collectionName; diff --git a/src/main/java/org/typesense/api/SynonymSet.java b/src/main/java/org/typesense/api/SynonymSet.java new file mode 100644 index 0000000..4672409 --- /dev/null +++ b/src/main/java/org/typesense/api/SynonymSet.java @@ -0,0 +1,34 @@ +package org.typesense.api; + +import org.typesense.api.utils.URLEncoding; + +import org.typesense.model.SynonymSetCreateSchema; +import org.typesense.model.SynonymSetSchema; +import org.typesense.model.SynonymSetDeleteSchema; + +public class SynonymSet { + + private String synonymSetName; + private ApiCall apiCall; + + public SynonymSet(String synonymSetName, ApiCall apiCall) { + this.synonymSetName = synonymSetName; + this.apiCall = apiCall; + } + + public SynonymSetCreateSchema retrieve() throws Exception { + return this.apiCall.get(this.getEndpoint(), null, SynonymSetCreateSchema.class); + } + + public SynonymSetSchema upsert(SynonymSetCreateSchema synonymSetCreateSchema) throws Exception { + return this.apiCall.put(this.getEndpoint(), synonymSetCreateSchema, null, SynonymSetSchema.class); + } + + public SynonymSetDeleteSchema delete() throws Exception { + return this.apiCall.delete(this.getEndpoint(), null, SynonymSetDeleteSchema.class); + } + + public String getEndpoint() { + return "/synonym_sets/" + URLEncoding.encodeURIComponent(this.synonymSetName); + } +} \ No newline at end of file diff --git a/src/main/java/org/typesense/api/SynonymSets.java b/src/main/java/org/typesense/api/SynonymSets.java new file mode 100644 index 0000000..764cb3c --- /dev/null +++ b/src/main/java/org/typesense/api/SynonymSets.java @@ -0,0 +1,27 @@ +package org.typesense.api; + +import java.util.List; +import org.typesense.model.SynonymSetCreateSchema; +import org.typesense.model.SynonymSetSchema; + +public class SynonymSets { + + private ApiCall apiCall; + public final static String RESOURCEPATH = "/synonym_sets"; + + public SynonymSets(ApiCall apiCall) { + this.apiCall = apiCall; + } + + public SynonymSetSchema upsert(String synonymSetName, SynonymSetCreateSchema synonymSetCreateSchema) throws Exception { + return this.apiCall.put(getEndpoint(synonymSetName), synonymSetCreateSchema, null, SynonymSetSchema.class); + } + + public SynonymSetSchema[] retrieve() throws Exception { + return this.apiCall.get(this.getEndpoint(null), null, SynonymSetSchema[].class); + } + + public String getEndpoint(String operation) { + return RESOURCEPATH + "/" + (operation == null ? "" : operation); + } +} \ No newline at end of file diff --git a/src/main/java/org/typesense/api/Synonyms.java b/src/main/java/org/typesense/api/Synonyms.java index 7f5a9ac..e46fc8b 100644 --- a/src/main/java/org/typesense/api/Synonyms.java +++ b/src/main/java/org/typesense/api/Synonyms.java @@ -5,6 +5,15 @@ import org.typesense.model.SearchSynonymSchema; import org.typesense.model.SearchSynonymsResponse; +/** + * @deprecated This class is deprecated and will be removed in a future version. + * Use {@link SynonymSets} instead for the new synonym sets API. + * + * Note: The old synonyms API is only available on Typesense v29.0 and below. + * For Typesense v30.0 and above, use the new synonym sets API. + */ +@Deprecated + public class Synonyms { private String collectionName; diff --git a/src/test/java/org/typesense/api/AnalyticsEventsTest.java b/src/test/java/org/typesense/api/AnalyticsEventsTest.java index 2f88002..036a2ee 100644 --- a/src/test/java/org/typesense/api/AnalyticsEventsTest.java +++ b/src/test/java/org/typesense/api/AnalyticsEventsTest.java @@ -7,22 +7,34 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.typesense.model.AnalyticsEvent; import org.typesense.model.AnalyticsEventCreateResponse; -import org.typesense.model.AnalyticsEventCreateSchema; +import org.typesense.model.AnalyticsEventData; public class AnalyticsEventsTest { private Client client; private Helper helper; + + private String ruleName; @BeforeEach void setUp() throws Exception { helper = new Helper(); client = helper.getClient(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + helper.teardown(); helper.createTestCollection(); helper.createTestQueryCollection(); - helper.createTestAnalyticsRule(); + helper.createTestQueryDocument(); + helper.createTestAnalyticsCollection(); + String ruleName = helper.createTestAnalyticsRule(); + + this.ruleName = ruleName; } @AfterEach @@ -32,17 +44,19 @@ void tearDown() throws Exception { @Test void testCreate() throws Exception { - HashMap eventData = new HashMap<>(); - eventData.put("q", "running shoes"); - eventData.put("user_id", "1234"); - AnalyticsEventCreateSchema analyticsEvent = new AnalyticsEventCreateSchema() - .type("search") - .name("products_search_event") + + AnalyticsEventData eventData = new AnalyticsEventData() + .q("running shoes") + .userId("1234") + .docId("query1"); + + AnalyticsEvent analyticsEvent = new AnalyticsEvent() + .eventType("search") + .name(ruleName) .data(eventData); AnalyticsEventCreateResponse result = this.client.analytics().events().create(analyticsEvent); assertNotNull(result); assertEquals(true, result.isOk()); - } } diff --git a/src/test/java/org/typesense/api/AnalyticsRulesTest.java b/src/test/java/org/typesense/api/AnalyticsRulesTest.java index 34f5dd2..199ea0a 100644 --- a/src/test/java/org/typesense/api/AnalyticsRulesTest.java +++ b/src/test/java/org/typesense/api/AnalyticsRulesTest.java @@ -1,21 +1,31 @@ package org.typesense.api; -import java.util.Arrays; - import org.junit.jupiter.api.AfterEach; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.typesense.model.AnalyticsRuleDeleteResponse; -import org.typesense.model.AnalyticsRuleParameters; -import org.typesense.model.AnalyticsRuleParametersDestination; -import org.typesense.model.AnalyticsRuleParametersSource; -import org.typesense.model.AnalyticsRuleSchema; -import org.typesense.model.AnalyticsRuleUpsertSchema; -import org.typesense.model.AnalyticsRulesRetrieveSchema; -public class AnalyticsRulesTest { +import org.typesense.model.AnalyticsRule; +import org.typesense.model.AnalyticsRuleCreate; +import org.typesense.model.AnalyticsRuleCreateParams; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import org.typesense.model.InlineResponse2003; +import org.typesense.model.CollectionSchema; +import org.typesense.model.Field; +import java.util.Arrays; +import java.util.Map; +import java.util.HashMap; + + +class AnalyticsRulesTest { private Client client; private Helper helper; @@ -24,9 +34,35 @@ public class AnalyticsRulesTest { void setUp() throws Exception { helper = new Helper(); client = helper.getClient(); - helper.teardown(); - helper.createTestCollection(); - helper.createTestQueryCollection(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + + try { + CollectionSchema collectionSchema = new CollectionSchema() + .name("companies") + .fields(Arrays.asList( + new Field().name("company_name").type("string"), + new Field().name("num_employees").type("int32"), + new Field().name("country").type("string") + )); + client.collections().create(collectionSchema); + } catch (Exception e) { + } + + try { + CollectionSchema analyticsCollectionSchema = new CollectionSchema() + .name("analytics_data") + .fields(Arrays.asList( + new Field().name("rule_name").type("string"), + new Field().name("event_type").type("string"), + new Field().name("counter_value").type("int32"), + new Field().name("timestamp").type("int64") + )); + client.collections().create(analyticsCollectionSchema); + } catch (Exception e) { + } } @AfterEach @@ -36,96 +72,127 @@ void tearDown() throws Exception { @Test void testCreate() throws Exception { - AnalyticsRuleSchema analyticsRuleSchema = new AnalyticsRuleSchema(); - analyticsRuleSchema.setName("nohits-queries"); - analyticsRuleSchema.setType(AnalyticsRuleSchema.TypeEnum.NOHITS_QUERIES); - analyticsRuleSchema.setParams(new AnalyticsRuleParameters() - .source(new AnalyticsRuleParametersSource() - .collections(Arrays.asList("books"))) - .destination(new AnalyticsRuleParametersDestination() - .collection("queries"))); - - AnalyticsRuleSchema result = this.client.analytics().rules().create(analyticsRuleSchema); + + String ruleName = "test-rule-" + System.currentTimeMillis(); + + AnalyticsRuleCreate analyticsRule = new AnalyticsRuleCreate() + .name(ruleName) + .type(AnalyticsRuleCreate.TypeEnum.COUNTER) + .collection("analytics_data") + .eventType("click") + .params(new AnalyticsRuleCreateParams() + .counterField("num_employees") + .weight(1)); + + List rules = new ArrayList<>(); + rules.add(analyticsRule); + + AnalyticsRules.AnalyticsRulesResponse result = this.client.analytics().rules().create(rules); assertNotNull(result); - assertEquals("nohits-queries", result.getName()); - - AnalyticsRuleParameters params = result.getParams(); - assertNotNull(params); - - assertNotNull(params.getSource()); - assertEquals(Arrays.asList("books"), params.getSource().getCollections()); - - assertNotNull(params.getDestination()); - assertEquals("queries", params.getDestination().getCollection()); - } - - @Test - void testUpsert() throws Exception { - AnalyticsRuleUpsertSchema analyticsRuleSchema = new AnalyticsRuleUpsertSchema() - .type(AnalyticsRuleUpsertSchema.TypeEnum.NOHITS_QUERIES) - .params(new AnalyticsRuleParameters() - .source(new AnalyticsRuleParametersSource() - .collections(Arrays.asList("books"))) - .destination(new AnalyticsRuleParametersDestination() - .collection("queries"))); - - AnalyticsRuleSchema result = this.client.analytics().rules().upsert("nohits-queries", analyticsRuleSchema); - assertNotNull(result); - assertEquals("nohits-queries", result.getName()); - - AnalyticsRuleParameters params = result.getParams(); - assertNotNull(params); - - assertNotNull(params.getSource()); - assertEquals(Arrays.asList("books"), params.getSource().getCollections()); - - assertNotNull(params.getDestination()); - assertEquals("queries", params.getDestination().getCollection()); + assertFalse(result.isEmpty()); + assertEquals(1, result.getCount()); + + AnalyticsRule createdRule = result.getFirstRule(); + assertNotNull(createdRule); + assertEquals(ruleName, createdRule.getName()); + assertEquals(AnalyticsRuleCreate.TypeEnum.COUNTER, createdRule.getType()); + assertEquals("analytics_data", createdRule.getCollection()); + assertEquals("click", createdRule.getEventType()); } @Test void testRetrieve() throws Exception { - helper.createTestAnalyticsRule(); - AnalyticsRuleSchema result = this.client.analytics().rules("analytics-rule").retrieve(); - + + String ruleName = "test-rule-" + System.currentTimeMillis(); + + AnalyticsRuleCreate analyticsRule = new AnalyticsRuleCreate() + .name(ruleName) + .type(AnalyticsRuleCreate.TypeEnum.COUNTER) + .collection("analytics_data") + .eventType("click") + .params(new AnalyticsRuleCreateParams() + .counterField("num_employees") + .weight(1)); + + List rules = new ArrayList<>(); + rules.add(analyticsRule); + + this.client.analytics().rules().create(rules); + + List result = this.client.analytics().rules().retrieve(); assertNotNull(result); - assertEquals("analytics-rule", result.getName()); - - AnalyticsRuleParameters params = result.getParams(); - assertNotNull(params); - - assertNotNull(params.getSource()); - assertEquals(Arrays.asList("books"), params.getSource().getCollections()); - - assertNotNull(params.getDestination()); - assertEquals("queries", params.getDestination().getCollection()); + assertTrue(result.size() >= 1, "number of rules is invalid"); + + AnalyticsRule foundRule = null; + for (AnalyticsRule rule : result) { + if (ruleName.equals(rule.getName())) { + foundRule = rule; + break; + } + } + + assertNotNull(foundRule, "rule not found"); + assertEquals(ruleName, foundRule.getName()); + assertEquals(AnalyticsRuleCreate.TypeEnum.COUNTER, foundRule.getType()); + assertEquals("analytics_data", foundRule.getCollection()); + assertEquals("click", foundRule.getEventType()); } - + @Test - void testRetrieveAll() throws Exception { - helper.createTestAnalyticsRule(); - AnalyticsRulesRetrieveSchema result = this.client.analytics().rules().retrieve(); - + void testRetrieveSingle() throws Exception { + + String ruleName = "test-rule-" + System.currentTimeMillis(); + + AnalyticsRuleCreate analyticsRule = new AnalyticsRuleCreate() + .name(ruleName) + .type(AnalyticsRuleCreate.TypeEnum.COUNTER) + .collection("analytics_data") + .eventType("click") + .params(new AnalyticsRuleCreateParams() + .counterField("num_employees") + .weight(1)); + + List rules = new ArrayList<>(); + rules.add(analyticsRule); + + this.client.analytics().rules().create(rules); + + AnalyticsRule result = this.client.analytics().rules(ruleName).retrieve(); assertNotNull(result); - assertEquals("analytics-rule", result.getRules().get(0).getName()); - assertEquals(1, result.getRules().size()); - - AnalyticsRuleParameters params = result.getRules().get(0).getParams(); - assertNotNull(params); - - assertNotNull(params.getSource()); - assertEquals(Arrays.asList("books"), params.getSource().getCollections()); - - assertNotNull(params.getDestination()); - assertEquals("queries", params.getDestination().getCollection()); + assertEquals(ruleName, result.getName()); + assertEquals(AnalyticsRuleCreate.TypeEnum.COUNTER, result.getType()); + assertEquals("analytics_data", result.getCollection()); + assertEquals("click", result.getEventType()); } - + @Test void testDelete() throws Exception { - helper.createTestAnalyticsRule(); - AnalyticsRuleDeleteResponse result = this.client.analytics().rules("analytics-rule").delete(); - + + String ruleName = "test-rule-" + System.currentTimeMillis(); + + AnalyticsRuleCreate analyticsRule = new AnalyticsRuleCreate() + .name(ruleName) + .type(AnalyticsRuleCreate.TypeEnum.COUNTER) + .collection("analytics_data") + .eventType("click") + .params(new AnalyticsRuleCreateParams() + .counterField("num_employees") + .weight(1)); + + List rules = new ArrayList<>(); + rules.add(analyticsRule); + + this.client.analytics().rules().create(rules); + + AnalyticsRule result = this.client.analytics().rules(ruleName).delete(); assertNotNull(result); - assertEquals("analytics-rule", result.getName()); + assertEquals(ruleName, result.getName()); + + try { + this.client.analytics().rules(ruleName).retrieve(); + fail("Rule should have been deleted"); + } catch (Exception e) { + assertTrue(e.getMessage().contains("not found") || e.getMessage().contains("404")); + } } } diff --git a/src/test/java/org/typesense/api/CurationSetTest.java b/src/test/java/org/typesense/api/CurationSetTest.java new file mode 100644 index 0000000..fb5a0b3 --- /dev/null +++ b/src/test/java/org/typesense/api/CurationSetTest.java @@ -0,0 +1,131 @@ +package org.typesense.api; + +import java.util.ArrayList; +import java.util.Arrays; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.typesense.model.CurationItemCreateSchema; +import org.typesense.model.CurationSetCreateSchema; +import org.typesense.model.CurationSetSchema; +import org.typesense.model.CurationSetDeleteSchema; +import org.typesense.model.CurationRule; +import org.typesense.model.CurationInclude; +import org.typesense.model.CurationExclude; + +import static org.junit.jupiter.api.Assertions.*; + +class CurationSetTest { + + private Client client; + private Helper helper; + private String curationSetName; + + @BeforeEach + void setUp() throws Exception { + helper = new Helper(); + client = helper.getClient(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + + helper.teardown(); + helper.createTestCollection(); + curationSetName = "test-curation-set-" + System.currentTimeMillis(); + } + + @AfterEach + void tearDown() throws Exception { + try { + client.curationSet(curationSetName).delete(); + } catch (Exception e) { + } + helper.teardown(); + } + + @Test + void testRetrieve() throws Exception { + CurationSetCreateSchema curationSetData = helper.createTestCurationSetData(); + client.curationSets().upsert(curationSetName, curationSetData); + + CurationSetCreateSchema result = client.curationSet(curationSetName).retrieve(); + + assertNotNull(result); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals("apple", result.getItems().get(0).getRule().getQuery()); + assertNotNull(result.getItems().get(0).getIncludes()); + assertEquals(2, result.getItems().get(0).getIncludes().size()); + assertEquals("422", result.getItems().get(0).getIncludes().get(0).getId()); + assertEquals("54", result.getItems().get(0).getIncludes().get(1).getId()); + assertNotNull(result.getItems().get(0).getExcludes()); + assertEquals(1, result.getItems().get(0).getExcludes().size()); + assertEquals("287", result.getItems().get(0).getExcludes().get(0).getId()); + } + + @Test + void testUpdate() throws Exception { + CurationSetCreateSchema originalData = helper.createTestCurationSetData(); + client.curationSets().upsert(curationSetName, originalData); + + CurationRule updatedRule = new CurationRule(); + updatedRule.setQuery("updated query"); + updatedRule.setMatch(CurationRule.MatchEnum.EXACT); + + CurationInclude include1 = new CurationInclude(); + include1.setId("422"); + include1.setPosition(1); + CurationInclude include2 = new CurationInclude(); + include2.setId("54"); + include2.setPosition(2); + CurationInclude include3 = new CurationInclude(); + include3.setId("999"); + include3.setPosition(3); + + CurationExclude exclude1 = new CurationExclude(); + exclude1.setId("287"); + + CurationItemCreateSchema updatedItem = new CurationItemCreateSchema(); + updatedItem.setId("dummy"); + updatedItem.setRule(updatedRule); + updatedItem.setIncludes(Arrays.asList(include1, include2, include3)); + updatedItem.setExcludes(Arrays.asList(exclude1)); + updatedItem.setRemoveMatchedTokens(true); + updatedItem.setFilterBy("category:=Electronics"); + updatedItem.setStopProcessing(true); + + CurationSetCreateSchema updatedData = new CurationSetCreateSchema(); + updatedData.setItems(Arrays.asList(updatedItem)); + updatedData.setDescription("Updated test curation set"); + + CurationSetSchema result = client.curationSet(curationSetName).upsert(updatedData); + + assertEquals(curationSetName, result.getName()); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals("updated query", result.getItems().get(0).getRule().getQuery()); + assertNotNull(result.getItems().get(0).getIncludes()); + assertEquals(3, result.getItems().get(0).getIncludes().size()); + assertEquals("422", result.getItems().get(0).getIncludes().get(0).getId()); + assertEquals("54", result.getItems().get(0).getIncludes().get(1).getId()); + assertEquals("999", result.getItems().get(0).getIncludes().get(2).getId()); + assertNotNull(result.getItems().get(0).getExcludes()); + assertEquals(1, result.getItems().get(0).getExcludes().size()); + assertEquals("287", result.getItems().get(0).getExcludes().get(0).getId()); + } + + @Test + void testDelete() throws Exception { + CurationSetCreateSchema curationSetData = helper.createTestCurationSetData(); + client.curationSets().upsert(curationSetName, curationSetData); + + CurationSetDeleteSchema result = client.curationSet(curationSetName).delete(); + + assertEquals(curationSetName, result.getName()); + + assertThrows(Exception.class, () -> { + client.curationSet(curationSetName).retrieve(); + }); + } +} diff --git a/src/test/java/org/typesense/api/CurationSetsTest.java b/src/test/java/org/typesense/api/CurationSetsTest.java new file mode 100644 index 0000000..1488952 --- /dev/null +++ b/src/test/java/org/typesense/api/CurationSetsTest.java @@ -0,0 +1,92 @@ +package org.typesense.api; + +import java.util.Arrays; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.typesense.model.CurationItemCreateSchema; +import org.typesense.model.CurationSetCreateSchema; +import org.typesense.model.CurationSetSchema; + +import static org.junit.jupiter.api.Assertions.*; + +class CurationSetsTest { + + private Client client; + private Helper helper; + private String curationSetName; + + @BeforeEach + void setUp() throws Exception { + helper = new Helper(); + client = helper.getClient(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + + helper.teardown(); + helper.createTestCollection(); + curationSetName = "test-curation-set-" + System.currentTimeMillis(); + } + + @AfterEach + void tearDown() throws Exception { + try { + client.curationSet(curationSetName).delete(); + } catch (Exception e) { + } + helper.teardown(); + } + + @Test + void testUpsert() throws Exception { + CurationSetCreateSchema curationSetData = helper.createTestCurationSetData(); + + CurationSetSchema result = client.curationSets().upsert(curationSetName, curationSetData); + + assertEquals(curationSetName, result.getName()); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals("apple", result.getItems().get(0).getRule().getQuery()); + assertNotNull(result.getItems().get(0).getIncludes()); + assertEquals(2, result.getItems().get(0).getIncludes().size()); + assertEquals("422", result.getItems().get(0).getIncludes().get(0).getId()); + assertEquals("54", result.getItems().get(0).getIncludes().get(1).getId()); + assertNotNull(result.getItems().get(0).getExcludes()); + assertEquals(1, result.getItems().get(0).getExcludes().size()); + assertEquals("287", result.getItems().get(0).getExcludes().get(0).getId()); + } + + @Test + void testRetrieve() throws Exception { + CurationSetCreateSchema curationSetData = helper.createTestCurationSetData(); + client.curationSets().upsert(curationSetName, curationSetData); + + CurationSetSchema[] result = client.curationSets().retrieve(); + + assertNotNull(result); + assertTrue(result.length >= 1); + + CurationSetSchema foundCurationSet = null; + for (CurationSetSchema cs : result) { + if (curationSetName.equals(cs.getName())) { + foundCurationSet = cs; + break; + } + } + + assertNotNull(foundCurationSet); + assertEquals(curationSetName, foundCurationSet.getName()); + assertEquals(1, foundCurationSet.getItems().size()); + assertEquals("dummy", foundCurationSet.getItems().get(0).getId()); + assertEquals("apple", foundCurationSet.getItems().get(0).getRule().getQuery()); + assertNotNull(foundCurationSet.getItems().get(0).getIncludes()); + assertEquals(2, foundCurationSet.getItems().get(0).getIncludes().size()); + assertEquals("422", foundCurationSet.getItems().get(0).getIncludes().get(0).getId()); + assertEquals("54", foundCurationSet.getItems().get(0).getIncludes().get(1).getId()); + assertNotNull(foundCurationSet.getItems().get(0).getExcludes()); + assertEquals(1, foundCurationSet.getItems().get(0).getExcludes().size()); + assertEquals("287", foundCurationSet.getItems().get(0).getExcludes().get(0).getId()); + } +} diff --git a/src/test/java/org/typesense/api/Helper.java b/src/test/java/org/typesense/api/Helper.java index 8943d39..9d9c8c6 100644 --- a/src/test/java/org/typesense/api/Helper.java +++ b/src/test/java/org/typesense/api/Helper.java @@ -2,16 +2,10 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; - -import org.typesense.model.AnalyticsRuleParameters; -import org.typesense.model.AnalyticsRuleParametersDestination; -import org.typesense.model.AnalyticsRuleParametersSource; -import org.typesense.model.AnalyticsRuleParametersSourceEvents; -import org.typesense.model.AnalyticsRuleSchema; -import org.typesense.model.AnalyticsRuleUpsertSchema; +import java.util.Map; +import java.util.Arrays; import org.typesense.model.ApiKey; import org.typesense.model.ApiKeySchema; import org.typesense.model.ApiKeysResponse; @@ -21,19 +15,64 @@ import org.typesense.model.CollectionResponse; import org.typesense.model.CollectionSchema; import org.typesense.model.Field; -import org.typesense.model.SearchOverrideInclude; -import org.typesense.model.SearchOverrideRule; -import org.typesense.model.SearchOverrideSchema; import org.typesense.model.SearchSynonymSchema; import org.typesense.model.StemmingDictionaryWords; import org.typesense.model.StopwordsSetSchema; import org.typesense.model.StopwordsSetUpsertSchema; import org.typesense.model.StopwordsSetsRetrieveAllSchema; +import org.typesense.model.SynonymItemSchema; +import org.typesense.model.SynonymSetCreateSchema; +import org.typesense.model.SynonymSetSchema; +import org.typesense.model.SynonymSetsRetrieveSchema; +import org.typesense.model.CurationItemCreateSchema; +import org.typesense.model.CurationSetCreateSchema; +import org.typesense.model.CurationRule; +import org.typesense.model.CurationInclude; +import org.typesense.model.CurationExclude; import org.typesense.resources.Node; +import org.typesense.model.AnalyticsRuleCreate; +import org.typesense.model.AnalyticsRuleCreateParams; +import org.typesense.model.AnalyticsRule; +import org.typesense.api.AnalyticsRules; public class Helper { private final Client client; + public static boolean isV30OrAbove(Client client) { + try { + Map debugResponse = client.debug.retrieve(); + if (debugResponse == null) { + return false; + } + + Object version = debugResponse.get("version"); + if (version == null) { + return false; + } + + String versionStr = version.toString(); + if ("nightly".equals(versionStr)) { + return true; + } + + String numberedVersion = versionStr; + if (versionStr.startsWith("v")) { + numberedVersion = versionStr.substring(1); + } + + String[] parts = numberedVersion.split("\\."); + if (parts.length == 0) { + return false; + } + + int majorVersion = Integer.parseInt(parts[0]); + return majorVersion >= 30; + + } catch (Exception e) { + return false; + } + } + Helper() { List nodes = new ArrayList<>(); nodes.add(new Node("http", "localhost", "8108")); @@ -64,6 +103,15 @@ public void createTestQueryCollection() throws Exception { client.collections().create(collectionSchema); } + public void createTestQueryDocument() throws Exception { + HashMap hmap = new HashMap<>(); + hmap.put("q", "running shoes"); + hmap.put("count", 42); + hmap.put("id", "query1"); + + client.collections("queries").documents().create(hmap); + } + public void createTestDocument() throws Exception { String[] authors = { "shakspeare", "william" }; HashMap hmap = new HashMap<>(); @@ -97,15 +145,6 @@ public ApiKey createTestKey() throws Exception { return client.keys().create(apiKeySchema); } - public void createTestOverrirde() throws Exception { - SearchOverrideSchema searchOverrideSchema = new SearchOverrideSchema(); - List searchOverrideIncludes = new ArrayList<>(); - searchOverrideIncludes.add(new SearchOverrideInclude().id("422").position(1)); - searchOverrideSchema.rule(new SearchOverrideRule().query("apple").match(SearchOverrideRule.MatchEnum.EXACT)) - .includes(searchOverrideIncludes); - client.collections("books").overrides().upsert("customize-apple", searchOverrideSchema); - } - public void createTestSynonym() throws Exception { SearchSynonymSchema synonym = new SearchSynonymSchema(); synonym.addSynonymsItem("blazer").addSynonymsItem("coat").addSynonymsItem("jacket"); @@ -113,20 +152,36 @@ public void createTestSynonym() throws Exception { client.collections("books").synonyms().upsert("coat-synonyms", synonym); } - public void createTestAnalyticsRule() throws Exception { - AnalyticsRuleUpsertSchema analyticsRuleSchema = new AnalyticsRuleUpsertSchema() - .type(AnalyticsRuleUpsertSchema.TypeEnum.NOHITS_QUERIES) - .params(new AnalyticsRuleParameters() - .source(new AnalyticsRuleParametersSource() - .collections(Arrays.asList("books")) - .events(Arrays.asList( - new AnalyticsRuleParametersSourceEvents() - .type("search") - .name("products_search_event")))) - .destination(new AnalyticsRuleParametersDestination() - .collection("queries"))); + public void createTestAnalyticsCollection() throws Exception { + List fields = new ArrayList<>(); + fields.add(new Field().name("rule_name").type(FieldTypes.STRING)); + fields.add(new Field().name("event_type").type(FieldTypes.STRING)); + fields.add(new Field().name("counter_value").type(FieldTypes.INT32)); + fields.add(new Field().name("timestamp").type(FieldTypes.INT64)); - client.analytics().rules().upsert("analytics-rule", analyticsRuleSchema); + CollectionSchema collectionSchema = new CollectionSchema(); + collectionSchema.name("analytics_data").fields(fields); + + client.collections().create(collectionSchema); + } + + public String createTestAnalyticsRule() throws Exception { + String ruleName = "analytics-rule-" + System.currentTimeMillis(); + + AnalyticsRuleCreate analyticsRule = new AnalyticsRuleCreate() + .name(ruleName) + .type(AnalyticsRuleCreate.TypeEnum.COUNTER) + .collection("analytics_data") + .eventType("click") + .params(new AnalyticsRuleCreateParams() + .counterField("num_employees") + .weight(1)); + + List rules = new ArrayList<>(); + rules.add(analyticsRule); + + client.analytics().rules().create(rules); + return ruleName; } public void createTestStopwordsSet() throws Exception { @@ -151,20 +206,69 @@ public void createStemmingDictionary() throws Exception{ client.stemming().dictionaries().upsert("irregular-plurals", stemmingDictionaryWords); } + public SynonymSetCreateSchema createTestSynonymSetData() { + SynonymItemSchema synonymItem = new SynonymItemSchema(); + synonymItem.setId("dummy"); + synonymItem.setSynonyms(Arrays.asList("foo", "bar", "baz")); + + SynonymSetCreateSchema synonymSetData = new SynonymSetCreateSchema(); + synonymSetData.setItems(Arrays.asList(synonymItem)); + return synonymSetData; + } + + public CurationSetCreateSchema createTestCurationSetData() { + CurationRule rule = new CurationRule(); + rule.setQuery("apple"); + rule.setMatch(CurationRule.MatchEnum.EXACT); + + CurationInclude include1 = new CurationInclude(); + include1.setId("422"); + include1.setPosition(1); + CurationInclude include2 = new CurationInclude(); + include2.setId("54"); + include2.setPosition(2); + + CurationExclude exclude1 = new CurationExclude(); + exclude1.setId("287"); + + CurationItemCreateSchema curationItem = new CurationItemCreateSchema(); + curationItem.setId("dummy"); + curationItem.setRule(rule); + curationItem.setIncludes(Arrays.asList(include1, include2)); + curationItem.setExcludes(Arrays.asList(exclude1)); + curationItem.setRemoveMatchedTokens(true); + curationItem.setFilterBy("category:=Electronics"); + curationItem.setStopProcessing(true); + + CurationSetCreateSchema curationSetData = new CurationSetCreateSchema(); + curationSetData.setItems(Arrays.asList(curationItem)); + curationSetData.setDescription("Test curation set"); + return curationSetData; + } + public void teardown() throws Exception { CollectionResponse[] collectionResponses = client.collections().retrieve(); for (CollectionResponse c : collectionResponses) { - client.collections(c.getName()).delete(); + if (c.getName() != null) { + client.collections(c.getName()).delete(); + } } CollectionAliasesResponse collectionAliasesResponse = client.aliases().retrieve(); for (CollectionAlias a : collectionAliasesResponse.getAliases()) { - client.aliases(a.getName()).delete(); + if (a.getName() != null) { + client.aliases(a.getName()).delete(); + } } AnalyticsRules analyticsRules = client.analytics().rules(); - for (AnalyticsRuleSchema r : analyticsRules.retrieve().getRules()) { - client.analytics().rules(r.getName()).delete(); + List rules = analyticsRules.retrieve(); + if (rules != null) { + for (AnalyticsRule r : rules) { + if (r.getName() != null) { + client.analytics().rules(r.getName()).delete(); + } + } } ApiKeysResponse apiKeysResponse = client.keys().retrieve(); @@ -176,5 +280,15 @@ public void teardown() throws Exception { for (StopwordsSetSchema s : stopwords.getStopwords()) { client.stopwords(s.getId()).delete(); } + + try { + SynonymSetSchema[] synonymSets = client.synonymSets().retrieve(); + for (SynonymSetSchema s : synonymSets) { + if (s.getName() != null) { + client.synonymSet(s.getName()).delete(); + } + } + } catch (Exception e) { + } } } diff --git a/src/test/java/org/typesense/api/OverridesTest.java b/src/test/java/org/typesense/api/OverridesTest.java deleted file mode 100644 index b034679..0000000 --- a/src/test/java/org/typesense/api/OverridesTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.typesense.api; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.typesense.model.SearchOverrideExclude; -import org.typesense.model.SearchOverrideInclude; -import org.typesense.model.SearchOverrideRule; -import org.typesense.model.SearchOverrideSchema; - -import java.util.ArrayList; -import java.util.List; - -class OverridesTest { - - private Client client; - private Helper helper; - - @BeforeEach - void setUp() throws Exception { - helper = new Helper(); - helper.teardown(); - client = helper.getClient(); - helper.createTestCollection(); - helper.createTestOverrirde(); - } - - @AfterEach - void tearDown() throws Exception { - helper.teardown(); - } - - @Test - void testUpsert() throws Exception { - SearchOverrideSchema searchOverrideSchema = new SearchOverrideSchema(); - - List searchOverrideIncludes = new ArrayList<>(); - searchOverrideIncludes.add(new SearchOverrideInclude().id("422").position(1)); - searchOverrideIncludes.add(new SearchOverrideInclude().id("54").position(2)); - - List searchOverrideExcludes = new ArrayList<>(); - searchOverrideExcludes.add(new SearchOverrideExclude().id("287")); - - searchOverrideSchema.rule(new SearchOverrideRule().query("apple").match(SearchOverrideRule.MatchEnum.EXACT)) - .includes(searchOverrideIncludes) - .excludes(searchOverrideExcludes); - - System.out.println(client.collections("books").overrides().upsert("apple", searchOverrideSchema)); - } - - @Test - void testRetrieveAll() throws Exception { - System.out.println(this.client.collections("books").overrides().retrieve()); - } - - @Test - void testRetrieve() throws Exception { - System.out.println(this.client.collections("books").overrides("customize-apple").retrieve()); - } - - @Test - void testDelete() throws Exception { - System.out.println(this.client.collections("books").overrides("customize-apple").delete()); - } -} \ No newline at end of file diff --git a/src/test/java/org/typesense/api/SynonymSetTest.java b/src/test/java/org/typesense/api/SynonymSetTest.java new file mode 100644 index 0000000..ea17727 --- /dev/null +++ b/src/test/java/org/typesense/api/SynonymSetTest.java @@ -0,0 +1,93 @@ +package org.typesense.api; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.typesense.model.SynonymItemSchema; +import org.typesense.model.SynonymSetCreateSchema; +import org.typesense.model.SynonymSetSchema; +import org.typesense.model.SynonymSetDeleteSchema; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SynonymSetTest { + + private Client client; + private Helper helper; + private String synonymSetName; + + @BeforeEach + void setUp() throws Exception { + helper = new Helper(); + client = helper.getClient(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + + helper.teardown(); + helper.createTestCollection(); + synonymSetName = "test-synonym-set-" + System.currentTimeMillis(); + } + + @AfterEach + void tearDown() throws Exception { + try { + client.synonymSet(synonymSetName).delete(); + } catch (Exception e) { + } + helper.teardown(); + } + + @Test + void testRetrieve() throws Exception { + SynonymSetCreateSchema synonymSetData = helper.createTestSynonymSetData(); + client.synonymSets().upsert(synonymSetName, synonymSetData); + + SynonymSetCreateSchema result = client.synonymSet(synonymSetName).retrieve(); + + assertNotNull(result); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals(Arrays.asList("foo", "bar", "baz"), result.getItems().get(0).getSynonyms()); + } + + @Test + void testUpdate() throws Exception { + SynonymSetCreateSchema originalData = helper.createTestSynonymSetData(); + client.synonymSets().upsert(synonymSetName, originalData); + + SynonymSetCreateSchema updatedData = new SynonymSetCreateSchema(); + List items = new ArrayList<>(); + SynonymItemSchema item = new SynonymItemSchema(); + item.setId("dummy"); + item.setSynonyms(Arrays.asList("foo", "bar", "baz", "qux")); + items.add(item); + updatedData.setItems(items); + + SynonymSetSchema result = client.synonymSet(synonymSetName).upsert(updatedData); + + assertEquals(synonymSetName, result.getName()); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals(Arrays.asList("foo", "bar", "baz", "qux"), result.getItems().get(0).getSynonyms()); + } + + @Test + void testDelete() throws Exception { + SynonymSetCreateSchema synonymSetData = helper.createTestSynonymSetData(); + client.synonymSets().upsert(synonymSetName, synonymSetData); + + SynonymSetDeleteSchema result = client.synonymSet(synonymSetName).delete(); + + assertEquals(synonymSetName, result.getName()); + + assertThrows(Exception.class, () -> { + client.synonymSet(synonymSetName).retrieve(); + }); + } +} \ No newline at end of file diff --git a/src/test/java/org/typesense/api/SynonymSetsTest.java b/src/test/java/org/typesense/api/SynonymSetsTest.java new file mode 100644 index 0000000..430cfad --- /dev/null +++ b/src/test/java/org/typesense/api/SynonymSetsTest.java @@ -0,0 +1,80 @@ +package org.typesense.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.typesense.model.SynonymItemSchema; +import org.typesense.model.SynonymSetCreateSchema; +import org.typesense.model.SynonymSetSchema; + +import static org.junit.jupiter.api.Assertions.*; + +class SynonymSetsTest { + + private Client client; + private Helper helper; + private String synonymSetName; + + @BeforeEach + void setUp() throws Exception { + helper = new Helper(); + client = helper.getClient(); + + if (!Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + + helper.teardown(); + helper.createTestCollection(); + synonymSetName = "test-synonym-set-" + System.currentTimeMillis(); + } + + @AfterEach + void tearDown() throws Exception { + try { + client.synonymSet(synonymSetName).delete(); + } catch (Exception e) { + } + helper.teardown(); + } + + @Test + void testUpsert() throws Exception { + SynonymSetCreateSchema synonymSetData = helper.createTestSynonymSetData(); + + SynonymSetSchema result = client.synonymSets().upsert(synonymSetName, synonymSetData); + + assertEquals(synonymSetName, result.getName()); + assertEquals(1, result.getItems().size()); + assertEquals("dummy", result.getItems().get(0).getId()); + assertEquals(Arrays.asList("foo", "bar", "baz"), result.getItems().get(0).getSynonyms()); + } + + @Test + void testRetrieve() throws Exception { + SynonymSetCreateSchema synonymSetData = helper.createTestSynonymSetData(); + client.synonymSets().upsert(synonymSetName, synonymSetData); + + SynonymSetSchema[] result = client.synonymSets().retrieve(); + + assertNotNull(result); + assertTrue(result.length >= 1); + + SynonymSetSchema foundSynonymSet = null; + for (SynonymSetSchema ss : result) { + if (synonymSetName.equals(ss.getName())) { + foundSynonymSet = ss; + break; + } + } + + assertNotNull(foundSynonymSet); + assertEquals(synonymSetName, foundSynonymSet.getName()); + assertEquals(1, foundSynonymSet.getItems().size()); + assertEquals("dummy", foundSynonymSet.getItems().get(0).getId()); + assertEquals(Arrays.asList("foo", "bar", "baz"), foundSynonymSet.getItems().get(0).getSynonyms()); + } +} diff --git a/src/test/java/org/typesense/api/SynonymsTest.java b/src/test/java/org/typesense/api/SynonymsTest.java index 57da150..13947d2 100644 --- a/src/test/java/org/typesense/api/SynonymsTest.java +++ b/src/test/java/org/typesense/api/SynonymsTest.java @@ -14,6 +14,11 @@ class SynonymsTest { void setUp() throws Exception { helper = new Helper(); client = helper.getClient(); + + if (Helper.isV30OrAbove(client)) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping test - requires Typesense v30 or above"); + } + helper.teardown(); helper.createTestCollection(); helper.createTestSynonym(); @@ -49,4 +54,4 @@ void testRetrieveAll() throws Exception { void testDelete() throws Exception { System.out.println(this.client.collections("books").synonyms("coat-synonyms").delete()); } -} \ No newline at end of file +}