Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 29865 query parser #30004

Merged
merged 91 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 90 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
cf827da
Revert "fix: #28563 removing template ajax (#28572)"
jdotcms May 15, 2024
fb39637
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 11, 2024
af6fed1
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 14, 2024
96dd743
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 18, 2024
fb71cf9
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 24, 2024
ae1d5e1
Merge branches 'master' and 'master' of github.com:dotCMS/core
jdotcms Jun 25, 2024
b523af6
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 27, 2024
95de159
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 27, 2024
679b600
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jun 28, 2024
4bb207b
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 2, 2024
320b4b1
Merge branches 'master' and 'master' of github.com:dotCMS/core
jdotcms Jul 2, 2024
ed6a751
Merge branches 'master' and 'master' of github.com:dotCMS/core
jdotcms Jul 3, 2024
02a6e96
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 4, 2024
5479786
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 5, 2024
0762192
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 9, 2024
2a61c7f
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 11, 2024
0afad59
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 17, 2024
9c510e7
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 18, 2024
24a5827
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 19, 2024
7468cd7
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 22, 2024
0388022
Merge branch 'master' of github.com:dotCMS/core
jdotcms Jul 29, 2024
162ade1
Merge branch 'master' of github.com:dotCMS/core
jdotcms Aug 19, 2024
1e9be41
Merge branch 'master' of github.com:dotCMS/core
jdotcms Aug 21, 2024
17cac16
Merge branch 'master' of github.com:dotCMS/core
jdotcms Aug 21, 2024
ff615f0
Merge branch 'master' of github.com:dotCMS/core
jdotcms Aug 27, 2024
431befb
Merge branch 'master' of github.com:dotCMS/core
jdotcms Aug 29, 2024
4e21df8
#29711 first draft for collectors
jdotcms Aug 29, 2024
233fbb7
#29711 first draft for collectors
jdotcms Aug 29, 2024
3350d84
#29711 adding first collectors
jdotcms Aug 29, 2024
b8ac5d5
#29711 adding first collectors
jdotcms Aug 29, 2024
9d3eb11
#29711 adding first collectors
jdotcms Aug 29, 2024
28ff348
#29711 adding first collectors
jdotcms Aug 30, 2024
8780b99
#29711 adding page collector
jdotcms Sep 3, 2024
54f9d97
#29711 merge master done
jdotcms Sep 4, 2024
dbd5ebc
#29711 adding the collectors and refactor
jdotcms Sep 4, 2024
7a90b7c
#29711 adding the collectors and refactor
jdotcms Sep 4, 2024
94ada0f
#29711 adding the collectors and refactor
jdotcms Sep 4, 2024
43d4d1b
#29711 refactor feedback and creating a new events payload with the a…
jdotcms Sep 5, 2024
95dd685
#29711 refactor feedback and creating a new events payload with the a…
jdotcms Sep 5, 2024
0083837
#29711 adding the collectors
jdotcms Sep 5, 2024
362962d
#29711 adding some fixes and enhancements to the collectors
jdotcms Sep 10, 2024
0137501
Making PageCollector and FileCollector sync/changes Attributes names
freddyDOTCMS Sep 11, 2024
5725855
#29711 adding some fixes
jdotcms Sep 11, 2024
07829fc
Using a different method to resolve pages / change attribute name obj…
freddyDOTCMS Sep 11, 2024
6d2404b
Better fix to match Page and File
freddyDOTCMS Sep 11, 2024
8a64118
Include dotsass files to be match
freddyDOTCMS Sep 11, 2024
e9165de
#29711 adding some fixes
jdotcms Sep 11, 2024
f96fbb1
#29711 adding vanities
jdotcms Sep 11, 2024
c69b416
#29711 adding vanities
jdotcms Sep 11, 2024
e6bc5d7
Handling URL Mapped content in the Page Collector
jcastro-dotcms Sep 11, 2024
12d814e
#29711 adding vanities
jdotcms Sep 11, 2024
496bfe8
#29711 moving to enum
jdotcms Sep 11, 2024
9d73a48
Getting Contentlet data when a File is retrieve
freddyDOTCMS Sep 12, 2024
114537f
merge
freddyDOTCMS Sep 12, 2024
73354ba
Adding a second collector for detail page data
jcastro-dotcms Sep 12, 2024
b803b98
Some tiny changes
freddyDOTCMS Sep 12, 2024
dc6da69
Fixing
freddyDOTCMS Sep 13, 2024
1781b10
Removing the second collector for detail page data, and creating a br…
jcastro-dotcms Sep 13, 2024
02fdb24
Merge remote-tracking branch 'origin/issue-29711-analytics-collectors…
jcastro-dotcms Sep 13, 2024
e68d064
Add response in Vanity attributes
freddyDOTCMS Sep 13, 2024
a351ce2
Minor refactoring
jcastro-dotcms Sep 13, 2024
9666df9
Minor refactoring
jcastro-dotcms Sep 13, 2024
9b3bea8
#29865 adding first draft query parser
jdotcms Sep 13, 2024
8f05879
#the filter parser is kinda working
jdotcms Sep 13, 2024
2ca4511
#the filter parser is kinda working
jdotcms Sep 13, 2024
d2ecb4e
refactoring
freddyDOTCMS Sep 16, 2024
190561c
Fixing
freddyDOTCMS Sep 16, 2024
3789e7a
Final fixing
freddyDOTCMS Sep 17, 2024
c8dd722
Merge remote-tracking branch 'origin/master' into issue-29711-analyti…
freddyDOTCMS Sep 17, 2024
e89e5d6
Final fixing
freddyDOTCMS Sep 17, 2024
08641fb
Making sonnar happy
freddyDOTCMS Sep 17, 2024
e98c166
Merge branch 'master' into issue-29865-query-parser
jcastro-dotcms Sep 17, 2024
9820e66
#29865 adding the filters parser
jdotcms Sep 17, 2024
e5b5e70
Merge branch 'master' of github.com:dotCMS/core into issue-29865-quer…
jdotcms Sep 17, 2024
7c5d3db
Merge branch 'issue-29865-query-parser' of github.com:dotCMS/core int…
jdotcms Sep 17, 2024
8c5f68a
#29865 adding the filters parser
jdotcms Sep 17, 2024
9312a13
#29865 adding the filters parser
jdotcms Sep 17, 2024
00c0de2
#29791 Removing duplicated code
freddyDOTCMS Sep 17, 2024
1e7cf5c
Merge branch 'master' into issue-29711-analytics-collectors-layer
freddyDOTCMS Sep 17, 2024
6fa06ba
#29791 fixing
freddyDOTCMS Sep 17, 2024
273aaca
Merge branch 'issue-29711-analytics-collectors-layer' of https://gith…
freddyDOTCMS Sep 17, 2024
ca3bc84
#29865 adding sonarq feedback
jdotcms Sep 18, 2024
5027232
#29711 adding sonarqfeedback
jdotcms Sep 18, 2024
98ab406
#29711 sonarq feedback
jdotcms Sep 18, 2024
543a1cd
#29711 sonarq feedback
jdotcms Sep 18, 2024
0a0c7f0
Merge branch 'issue-29711-analytics-collectors-layer' of github.com:d…
jdotcms Sep 18, 2024
42c1a12
#29865 sonarq feedback
jdotcms Sep 18, 2024
73ebf0c
#29865 sonarq feedback
jdotcms Sep 18, 2024
7de3372
#29865 fixing merge
jdotcms Sep 18, 2024
c9c6deb
#29865 some doc
jdotcms Sep 18, 2024
7f89897
#29865 just more comments
jdotcms Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions dotCMS/src/main/java/com/dotcms/analytics/Util.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.dotcms.analytics;

import com.dotmarketing.beans.Host;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.cms.urlmap.UrlMapContext;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.PageMode;
import io.vavr.control.Try;

import static com.dotcms.exception.ExceptionUtil.getErrorMessage;
Expand Down
136 changes: 136 additions & 0 deletions dotCMS/src/main/java/com/dotcms/analytics/query/AnalyticsQuery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package com.dotcms.analytics.query;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.Set;

/**
* Encapsulates a simplificated query for the analytics backend
* @author jsanca
*/
jdotcms marked this conversation as resolved.
Show resolved Hide resolved
@JsonDeserialize(builder = AnalyticsQuery.Builder.class)
public class AnalyticsQuery {

private final Set<String> dimensions; // ["Events.referer", "Events.experiment", "Events.variant", "Events.utcTime", "Events.url", "Events.lookBackWindow", "Events.eventType"]
private final Set<String> measures; // ["Events.count", "Events.uniqueCount"]
private final String filters; // Events.variant = ["B"] or Events.experiments = ["B"]
private final long limit;
private final long offset;
private final String timeDimensions; // Events.day day
private String orders; // Events.day ASC

private AnalyticsQuery(final Builder builder) {
this.dimensions = builder.dimensions;
this.measures = builder.measures;
this.filters = builder.filters;
this.limit = builder.limit;
this.offset = builder.offset;
this.timeDimensions = builder.timeDimensions;
this.orders = builder.orders;
}

public Set<String> getDimensions() {
return dimensions;
}

public Set<String> getMeasures() {
return measures;
}

public String getFilters() {
return filters;
}

public long getLimit() {
return limit;
}

public long getOffset() {
return offset;
}

public String getTimeDimensions() {
return timeDimensions;
}

public String getOrders() {
return orders;
}

public static class Builder {

@JsonProperty()
private Set<String> dimensions;
@JsonProperty()
private Set<String> measures;
@JsonProperty()
private String filters;
@JsonProperty()
private long limit;
@JsonProperty()
private long offset;
@JsonProperty()
private String timeDimensions;
@JsonProperty()
private String orders;


public Builder dimensions(Set<String> dimensions) {
this.dimensions = dimensions;
return this;
}

public Builder measures(Set<String> measures) {
this.measures = measures;
return this;
}

public Builder filters(String filters) {
this.filters = filters;
return this;
}

public Builder limit(long limit) {
this.limit = limit;
return this;
}

public Builder offset(long offset) {
this.offset = offset;
return this;
}

public Builder timeDimensions(String timeDimensions) {
this.timeDimensions = timeDimensions;
return this;
}

public Builder orders(String orders) {
this.orders = orders;
return this;
}

public AnalyticsQuery build() {
return new AnalyticsQuery(this);
}
}

public static Builder builder() {
return new Builder();
}

@Override
public String toString() {
return "AnalyticsQuery{" +
"dimensions=" + dimensions +
", measures=" + measures +
", filters='" + filters + '\'' +
", limit=" + limit +
", offset=" + offset +
", timeDimensions='" + timeDimensions + '\'' +
", orders='" + orders + '\'' +
'}';
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package com.dotcms.analytics.query;

import com.dotcms.cube.CubeJSQuery;
import com.dotcms.cube.filters.Filter;
import com.dotcms.cube.filters.LogicalFilter;
import com.dotcms.cube.filters.SimpleFilter;
import com.dotcms.rest.api.v1.DotObjectMapperProvider;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.vavr.Tuple2;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Parser for the analytics query, it can parse a json string to a {@link AnalyticsQuery} or a {@link CubeJSQuery}
* @author jsanca
*/
public class AnalyticsQueryParser {

/**
* Parse a json string to a {@link AnalyticsQuery}
* Example:
* {
* "dimensions": ["Events.referer", "Events.experiment", "Events.variant", "Events.utcTime", "Events.url", "Events.lookBackWindow", "Events.eventType"],
* "measures": ["Events.count", "Events.uniqueCount"],
* "filters": "Events.variant = ['B'] or Events.experiments = ['B']",
* "limit":100,
* "offset":1,
* "timeDimensions":"Events.day day",
* "orders":"Events.day ASC"
* }
* @param json
* @return AnalyticsQuery
*/
public AnalyticsQuery parseJsonToQuery(final String json) {

if (Objects.isNull(json)) {
throw new IllegalArgumentException("Json can not be null");
}
try {

Logger.debug(this, ()-> "Parsing json to query: " + json);
return DotObjectMapperProvider.getInstance().getDefaultObjectMapper()
.readValue(json, AnalyticsQuery.class);
} catch (JsonProcessingException e) {
Logger.error(this, e.getMessage(), e);
throw new DotRuntimeException(e);
}
}

/**
* Parse a json string to a {@link CubeJSQuery}
* Example:
* {
* "dimensions": ["Events.referer", "Events.experiment", "Events.variant", "Events.utcTime", "Events.url", "Events.lookBackWindow", "Events.eventType"],
* "measures": ["Events.count", "Events.uniqueCount"],
* "filters": "Events.variant = ['B'] or Events.experiments = ['B']",
* "limit":100,
* "offset":1,
* "timeDimensions":"Events.day day",
* "orders":"Events.day ASC"
* }
* @param json
* @return CubeJSQuery
*/
public CubeJSQuery parseJsonToCubeQuery(final String json) {

Logger.debug(this, ()-> "Parsing json to cube query: " + json);
final AnalyticsQuery query = parseJsonToQuery(json);
return parseQueryToCubeQuery(query);
}

/**
* Parse an {@link AnalyticsQuery} to a {@link CubeJSQuery}
* @param query
* @return CubeJSQuery
*/
public CubeJSQuery parseQueryToCubeQuery(final AnalyticsQuery query) {

if (Objects.isNull(query)) {
throw new IllegalArgumentException("Query can not be null");
}

final CubeJSQuery.Builder builder = new CubeJSQuery.Builder();
Logger.debug(this, ()-> "Parsing query to cube query: " + query);

if (UtilMethods.isSet(query.getDimensions())) {
builder.dimensions(query.getDimensions());
}

if (UtilMethods.isSet(query.getMeasures())) {
builder.measures(query.getMeasures());
}

if (UtilMethods.isSet(query.getFilters())) {
builder.filters(parseFilters(query.getFilters()));
}

builder.limit(query.getLimit()).offset(query.getOffset());

if (UtilMethods.isSet(query.getOrders())) {
builder.orders(parseOrders(query.getOrders()));
}

if (UtilMethods.isSet(query.getTimeDimensions())) {
builder.timeDimensions(parseTimeDimensions(query.getTimeDimensions()));
}

return builder.build();
}

private Collection<CubeJSQuery.TimeDimension> parseTimeDimensions(final String timeDimensions) {
final TimeDimensionParser.TimeDimension parsedTimeDimension = TimeDimensionParser.parseTimeDimension(timeDimensions);
return Stream.of(
new CubeJSQuery.TimeDimension(parsedTimeDimension.getTerm(),
parsedTimeDimension.getField())
).collect(Collectors.toList());
}

private Collection<CubeJSQuery.OrderItem> parseOrders(final String orders) {

final OrderParser.ParsedOrder parsedOrder = OrderParser.parseOrder(orders);
return Stream.of(
new CubeJSQuery.OrderItem(parsedOrder.getTerm(),
"ASC".equalsIgnoreCase(parsedOrder.getOrder())?
Filter.Order.ASC:Filter.Order.DESC)
).collect(Collectors.toList());
}

private Collection<Filter> parseFilters(final String filters) {
final Tuple2<List<FilterParser.Token>,List<FilterParser.LogicalOperator>> result =
FilterParser.parseFilterExpression(filters);

final List<Filter> filterList = new ArrayList<>();
final List<SimpleFilter> simpleFilters = new ArrayList<>();

for (final FilterParser.Token token : result._1) {

simpleFilters.add(
new SimpleFilter(token.member,
parseOperator(token.operator),
new Object[]{token.values}));
}

// if has operators
if (UtilMethods.isSet(result._2())) {

FilterParser.LogicalOperator logicalOperator = result._2().get(0); // first one
LogicalFilter.Builder logicalFilterBuilder = logicalOperator == FilterParser.LogicalOperator.AND?
LogicalFilter.Builder.and():LogicalFilter.Builder.or();

LogicalFilter logicalFilterFirst = logicalFilterBuilder.add(simpleFilters.get(0)).add(simpleFilters.get(1)).build();
for (int i = 1; i < result._2().size(); i++) { // nest the next ones

logicalOperator = result._2().get(i);
logicalFilterBuilder = logicalOperator == FilterParser.LogicalOperator.AND?
LogicalFilter.Builder.and():LogicalFilter.Builder.or();

logicalFilterFirst = logicalFilterBuilder.add(logicalFilterFirst)
.add(simpleFilters.get(i + 1)).build();
}

filterList.add(logicalFilterFirst);
} else {
filterList.addAll(simpleFilters);
}

return filterList;
}

private SimpleFilter.Operator parseOperator(final String operator) {
switch (operator) {
case "=":
return SimpleFilter.Operator.EQUALS;
case "!=":
return SimpleFilter.Operator.NOT_EQUALS;
case "in":
return SimpleFilter.Operator.CONTAINS;
case "!in":
return SimpleFilter.Operator.NOT_CONTAINS;
default:
throw new DotRuntimeException("Operator not supported: " + operator);
}
}
}
Loading
Loading