You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2018/12/14 15:02:24 UTC

[syncope] 02/02: [SYNCOPE-1416] merge on jsonb implementation

This is an automated email from the ASF dual-hosted git repository.

fmartelli pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 40fc384f9851868c4db267457d49fb7757b23a77
Author: fmartelli <fa...@gmail.com>
AuthorDate: Fri Dec 14 15:38:23 2018 +0100

    [SYNCOPE-1416] merge on jsonb implementation
---
 .../persistence/jpa/dao/PGJPAJSONAnySearchDAO.java | 105 ++++++++++++++++-----
 .../core/persistence/jpa/dao/JPAAnySearchDAO.java  |  11 +--
 .../core/persistence/jpa/dao/SearchSupport.java    |  24 +++++
 .../persistence/jpa/dao/SearchViewSupport.java     |   4 -
 .../org/apache/syncope/fit/core/SearchITCase.java  |   9 +-
 5 files changed, 118 insertions(+), 35 deletions(-)

diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
index f240705..75b8ff5 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
@@ -18,10 +18,16 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.text.ParseException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
@@ -34,17 +40,60 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
 
 public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
 
+    private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance(SyncopeConstants.DEFAULT_DATE_PATTERN);
+
     @Override
     SearchSupport buildSearchSupport(final AnyTypeKind kind) {
         return new SearchSupport(kind);
     }
 
     @Override
-    protected void processOBS(final SearchSupport svs, final OrderBySupport obs, final StringBuilder where) {
+    protected void processOBS(
+            final SearchSupport svs,
+            final Set<String> involvedPlainAttrs,
+            final OrderBySupport obs,
+            final StringBuilder where) {
+
+        Set<String> attrs = obs.items.stream().
+                map(item -> item.orderBy.substring(0, item.orderBy.indexOf(" "))).collect(Collectors.toSet());
+
         obs.views.forEach(searchView -> {
-            where.append(',').
-                    append(searchView.name).
-                    append(' ').append(searchView.alias);
+            if (searchView.name.equals(svs.field().name)) {
+                StringBuilder attrWhere = new StringBuilder();
+                StringBuilder nullAttrWhere = new StringBuilder();
+
+                where.append(", (SELECT * FROM ").append(searchView.name);
+
+                if (svs.nonMandatorySchemas || obs.nonMandatorySchemas) {
+                    attrs.forEach(field -> {
+                        if (attrWhere.length() == 0) {
+                            attrWhere.append(" WHERE ");
+                        } else {
+                            attrWhere.append(" OR ");
+                        }
+                        attrWhere.append("plainAttrs @> '[{\"schema\":\"").append(field).append("\"}]'::jsonb");
+
+                        nullAttrWhere.append(" UNION SELECT DISTINCT any_id,").append(svs.table().alias).append(".*, ").
+                                append("'{\"schema\": \"").
+                                append(field).
+                                append("\"}'::jsonb as attrs, '{}'::jsonb as attrValues").
+                                append(" FROM ").append(svs.table().name).append(" ").append(svs.table().alias).
+                                append(", ").append(svs.field().name).
+                                append(" WHERE ").
+                                append("any_id NOT IN ").
+                                append("(SELECT distinct any_id FROM ").
+                                append(svs.field().name).
+                                append(" WHERE ").append(svs.table().alias).append(".id=any_id AND ").
+                                append("plainAttrs @> '[{\"schema\":\"").append(field).append("\"}]'::jsonb)");
+                    });
+                    where.append(attrWhere).append(nullAttrWhere);
+                }
+
+                where.append(')');
+            } else {
+                where.append(',').append(searchView.name);
+            }
+            where.append(' ').append(searchView.alias);
         });
     }
 
@@ -62,20 +111,12 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
 
         obs.views.add(svs.field());
 
-        item.select = svs.field().alias + ".attrValues ->> '" + fieldName + "' AS " + fieldName;
+        item.select = svs.field().alias + ".attrValues ->> '" + field(schema, null) + "' AS " + fieldName;
         item.where = "attrs ->> 'schema' = '" + fieldName + "'";
         item.orderBy = fieldName + " " + clause.getDirection().name();
     }
 
-    private void fillAttrQuery(
-            final AnyUtils anyUtils,
-            final StringBuilder query,
-            final PlainAttrValue attrValue,
-            final PlainSchema schema,
-            final AttributeCond cond,
-            final boolean not,
-            final List<Object> parameters) {
-
+    private Pair<Boolean, String> field(final PlainSchema schema, final AttributeCond.Type type) {
         String key;
         boolean lower = false;
         switch (schema.getType()) {
@@ -100,9 +141,22 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
                 break;
 
             default:
-                lower = cond.getType() == AttributeCond.Type.IEQ || cond.getType() == AttributeCond.Type.ILIKE;
+                lower = type == AttributeCond.Type.IEQ || type == AttributeCond.Type.ILIKE;
                 key = "stringValue";
         }
+        return Pair.of(lower, key);
+    }
+
+    private void fillAttrQuery(
+            final AnyUtils anyUtils,
+            final StringBuilder query,
+            final PlainAttrValue attrValue,
+            final PlainSchema schema,
+            final AttributeCond cond,
+            final boolean not,
+            final List<Object> parameters) {
+
+        Pair<Boolean, String> field = field(schema, cond.getType());
 
         if (!not && cond.getType() == AttributeCond.Type.EQ) {
             PlainAttr<?> container = anyUtils.newPlainAttr();
@@ -119,11 +173,11 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
         } else {
             query.append("attrs ->> 'schema' = ?").append(setParameter(parameters, cond.getSchema())).
                     append(" AND ").
-                    append(lower ? "LOWER(" : "").
+                    append(field.getLeft() ? "LOWER(" : "").
                     append(schema.isUniqueConstraint()
                             ? "attrs -> 'uniqueValue'" : "attrValues").
-                    append(" ->> '").append(key).append("'").
-                    append(lower ? ")" : "");
+                    append(" ->> '").append(field.getRight()).append("'").
+                    append(field.getLeft() ? ")" : "");
 
             switch (cond.getType()) {
                 case LIKE:
@@ -175,9 +229,18 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
                     query.append('=');
             }
 
-            query.append(lower ? "LOWER(" : "").
-                    append("?").append(setParameter(parameters, cond.getExpression())).
-                    append(lower ? ")" : "");
+            String value = cond.getExpression();
+            if (schema.getType() == AttrSchemaType.Date) {
+                try {
+                    value = String.valueOf(DATE_FORMAT.parse(value).getTime());
+                } catch (ParseException e) {
+                    LOG.error("Could not parse {} as date", value, e);
+                }
+            }
+
+            query.append(field.getLeft() ? "LOWER(" : "").
+                    append("?").append(setParameter(parameters, value)).
+                    append(field.getLeft() ? ")" : "");
         }
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 11ace3a..0a50b2d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -245,10 +245,8 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
             final OrderBySupport obs,
             final StringBuilder where) {
 
-        Set<String> attrs = new HashSet<>(involvedPlainAttrs);
-        for (OrderBySupport.Item item : obs.items) {
-            attrs.add(item.orderBy.substring(0, item.orderBy.indexOf(" ")));
-        }
+        Set<String> attrs = obs.items.stream().
+                map(item -> item.orderBy.substring(0, item.orderBy.indexOf(" "))).collect(Collectors.toSet());
 
         obs.views.forEach(searchView -> {
             where.append(',');
@@ -261,10 +259,11 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                 if (svs.nonMandatorySchemas || obs.nonMandatorySchemas) {
                     attrs.forEach(field -> {
                         if (attrWhere.length() == 0) {
-                            attrWhere.append(" WHERE schema_id='").append(field).append("'");
+                            attrWhere.append(" WHERE ");
                         } else {
-                            attrWhere.append(" OR ").append("schema_id='").append(field).append("'");
+                            attrWhere.append(" OR ");
                         }
+                        attrWhere.append("schema_id='").append(field).append("'");
 
                         nullAttrWhere.append(" UNION SELECT any_id, ").
                                 append("'").
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
index 3304a70..f10b29a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
@@ -22,6 +22,9 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
+import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 
 public class SearchSupport {
 
@@ -103,6 +106,27 @@ public class SearchSupport {
         return result;
     }
 
+    public SearchView table() {
+        String result;
+
+        switch (anyTypeKind) {
+            case ANY_OBJECT:
+                result = JPAAnyObject.TABLE;
+                break;
+
+            case GROUP:
+                result = JPAGroup.TABLE;
+                break;
+
+            case USER:
+            default:
+                result = JPAUser.TABLE;
+                break;
+        }
+
+        return new SearchView("t", result);
+    }
+
     public SearchView field() {
         String result;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchViewSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchViewSupport.java
index 9dc95d8..93045f9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchViewSupport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchViewSupport.java
@@ -30,10 +30,6 @@ public class SearchViewSupport extends SearchSupport {
         return new SearchView("sva", field().name + "_attr");
     }
 
-    public SearchView nullAttr() {
-        return new SearchView("svna", field().name + "_null_attr");
-    }
-
     public SearchView uniqueAttr() {
         return new SearchView("svua", field().name + "_unique_attr");
     }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index 98cfd21..6151871 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -293,7 +293,7 @@ public class SearchITCase extends AbstractITCase {
         PagedResult<UserTO> issueSYNCOPE1416 = userService.search(new AnyQuery.Builder().
                 realm(SyncopeConstants.ROOT_REALM).
                 fiql(SyncopeClient.getUserSearchConditionBuilder().
-                        is("loginDate").lexicalNotBefore("2009-05-26").
+                        is("loginDate").lexicalNotBefore("2009-05-26T00:00:00+0300").
                         and("username").equalTo("rossini").query()).
                 orderBy(SyncopeClient.getOrderByClauseBuilder().asc("loginDate").build()).
                 build());
@@ -438,9 +438,10 @@ public class SearchITCase extends AbstractITCase {
     @Test
     public void orderBy() {
         PagedResult<UserTO> matchingUsers = userService.search(
-                new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
-                        fiql(SyncopeClient.getUserSearchConditionBuilder().is("userId").equalTo("*@apache.org").query()).
-                        orderBy(SyncopeClient.getOrderByClauseBuilder().asc("status").desc("firstname").build()).build());
+                new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).fiql(
+                        SyncopeClient.getUserSearchConditionBuilder().is("userId").equalTo("*@apache.org").query()).
+                        orderBy(SyncopeClient.getOrderByClauseBuilder().asc("status").desc("firstname").build()).
+                        build());
         assertNotNull(matchingUsers);
 
         assertFalse(matchingUsers.getResult().isEmpty());