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

sergeykamov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git


The following commit(s) were added to refs/heads/master by this push:
     new 78eea74  REST requests metadata processing changed.
78eea74 is described below

commit 78eea74a8d41c0da476a373e19393aa67412fbca
Author: Sergey Kamov <[email protected]>
AuthorDate: Mon May 17 23:52:30 2021 +0300

    REST requests metadata processing changed.
---
 nlpcraft/src/main/resources/sql/create_schema.sql  |  27 +-
 nlpcraft/src/main/resources/sql/drop_schema.sql    |   2 -
 .../org/apache/nlpcraft/common/util/NCUtils.scala  |  40 ++-
 .../model/intent/compiler/NCIdlCompiler.scala      |   1 -
 .../model/tools/embedded/NCEmbeddedResult.java     |   9 +-
 .../model/tools/test/NCTestClientBuilder.java      |  32 ++-
 .../nlpcraft/model/tools/test/NCTestResult.java    |  12 +-
 .../probe/mgrs/nlp/NCProbeEnrichmentManager.scala  |  15 +-
 .../probe/mgrs/nlp/impl/NCRequestImpl.scala        |  15 +-
 .../nlpcraft/server/company/NCCompanyManager.scala |  22 +-
 .../apache/nlpcraft/server/mdo/NCCompanyMdo.scala  |   1 +
 .../nlpcraft/server/mdo/NCCompanyPropertyMdo.scala |  41 ---
 .../nlpcraft/server/mdo/NCQueryStateMdo.scala      |   4 +-
 .../org/apache/nlpcraft/server/mdo/NCUserMdo.scala |   7 +-
 .../nlpcraft/server/mdo/NCUserPropertyMdo.scala    |  41 ---
 .../nlpcraft/server/probe/NCProbeManager.scala     |  28 +-
 .../server/proclog/NCProcessLogManager.scala       |   3 +
 .../nlpcraft/server/query/NCQueryManager.scala     |  29 ++-
 .../nlpcraft/server/rest/NCBasicRestApi.scala      | 190 ++++++++------
 .../apache/nlpcraft/server/sql/NCSqlManager.scala  | 283 ++++++++-------------
 .../nlpcraft/server/user/NCUserManager.scala       |  22 +-
 .../main/scala/org/apache/nlpcraft/utils/README.md |   2 -
 .../nlpcraft/model/meta/NCMetaResultSpec.scala     |  77 ++++++
 .../nlpcraft/server/rest/NCRestAskSpec.scala       |  16 ++
 .../apache/nlpcraft/server/rest/NCRestSpec.scala   |   3 +
 .../nlpcraft/server/rest/RestTestModel.scala       |  39 ++-
 openapi/nlpcraft_swagger.yml                       |  17 +-
 sql/mysql/drop_schema.sql                          |   2 -
 sql/mysql/schema.sql                               |  29 +--
 sql/oracle/drop_schema.sql                         |  16 --
 sql/oracle/schema.sql                              |  33 +--
 sql/postgres/drop_schema.sql                       |   2 -
 sql/postgres/schema.sql                            |  33 +--
 33 files changed, 506 insertions(+), 587 deletions(-)

diff --git a/nlpcraft/src/main/resources/sql/create_schema.sql 
b/nlpcraft/src/main/resources/sql/create_schema.sql
index 570e215..1965ca0 100644
--- a/nlpcraft/src/main/resources/sql/create_schema.sql
+++ b/nlpcraft/src/main/resources/sql/create_schema.sql
@@ -30,6 +30,7 @@ CREATE TABLE nc_company (
     postal_code VARCHAR,
     auth_token VARCHAR NOT NULL, -- Unique.
     auth_token_hash VARCHAR NOT NULL, -- Unique.
+    properties_gzip VARCHAR NULL,
     created_on TIMESTAMP NOT NULL,
     last_modified_on TIMESTAMP NOT NULL
 ) WITH "template=replicated, atomicity=transactional";
@@ -38,18 +39,6 @@ CREATE INDEX nc_company_idx_1 ON nc_company(name);
 CREATE INDEX nc_company_idx_2 ON nc_company(auth_token);
 CREATE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
-DROP TABLE IF EXISTS nc_company_property;
-CREATE TABLE nc_company_property (
-    id LONG PRIMARY KEY,
-    company_id LONG NOT NULL, -- Foreign key nc_company.id.
-    property VARCHAR NOT NULL,
-    value VARCHAR NULL,
-    created_on TIMESTAMP NOT NULL,
-    last_modified_on TIMESTAMP NOT NULL
-) WITH "template=replicated, atomicity=transactional";
-
-CREATE INDEX nc_company_property_idx_1 ON nc_company_property(company_id);
-
 DROP TABLE IF EXISTS nc_user;
 CREATE TABLE nc_user (
     id LONG PRIMARY KEY,
@@ -61,6 +50,7 @@ CREATE TABLE nc_user (
     last_name VARCHAR NULL,
     is_admin BOOL NOT NULL,
     passwd_salt VARCHAR NULL,
+    properties_gzip VARCHAR NULL,
     created_on TIMESTAMP NOT NULL,
     last_modified_on TIMESTAMP NOT NULL
 ) WITH "template=replicated, atomicity=transactional";
@@ -69,18 +59,6 @@ CREATE INDEX nc_user_idx_1 ON nc_user(email);
 CREATE INDEX nc_user_idx_2 ON nc_user(company_id, ext_id);
 CREATE INDEX nc_user_idx_3 ON nc_user(company_id);
 
-DROP TABLE IF EXISTS nc_user_property;
-CREATE TABLE nc_user_property (
-    id LONG PRIMARY KEY,
-    user_id LONG NOT NULL, -- Foreign key nc_user.id.
-    property VARCHAR NOT NULL,
-    value VARCHAR NULL,
-    created_on TIMESTAMP NOT NULL,
-    last_modified_on TIMESTAMP NOT NULL
-) WITH "template=replicated, atomicity=transactional";
-
-CREATE INDEX nc_user_property_idx_1 ON nc_user_property(user_id);
-
 DROP TABLE IF EXISTS passwd_pool;
 CREATE TABLE passwd_pool (
     id LONG PRIMARY KEY,
@@ -103,6 +81,7 @@ CREATE TABLE proc_log (
     cancel_tstamp TIMESTAMP NULL,
     res_type VARCHAR NULL,
     res_body_gzip VARCHAR NULL,
+    res_meta_gzip VARCHAR NULL,
     intent_id VARCHAR NULL,
     error VARCHAR NULL,
     probe_token VARCHAR NULL,
diff --git a/nlpcraft/src/main/resources/sql/drop_schema.sql 
b/nlpcraft/src/main/resources/sql/drop_schema.sql
index b1ce0b9..f985bae 100644
--- a/nlpcraft/src/main/resources/sql/drop_schema.sql
+++ b/nlpcraft/src/main/resources/sql/drop_schema.sql
@@ -16,9 +16,7 @@
 --
 
 DROP TABLE IF EXISTS proc_log;
-DROP TABLE IF EXISTS nc_user_property;
 DROP TABLE IF EXISTS nc_user;
-DROP TABLE IF EXISTS nc_company_property;
 DROP TABLE IF EXISTS nc_company;
 DROP TABLE IF EXISTS passwd_pool;
 DROP TABLE IF EXISTS feedback;
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 0248b56..4e2e7bd 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -1149,14 +1149,20 @@ object NCUtils extends LazyLogging {
       * @param rawStr String to compress.
       * @return Compressed Base64-encoded string.
       */
+    @throws[NCE]
     def compress(rawStr: String): String = {
         val arr = new ByteArrayOutputStream(1024)
 
-        managed(new GOS(arr)) acquireAndGet { zip ⇒
-            zip.write(rawStr.getBytes)
-        }
+        try {
+            managed(new GOS(arr)) acquireAndGet { zip ⇒
+                zip.write(rawStr.getBytes)
+            }
 
-        Base64.encodeBase64String(arr.toByteArray)
+            Base64.encodeBase64String(arr.toByteArray)
+        }
+        catch {
+            case e: Exception ⇒ throw new NCE("Error during data 
compression.", e)
+        }
     }
 
     /**
@@ -1165,8 +1171,13 @@ object NCUtils extends LazyLogging {
       * @param zipStr Compressed string.
       * @return Uncompressed string.
       */
+    @throws[NCE]
     def uncompress(zipStr: String): String =
-        IOUtils.toString(new GIS(new 
ByteArrayInputStream(Base64.decodeBase64(zipStr))), Charset.defaultCharset())
+        try
+            IOUtils.toString(new GIS(new 
ByteArrayInputStream(Base64.decodeBase64(zipStr))), Charset.defaultCharset())
+        catch {
+            case e: Exception ⇒ throw new NCE("Error during data 
decompression.", e)
+        }
 
     /**
       * Sleeps number of milliseconds properly handling exceptions.
@@ -1672,6 +1683,7 @@ object NCUtils extends LazyLogging {
      * @param json JSON to convert.
      * @return
      */
+    @throws[Exception]
     def jsonToScalaMap(json: String): Map[String, Object] =
         GSON.fromJson(json, classOf[java.util.HashMap[String, 
Object]]).asScala.toMap
 
@@ -1681,8 +1693,14 @@ object NCUtils extends LazyLogging {
      * @param json JSON to convert.
      * @return
      */
-    def jsonToJavaMap(json: String): java.util.Map[String, Object] =
-        GSON.fromJson(json, classOf[java.util.HashMap[String, Object]])
+    @throws[NCE]
+    def jsonToJavaMap(json: String): java.util.Map[String, Object] = {
+        try
+            GSON.fromJson(json, classOf[java.util.HashMap[String, Object]])
+        catch {
+            case e: Exception ⇒ throw new NCE(s"Cannot deserialize JSON to 
map: '$json'", e)
+        }
+    }
 
     /**
      *
@@ -1690,9 +1708,13 @@ object NCUtils extends LazyLogging {
      * @param field
      * @return
      */
-    @throws[Exception]
+    @throws[NCE]
     def getJsonBooleanField(json: String, field: String): Boolean =
-        
GSON.getAdapter(classOf[JsonElement]).fromJson(json).getAsJsonObject.get(field).getAsBoolean
+        try
+            
GSON.getAdapter(classOf[JsonElement]).fromJson(json).getAsJsonObject.get(field).getAsBoolean
+        catch {
+            case e: Exception ⇒ throw new NCE(s"Cannot deserialize JSON to 
map: '$json'", e)
+        }
 
     /**
      *
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompiler.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompiler.scala
index 3e9f737..50c4959 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompiler.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompiler.scala
@@ -86,7 +86,6 @@ object NCIdlCompiler extends LazyLogging {
         // List of instructions for the current expression.
         private var expr = mutable.Buffer.empty[SI]
 
-
         /**
          *
          * @return
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/embedded/NCEmbeddedResult.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/embedded/NCEmbeddedResult.java
index 3ece971..e18d2f6 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/embedded/NCEmbeddedResult.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/embedded/NCEmbeddedResult.java
@@ -18,6 +18,7 @@
 package org.apache.nlpcraft.model.tools.embedded;
 
 import org.apache.nlpcraft.model.*;
+
 import java.util.function.*;
 
 /**
@@ -30,7 +31,7 @@ import java.util.function.*;
  * @see NCResult
  * @see NCEmbeddedProbe
  */
-public interface NCEmbeddedResult {
+public interface NCEmbeddedResult extends NCMetadata {
     /**
      * Gets the ID of the model that produced this result. Note that embedded 
probe can host more than one
      * data model hence this parameter is important to distinguish to which 
model this result belongs.
@@ -173,11 +174,11 @@ public interface NCEmbeddedResult {
     String getProbeId();
 
     /**
-     * Gets request processing log holder as JSON string.
+     * Gets request processing log as JSON string.
      * 
-     * @return Request processing log holder as JSON string.
+     * @return Request processing log as JSON string.
      */
-    String getLogHolderJson();
+    String getLogJson();
 
     /**
      * Gets ID of the intent that was matched against the input sentence. Only 
set if result was
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestClientBuilder.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestClientBuilder.java
index df39787..434a773 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestClientBuilder.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestClientBuilder.java
@@ -192,6 +192,7 @@ public class NCTestClientBuilder {
         @SerializedName("usrId") private long userId;
         @SerializedName("resType") private String resType;
         @SerializedName("resBody") private Object resBody;
+        @SerializedName("resMeta") private Map<String, Object>  resMeta;
         @SerializedName("status") private String status;
         @SerializedName("error") private String error;
         @SerializedName("createTstamp") private long createTstamp;
@@ -288,6 +289,22 @@ public class NCTestClientBuilder {
 
         /**
          *
+         * @return
+         */
+        public Map<String, Object> getResultMeta() {
+            return resMeta;
+        }
+
+        /**
+         *
+         * @param resMeta
+         */
+        public void setResultMeta(Map<String, Object> resMeta) {
+            this.resMeta = resMeta;
+        }
+
+        /**
+         *
          * @param resBody
          */
         public void setResultBody(String resBody) {
@@ -557,7 +574,7 @@ public class NCTestClientBuilder {
                     e.getLocalizedMessage()
                 );
 
-                return mkResult(txt, mdlId, null, null, 
e.getLocalizedMessage(), null, 0);
+                return mkResult(txt, mdlId, null, null, null, 
e.getLocalizedMessage(), null, 0);
             }
 
             NCRequestStateJson state = resJs.getState();
@@ -573,6 +590,7 @@ public class NCTestClientBuilder {
                             gson.toJson(state.getResultBody()) :
                             (String)state.getResultBody() :
                         null,
+                    state.getResultMeta(),
                     state.getError(),
                     state.getIntentId(),
                     System.currentTimeMillis() - now
@@ -600,7 +618,7 @@ public class NCTestClientBuilder {
                     e.getLocalizedMessage()
                 );
 
-                return mkResult(txt, mdlId, null, null, 
e.getLocalizedMessage(), null,0);
+                return mkResult(txt, mdlId, null, null, null, 
e.getLocalizedMessage(), null,0);
             }
 
             long maxTime = System.currentTimeMillis() + DFLT_MAX_WAIT_TIME;
@@ -637,6 +655,7 @@ public class NCTestClientBuilder {
                     res.getBody() != null ?
                         "json".equals(res.getType()) ? 
gson.toJson(res.getBody()) : res.getBody() :
                         null,
+                    res.getMetadata(),
                     res.getErrorMessage(),
                     res.getIntentId(),
                     System.currentTimeMillis() - now
@@ -1007,6 +1026,7 @@ public class NCTestClientBuilder {
          * @param mdlId Model ID.
          * @param resType
          * @param resBody
+         * @param resMeta
          * @param errMsg
          * @param intentId
          * @param time
@@ -1017,6 +1037,7 @@ public class NCTestClientBuilder {
             String mdlId,
             String resType,
             String resBody,
+            Map<String, Object> resMeta,
             String errMsg,
             String intentId,
             long time
@@ -1026,7 +1047,7 @@ public class NCTestClientBuilder {
             assert (resType != null && resBody != null) ^ errMsg != null;
 
             return new NCTestResult() {
-                private Optional<String> convert(String s) {
+                private<T> Optional<T> convert(T s) {
                     return s == null ? Optional.empty() : Optional.of(s);
                 }
 
@@ -1064,6 +1085,11 @@ public class NCTestClientBuilder {
                 public String getIntentId() {
                     return intentId;
                 }
+
+                @Override
+                public Optional<Map<String, Object>> getResultMeta() {
+                    return convert(resMeta);
+                }
             };
         }
     }
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestResult.java
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestResult.java
index 44157be..130388b 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestResult.java
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/test/NCTestResult.java
@@ -17,6 +17,7 @@
 
 package org.apache.nlpcraft.model.tools.test;
 
+import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -61,7 +62,16 @@ public interface NCTestResult {
      * @see #isOk()
      */
     Optional<String> getResultType();
-    
+
+    /**
+     * Gets optional execution result metadata. Only provided if processing 
succeeded.
+     *
+     * @return Optional execution result metadata.
+     * @see #isFailed()
+     * @see #isOk()
+     */
+    Optional<Map<String, Object>> getResultMeta();
+
     /**
      * Gets optional execution error. Only provided if processing failed.
      *
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
index 398d1c2..c0c7a21 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/NCProbeEnrichmentManager.scala
@@ -275,6 +275,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
           *
           * @param resType Result type.
           * @param resBody Result body.
+          * @param resMeta Result meta.
           * @param errMsg Error message.
           * @param errCode Error code.
           * @param msgName Message name.
@@ -284,6 +285,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
         def respond(
             resType: Option[String],
             resBody: Option[String],
+            resMeta: Option[JavaMeta],
             errMsg: Option[String],
             errCode: Option[Int],
             msgName: String,
@@ -302,6 +304,10 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                 if (vOpt.isDefined)
                     msg += name → vOpt.get
 
+            def addMeta(name: String, vOpt: Option[JavaMeta]): Unit =
+                if (vOpt.isDefined)
+                    msg += name → vOpt.get.asInstanceOf[Serializable]
+
             if (resBody.isDefined && resBody.get.length > 
Config.resultMaxSize) {
                 addOptional("error", Some("Result is too big. Model result 
must be corrected."))
                 addOptional("errorCode", Some(RESULT_TOO_BIG))
@@ -311,6 +317,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                 addOptional("errorCode", errCode.map(Integer.valueOf))
                 addOptional("resType", resType)
                 addOptional("resBody", resBody)
+                addMeta("resMeta", resMeta)
                 addOptional("log", log)
                 addOptional("intentId", intentId)
             }
@@ -322,11 +329,12 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                     override val getOriginalText: String = txt
                     override val getUserId: Long = usrId
                     override val getBody: String = 
msg.dataOpt[String]("resBody").orNull
+                    override val getMetadata: JavaMeta = 
msg.dataOpt[JavaMeta]("resMeta").orNull
                     override val getType: String = 
msg.dataOpt[String]("resType").orNull
                     override val getErrorMessage: String = 
msg.dataOpt[String]("error").orNull
                     override val getErrorCode: Int = 
msg.dataOpt[Int]("errorCode").getOrElse(0)
                     override def getProbeId: String = Config.id
-                    override def getLogHolderJson: String = log.orNull
+                    override def getLogJson: String = log.orNull
                     override def getIntentId: String = intentId.orNull
                 }
 
@@ -394,6 +402,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
             respond(
                 None,
                 None,
+                None,
                 Some(errMsg),
                 Some(errCode),
                 "P2S_ASK_RESULT",
@@ -531,6 +540,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                 respond(
                     None,
                     None,
+                    None,
                     Some(errMsg),
                     Some(errCode),
                     "P2S_ASK_RESULT",
@@ -621,6 +631,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
         def respondWithResult(res: NCResult, log: Option[String]): Unit = 
respond(
             Some(res.getType),
             Some(res.getBody),
+            Some(res.getMetadata),
             None,
             None,
             "P2S_ASK_RESULT",
@@ -693,6 +704,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                             respond(
                                 None,
                                 None,
+                                None,
                                 Some(e.getMessage), // User provided rejection 
message.
                                 Some(MODEL_REJECTION),
                                 "P2S_ASK_RESULT",
@@ -722,6 +734,7 @@ object NCProbeEnrichmentManager extends NCService with 
NCOpenCensusModelStats {
                             respond(
                                 None,
                                 None,
+                                None,
                                 Some("Processing failed with unexpected 
error."), // System error message.
                                 Some(UNEXPECTED_ERROR),
                                 "P2S_ASK_RESULT",
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/impl/NCRequestImpl.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/impl/NCRequestImpl.scala
index d828961..394a96e 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/impl/NCRequestImpl.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/impl/NCRequestImpl.scala
@@ -17,14 +17,13 @@
 
 package org.apache.nlpcraft.probe.mgrs.nlp.impl
 
-import java.util.{Collections, Optional}
 import org.apache.nlpcraft.common._
 import org.apache.nlpcraft.model._
 import org.apache.nlpcraft.model.impl._
 
-import java.util
-import scala.collection._
+import java.util.{Collections, Optional}
 import scala.collection.JavaConverters._
+import scala.collection._
 import scala.compat.java8.OptionConverters._
 
 /**
@@ -55,7 +54,7 @@ case class NCRequestImpl(nlpMeta: Map[String, Any], srvReqId: 
String) extends NC
         getOpt("COMPANY_CITY"),
         getOpt("COMPANY_ADDRESS"),
         getOpt("COMPANY_POSTAL"),
-        getMap("COMPANY_META")
+        getOpt("COMPANY_META").orElse(Collections.emptyMap())
     )
     override lazy val getUser: NCUser = new NCUserImpl(
         nlpMeta("USER_ID").asInstanceOf[Long],
@@ -63,7 +62,7 @@ case class NCRequestImpl(nlpMeta: Map[String, Any], srvReqId: 
String) extends NC
         getOpt("LAST_NAME"),
         getOpt("EMAIL"),
         getOpt("AVATAR_URL"),
-        getMap("META"),
+        getOpt("META").orElse(Collections.emptyMap()),
         nlpMeta("IS_ADMIN").asInstanceOf[Boolean],
         nlpMeta("SIGNUP_TSTAMP").asInstanceOf[Long]
     )
@@ -73,10 +72,4 @@ case class NCRequestImpl(nlpMeta: Map[String, Any], 
srvReqId: String) extends NC
             case Some(v) ⇒ Optional.of(v.asInstanceOf[T])
             case None ⇒ Optional.empty()
         }
-
-    private def getMap(key: String): util.Map[String, AnyRef] = {
-        val m: Optional[JavaMeta] = getOpt(key)
-
-        if (m.isPresent) m.get() else Collections.emptyMap()
-    }
 }
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
index 41a6444..0e3937f 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/company/NCCompanyManager.scala
@@ -21,7 +21,7 @@ import io.opencensus.trace.Span
 import org.apache.ignite.{IgniteAtomicSequence, IgniteSemaphore}
 import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.server.ignite.NCIgniteInstance
-import org.apache.nlpcraft.server.mdo.{NCCompanyMdo, NCCompanyPropertyMdo}
+import org.apache.nlpcraft.server.mdo.NCCompanyMdo
 import org.apache.nlpcraft.server.sql.{NCSql, NCSqlManager}
 import org.apache.nlpcraft.server.user.NCUserManager
 
@@ -168,7 +168,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
-        props: Option[Map[String, String]],
+        props: Option[String],
         parent: Span = null
     ): Unit =
         startScopedSpan("updateCompany", parent, "id" → id) { span ⇒
@@ -282,7 +282,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         adminFirstName: String,
         adminLastName: String,
         adminAvatarUrl: Option[String],
-        props: Option[Map[String, String]],
+        props: Option[String],
         mkToken: () ⇒ String,
         parent: Span = null
     ): NCCompanyCreationData = {
@@ -371,7 +371,7 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
         adminFirstName: String,
         adminLastName: String,
         adminAvatarUrl: Option[String],
-        props: Option[Map[String, String]],
+        props: Option[String],
         parent: Span = null
     ): NCCompanyCreationData = startScopedSpan("addCompany", parent, "name" → 
name) { _ ⇒
         addCompany0(
@@ -391,18 +391,4 @@ object NCCompanyManager extends NCService with 
NCIgniteInstance {
             () ⇒ mkToken()
         )
     }
-
-    /**
-      * Gets company properties for given company ID.
-      *
-      * @param id User ID.
-      * @param parent Optional parent span.
-      */
-    @throws[NCE]
-    def getCompanyProperties(id: Long, parent: Span = null): 
Seq[NCCompanyPropertyMdo] =
-        startScopedSpan("getCompanyProperties", parent, "companyId" → id) { 
span ⇒
-            NCSql.sql {
-                NCSqlManager.getCompanyProperties(id, span)
-            }
-        }
 }
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyMdo.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyMdo.scala
index c4aa5a8..2405bf2 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyMdo.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyMdo.scala
@@ -37,6 +37,7 @@ case class NCCompanyMdo(
     @NCMdoField(column = "postal_code") postalCode: Option[String],
     @NCMdoField(column = "auth_token") authToken: String,
     @NCMdoField(column = "auth_token_hash") authTokenHash: String,
+    @NCMdoField(column = "properties_gzip") propertiesGzip: Option[String],
     @NCMdoField(column = "created_on") createdOn: Timestamp,
     @NCMdoField(column = "last_modified_on") lastModifiedOn: Timestamp
 ) extends NCAnnotatedMdo[NCCompanyMdo]
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyPropertyMdo.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyPropertyMdo.scala
deleted file mode 100644
index 695a41c..0000000
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCCompanyPropertyMdo.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.nlpcraft.server.mdo
-
-import org.apache.nlpcraft.server.mdo.impl._
-import org.apache.nlpcraft.server.sql.NCSql.Implicits.RsParser
-
-import java.sql.Timestamp
-
-/**
-  * Company property MDO.
-  */
-@NCMdoEntity(table = "nc_company_property")
-case class NCCompanyPropertyMdo(
-    @NCMdoField(column = "id", pk = true) id: Long,
-    @NCMdoField(column = "company_id") userId: Long,
-    @NCMdoField(column = "property") property: String,
-    @NCMdoField(column = "value") value: String,
-    @NCMdoField(column = "created_on") createdOn: Timestamp,
-    @NCMdoField(column = "last_modified_on") lastModifiedOn: Timestamp
-) extends NCAnnotatedMdo[NCCompanyPropertyMdo]
-
-object NCCompanyPropertyMdo {
-    implicit val x: RsParser[NCCompanyPropertyMdo] =
-        NCAnnotatedMdo.mkRsParser(classOf[NCCompanyPropertyMdo])
-}
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
index 27f440a..7677b36 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCQueryStateMdo.scala
@@ -17,8 +17,9 @@
 
 package org.apache.nlpcraft.server.mdo
 
-import java.sql.Timestamp
+import org.apache.nlpcraft.common.JavaMeta
 
+import java.sql.Timestamp
 import org.apache.nlpcraft.server.sql.NCSql.Implicits.RsParser
 import org.apache.nlpcraft.server.mdo.impl._
 
@@ -45,6 +46,7 @@ case class NCQueryStateMdo(
     // Query OK.
     @NCMdoField var resultType: Option[String] = None,
     @NCMdoField var resultBody: Option[String] = None,
+    @NCMdoField var resultMeta: Option[JavaMeta] = None,
     // Query ERROR.
     @NCMdoField var error: Option[String] = None,
     @NCMdoField var errorCode: Option[Int] = None
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserMdo.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserMdo.scala
index 9d7be9d..063a4be 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserMdo.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserMdo.scala
@@ -36,6 +36,7 @@ case class NCUserMdo(
     @NCMdoField(column = "last_name") lastName: Option[String],
     @NCMdoField(column = "avatar_url") avatarUrl: Option[String],
     @NCMdoField(column = "passwd_salt") passwordSalt: Option[String],
+    @NCMdoField(column = "properties_gzip") propertiesGzip: Option[String],
     @NCMdoField(column = "is_admin") isAdmin: Boolean,
     @NCMdoField(column = "created_on") createdOn: Timestamp,
     @NCMdoField(column = "last_modified_on") lastModifiedOn: Timestamp
@@ -54,11 +55,12 @@ object NCUserMdo {
         lastName: Option[String],
         avatarUrl: Option[String],
         passwordSalt: Option[String],
+        propertiesGzip: Option[String],
         isAdmin: Boolean
     ): NCUserMdo = {
         val now = U.nowUtcTs()
 
-        NCUserMdo(id, companyId, extId, email, firstName, lastName, avatarUrl, 
passwordSalt, isAdmin, now, now)
+        NCUserMdo(id, companyId, extId, email, firstName, lastName, avatarUrl, 
passwordSalt, propertiesGzip, isAdmin, now, now)
     }
 
     def apply(
@@ -70,11 +72,12 @@ object NCUserMdo {
         lastName: Option[String],
         avatarUrl: Option[String],
         passwordSalt: Option[String],
+        propertiesGzip: Option[String],
         isAdmin: Boolean,
         createdOn: Timestamp
     ): NCUserMdo = {
         require(createdOn != null, "Created date cannot be null.")
 
-        NCUserMdo(id, companyId, extId, email, firstName, lastName, avatarUrl, 
passwordSalt, isAdmin, createdOn, createdOn)
+        NCUserMdo(id, companyId, extId, email, firstName, lastName, avatarUrl, 
passwordSalt, propertiesGzip, isAdmin, createdOn, createdOn)
     }
 }
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserPropertyMdo.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserPropertyMdo.scala
deleted file mode 100644
index f1813cd..0000000
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCUserPropertyMdo.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.nlpcraft.server.mdo
-
-import java.sql.Timestamp
-
-import org.apache.nlpcraft.server.mdo.impl._
-import org.apache.nlpcraft.server.sql.NCSql.Implicits.RsParser
-
-/**
-  * User property MDO.
-  */
-@NCMdoEntity(table = "nc_user_property")
-case class NCUserPropertyMdo(
-    @NCMdoField(column = "id", pk = true) id: Long,
-    @NCMdoField(column = "user_id") userId: Long,
-    @NCMdoField(column = "property") property: String,
-    @NCMdoField(column = "value") value: String,
-    @NCMdoField(column = "created_on") createdOn: Timestamp,
-    @NCMdoField(column = "last_modified_on") lastModifiedOn: Timestamp
-) extends NCAnnotatedMdo[NCUserPropertyMdo]
-
-object NCUserPropertyMdo {
-    implicit val x: RsParser[NCUserPropertyMdo] =
-        NCAnnotatedMdo.mkRsParser(classOf[NCUserPropertyMdo])
-}
\ No newline at end of file
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
index 34b654b..9452b83 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
@@ -722,6 +722,7 @@ object NCProbeManager extends NCService {
                         val errCodeOpt = probeMsg.dataOpt[Int]("errorCode")
                         val resTypeOpt = probeMsg.dataOpt[String]("resType")
                         val resBodyOpt = probeMsg.dataOpt[String]("resBody")
+                        val resMetaOpt = probeMsg.dataOpt[JavaMeta]("resMeta")
                         val logJson = probeMsg.dataOpt[String]("log")
                         val intentId = probeMsg.dataOpt[String]("intentId")
 
@@ -740,13 +741,11 @@ object NCProbeManager extends NCService {
                         else { // OK result.
                             require(resTypeOpt.isDefined && 
resBodyOpt.isDefined, "Result defined")
                      
-                            val resType = resTypeOpt.get
-                            val resBody = resBodyOpt.get
-                     
                             NCQueryManager.setResult(
                                 srvReqId,
-                                resType,
-                                resBody,
+                                resTypeOpt.get,
+                                resBodyOpt.get,
+                                resMetaOpt,
                                 logJson,
                                 intentId
                             )
@@ -849,22 +848,11 @@ object NCProbeManager extends NCService {
         usrAgent: Option[String],
         rmtAddr: Option[String],
         data: Option[String],
-        usrMeta: Option[Map[String, String]],
-        companyMeta: Option[Map[String, String]],
+        usrMeta: Option[JavaMeta],
+        companyMeta: Option[JavaMeta],
         enableLog: Boolean,
         parent: Span = null): Unit = {
         startScopedSpan("askProbe", parent, "srvReqId" → srvReqId, "usrId" → 
usr.id, "mdlId" → mdlId, "txt" → txt) { span ⇒
-            def convertMeta(metaOpt: Option[Map[String, String]]): 
util.HashMap[String, String] =
-                metaOpt match {
-                    case Some(meta) ⇒
-                        val map = new util.HashMap[String, String]()
-
-                        meta.foreach { case (k, v) ⇒ map.put(k, v) }
-
-                        map
-                    case None ⇒ null
-                }
-
             val senMeta = new util.HashMap[String, java.io.Serializable]()
 
             Map(
@@ -880,7 +868,7 @@ object NCProbeManager extends NCService {
                 "IS_ADMIN" → usr.isAdmin,
                 "AVATAR_URL" → usr.avatarUrl.orNull,
                 "DATA" → data.orNull,
-                "META" → convertMeta(usrMeta),
+                "META" → usrMeta.orNull,
                 "COMPANY_ID" → company.id,
                 "COMPANY_NAME" → company.name,
                 "COMPANY_WEBSITE" → company.website.orNull,
@@ -889,7 +877,7 @@ object NCProbeManager extends NCService {
                 "COMPANY_CITY" → company.city.orNull,
                 "COMPANY_ADDRESS" → company.address.orNull,
                 "COMPANY_POSTAL" → company.postalCode.orNull,
-                "COMPANY_META" → convertMeta(companyMeta)
+                "COMPANY_META" → companyMeta.orNull
             ).
                 filter(_._2 != null).
                 foreach(p ⇒ senMeta.put(p._1, 
p._2.asInstanceOf[java.io.Serializable]))
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/proclog/NCProcessLogManager.scala
 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/proclog/NCProcessLogManager.scala
index f7958b9..436a0e1 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/proclog/NCProcessLogManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/proclog/NCProcessLogManager.scala
@@ -91,6 +91,7 @@ object NCProcessLogManager extends NCService with 
NCIgniteInstance {
       * @param errMsg
       * @param resType
       * @param resBody
+      * @param resMeta
       * @param intentId
       * @param parent Optional parent span.
       */
@@ -101,6 +102,7 @@ object NCProcessLogManager extends NCService with 
NCIgniteInstance {
         errMsg: Option[String],
         resType: Option[String],
         resBody: Option[String],
+        resMeta: Option[JavaMeta],
         intentId: Option[String],
         parent: Span = null
     ): Unit =
@@ -115,6 +117,7 @@ object NCProcessLogManager extends NCService with 
NCIgniteInstance {
                     errMsg.orNull,
                     resType.orNull,
                     resBody.orNull,
+                    resMeta.orNull,
                     intentId.orNull,
                     tstamp,
                     span
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
index ad6fdb1..2b762f3 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/query/NCQueryManager.scala
@@ -22,6 +22,7 @@ import org.apache.ignite.IgniteCache
 import org.apache.ignite.events.{CacheEvent, EventType}
 import org.apache.nlpcraft.common.ascii.NCAsciiTable
 import org.apache.nlpcraft.common.pool.NCThreadPoolManager
+import org.apache.nlpcraft.common.util.NCUtils.{jsonToJavaMap, uncompress}
 import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.server.apicodes.NCApiStatusCode._
 import org.apache.nlpcraft.server.company.NCCompanyManager
@@ -221,18 +222,6 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
         val usr = NCUserManager.getUserById(usrId, parent).getOrElse(throw new 
NCE(s"Unknown user ID: $usrId"))
         val company = NCCompanyManager.getCompany(usr.companyId, 
parent).getOrElse(throw new NCE(s"Unknown company ID: ${usr.companyId}"))
 
-        val usrMeta = {
-            val m = NCUserManager.getUserProperties(usrId, parent)
-
-            if (m.isEmpty) None else Some(m.map(p ⇒ p.property → 
p.value).toMap)
-        }
-
-        val compMeta = {
-            val m = NCCompanyManager.getCompanyProperties(usr.companyId, 
parent)
-
-            if (m.isEmpty) None else Some(m.map(p ⇒ p.property → 
p.value).toMap)
-        }
-
         // Check input length.
         if (txt0.split(" ").length > MAX_WORDS)
             throw new NCE(s"User input is too long (max is $MAX_WORDS words).")
@@ -285,6 +274,13 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
 
                 val enabledBuiltInToks = NCProbeManager.getModel(mdlId, 
span).enabledBuiltInTokens
 
+                @throws[NCE]
+                def unzipProperties(gzipOpt: Option[String]): Option[JavaMeta] 
=
+                    gzipOpt match {
+                        case Some(gzip) ⇒ Some(jsonToJavaMap(uncompress(gzip)))
+                        case None ⇒ None
+                    }
+
                 // Enrich the user input and send it to the probe.
                 NCProbeManager.askProbe(
                     srvReqId,
@@ -296,8 +292,8 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
                     usrAgent,
                     rmtAddr,
                     data,
-                    usrMeta,
-                    compMeta,
+                    unzipProperties(usr.propertiesGzip),
+                    unzipProperties(company.propertiesGzip),
                     enableLog,
                     span
                 )
@@ -369,6 +365,7 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
                     None,
                     None,
                     None,
+                    None,
                     span
                 )
         }
@@ -379,6 +376,7 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
       * @param srvReqId Server request ID.
       * @param resType
       * @param resBody
+      * @param resMeta
       * @param logJson
       * @param intentId
       * @param parent Optional parent span.
@@ -388,6 +386,7 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
         srvReqId: String,
         resType: String,
         resBody: String,
+        resMeta: Option[JavaMeta],
         logJson: Option[String],
         intentId: Option[String],
         parent: Span = null
@@ -407,6 +406,7 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
                         copy.status = QRY_READY.toString
                         copy.resultType = Some(resType)
                         copy.resultBody = Some(resBody)
+                        copy.resultMeta = resMeta
                         copy.logJson = logJson
                         copy.intentId = intentId
     
@@ -430,6 +430,7 @@ object NCQueryManager extends NCService with 
NCIgniteInstance with NCOpenCensusS
                     None,
                     resType = Some(resType),
                     resBody = Some(resBody),
+                    resMeta = resMeta,
                     intentId = intentId,
                     span
                 )
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
index a681a8f..4d9750b 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
@@ -24,14 +24,17 @@ import akka.http.scaladsl.model._
 import akka.http.scaladsl.model.headers._
 import akka.http.scaladsl.server.Directives.{entity, _}
 import akka.http.scaladsl.server._
-import com.google.gson.Gson
+import com.fasterxml.jackson.core.JsonProcessingException
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
 import com.typesafe.scalalogging.LazyLogging
 import io.opencensus.stats.Measure
 import io.opencensus.trace.{Span, Status}
 import org.apache.commons.validator.routines.UrlValidator
 import org.apache.nlpcraft.common.opencensus.NCOpenCensusTrace
 import org.apache.nlpcraft.common.pool.NCThreadPoolManager
-import org.apache.nlpcraft.common.{NCE, U}
+import org.apache.nlpcraft.common.util.NCUtils.{jsonToJavaMap, uncompress}
+import org.apache.nlpcraft.common.{JavaMeta, NCE, U}
 import org.apache.nlpcraft.model.NCModelView
 import org.apache.nlpcraft.server.apicodes.NCApiStatusCode.{API_OK, _}
 import org.apache.nlpcraft.server.company.NCCompanyManager
@@ -52,12 +55,10 @@ import scala.concurrent.{ExecutionContext, Future}
   * REST API default implementation.
   */
 class NCBasicRestApi extends NCRestApi with LazyLogging with NCOpenCensusTrace 
with NCOpenCensusServerStats {
-    protected final val GSON = new Gson()
     protected final val URL_VALIDATOR = new UrlValidator(Array("http", 
"https"), UrlValidator.ALLOW_LOCAL_URLS)
 
     final val API_VER = 1
     final val API = "api" / s"v$API_VER"
-
     /** */
     private final val CORS_HDRS = List(
         `Access-Control-Allow-Origin`.*,
@@ -65,6 +66,10 @@ class NCBasicRestApi extends NCRestApi with LazyLogging with 
NCOpenCensusTrace w
         `Access-Control-Allow-Headers`("Authorization", "Content-Type", 
"X-Requested-With")
     )
 
+    private final val JS_MAPPER = new ObjectMapper()
+
+    JS_MAPPER.registerModule(DefaultScalaModule)
+
     /*
      * General control exception.
      * Note that these classes must be public because scala 2.11 internal 
errors (compilations problems).
@@ -132,6 +137,20 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
 
     /**
       *
+      * @param o
+      * @throws
+      * @return
+      */
+    @throws[NCE]
+    private def toJs(o: AnyRef): String =
+        try
+            JS_MAPPER.writeValueAsString(o)
+        catch {
+            case e: JsonProcessingException ⇒ throw new NCE(s"JSON 
serialization error for: $o", e)
+        }
+
+    /**
+      *
       * @param acsTkn Access token to check.
       * @param shouldBeAdmin Admin flag.
       * @return
@@ -220,28 +239,38 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                 else
                     s.resultBody.orNull
                 ),
+            "resMeta" → s.resultMeta.orNull,
             "error" → s.error.orNull,
             "errorCode" → s.errorCode.map(Integer.valueOf).orNull,
             "logHolder" → (if (s.logJson.isDefined) 
U.jsonToObject(s.logJson.get) else null),
             "intentId" → s.intentId.orNull
         ).filter(_._2 != null).asJava
 
+
     /**
-      * Checks properties.
+      * Extracts and checks JSON.
       *
-      * @param propsOpt Optional properties.
+      * @param jsOpt JSON value. Optional.
+      * @param name Property name.
       */
     @throws[TooLargeField]
-    private def checkProperties(propsOpt: Option[Map[String, String]]): Unit =
-        propsOpt match {
-            case Some(props) ⇒
-                props.foreach { case (k, v) ⇒
-                    checkLength(k, k, 64)
-
-                    if (v != null && v.nonEmpty && v.length > 512)
-                        throw TooLargeField(v, 512)
+    @throws[InvalidField]
+    private def extractJson(jsOpt: Option[spray.json.JsValue], name: String): 
Option[String] =
+        jsOpt match {
+            case Some(js) ⇒
+                val s = js.compactPrint
+
+                checkLength(name, s, 512000)
+
+                // Validates.
+                try
+                    U.jsonToJavaMap(s)
+                catch {
+                    case _: NCE ⇒ throw InvalidField(name)
                 }
-            case None ⇒ // No-op.
+
+                Some(s)
+            case None ⇒ None
         }
 
     /**
@@ -526,9 +555,28 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
       *
       * @param fut
       */
-    private def successWithJs(fut: Future[String]): Route = onSuccess(fut) {
-        js ⇒ complete(HttpResponse(entity = 
HttpEntity(ContentTypes.`application/json`, js)))
-    }
+    private def successWithJs(fut: Future[String]): Route =
+        onSuccess(fut) {
+            js ⇒ complete(HttpResponse(entity = 
HttpEntity(ContentTypes.`application/json`, js)))
+        }
+
+    /**
+      *
+      * @param o
+      */
+    private def completeJs(o: Object): Route =
+        complete(HttpResponse(entity = 
HttpEntity(ContentTypes.`application/json`, toJs(o))))
+
+    /**
+      *
+      * @param gzipOpt
+      */
+    @throws[NCE]
+    private def unzipProperties(gzipOpt: Option[String]): Option[JavaMeta] =
+        gzipOpt match {
+            case Some(gzip) ⇒ Some(jsonToJavaMap(uncompress(gzip)))
+            case None ⇒ None
+        }
 
     /**
       *
@@ -559,14 +607,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "acsTok" → req.acsTok, "usrExtId" → req.usrExtId, "mdlId" 
→ req.mdlId, "txt" → req.txt
                 )
 
-                val dataJsOpt =
-                    req.data match {
-                        case Some(data) ⇒ Some(data.compactPrint)
-                        case None ⇒ None
-                    }
-
-                checkLengthOpt("data", dataJsOpt,512000)
-
+                val dataJs = extractJson(req.data, "data")
                 val acsUsr = authenticate(req.acsTok)
 
                 checkModelId(req.mdlId, acsUsr.companyId)
@@ -579,7 +620,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                             mdlId = req.mdlId,
                             usrAgent = usrAgent,
                             rmtAddr = getAddress(rmtAddr),
-                            data = dataJsOpt,
+                            data = dataJs,
                             req.enableLog.getOrElse(false),
                             parent = span
                         ))
@@ -640,8 +681,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
 
                 successWithJs(
                     fut.collect {
-                        // We have to use GSON (not spray) here to serialize 
'resBody' field.
-                        case res ⇒ GSON.toJson(
+                        // We have to use Jackson (not spray) here to 
serialize 'resBody' field.
+                        case res ⇒ toJs(
                             Map(
                                 "status" → API_OK.toString,
                                 "state" → queryStateToMap(res)
@@ -724,8 +765,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                         toSeq.sortBy(-_.createTstamp.getTime).
                         take(req.maxRows.getOrElse(Integer.MAX_VALUE))
 
-                // We have to use GSON (not spray) here to serialize 'resBody' 
field.
-                val js = GSON.toJson(
+                // We have to use Jackson (not spray) here to serialize 
'resBody' field.
+                val js = toJs(
                     Map(
                         "status" → API_OK.toString,
                         "states" → states.map(queryStateToMap).asJava
@@ -772,8 +813,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
 
                 successWithJs(
                     fut.collect {
-                        // We have to use GSON (not spray) here to serialize 
'result' field.
-                        case res ⇒ GSON.toJson(Map("status" → API_OK.toString, 
"result" → res).asJava)
+                        // We have to use Jackson (not spray) here to 
serialize 'result' field.
+                        case res ⇒ toJs(Map("status" → API_OK.toString, 
"result" → res).asJava)
                     }
                 )
             }
@@ -881,7 +922,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             adminFirstName: String,
             adminLastName: String,
             adminAvatarUrl: Option[String],
-            properties: Option[Map[String, String]]
+            properties: Option[spray.json.JsValue]
         )
         case class Res$Company$Add(
             status: String,
@@ -911,7 +952,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "adminAvatarUrl" → req.adminAvatarUrl
                 )
 
-                checkProperties(req.properties)
+                val propsJs = extractJson(req.properties, "properties")
 
                 // Via REST only administrators of already created companies 
can create new companies.
                 authenticateAsAdmin(req.acsTok)
@@ -929,7 +970,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.adminFirstName,
                     req.adminLastName,
                     req.adminAvatarUrl,
-                    req.properties,
+                    propsJs,
                     span
                 )
 
@@ -958,11 +999,10 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             city: Option[String],
             address: Option[String],
             postalCode: Option[String],
-            properties: Option[Map[String, String]]
+            properties: Option[JavaMeta]
         )
 
         implicit val reqFmt: RootJsonFormat[Req$Company$Get] = 
jsonFormat1(Req$Company$Get)
-        implicit val resFmt: RootJsonFormat[Res$Company$Get] = 
jsonFormat10(Res$Company$Get)
 
         entity(as[Req$Company$Get]) { req ⇒
             startScopedSpan("company$get", "acsTok" → req.acsTok) { span ⇒
@@ -974,9 +1014,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     getCompany(acsUsr.companyId, span).
                     getOrElse(throw new NCE(s"Company not found: 
${acsUsr.companyId}"))
 
-                val props = 
NCCompanyManager.getCompanyProperties(acsUsr.companyId, span)
-
-                complete {
+                completeJs {
                     Res$Company$Get(API_OK,
                         company.id,
                         company.name,
@@ -986,7 +1024,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                         company.city,
                         company.address,
                         company.postalCode,
-                        if (props.isEmpty) None else Some(props.map(p ⇒ 
p.property → p.value).toMap)
+                        unzipProperties(company.propertiesGzip)
                     )
                 }
             }
@@ -1010,7 +1048,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             city: Option[String],
             address: Option[String],
             postalCode: Option[String],
-            properties: Option[Map[String, String]]
+            properties: Option[spray.json.JsValue]
         )
         case class Res$Company$Update(
             status: String
@@ -1031,8 +1069,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "postalCode" → req.postalCode
                 )
 
-                checkProperties(req.properties)
-
+                val propsJs = extractJson(req.properties, "properties")
                 val admUsr = authenticateAsAdmin(req.acsTok)
 
                 NCCompanyManager.updateCompany(
@@ -1044,7 +1081,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.city,
                     req.address,
                     req.postalCode,
-                    req.properties,
+                    propsJs,
                     span
                 )
 
@@ -1314,7 +1351,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             lastName: String,
             avatarUrl: Option[String],
             isAdmin: Boolean,
-            properties: Option[Map[String, String]],
+            properties: Option[spray.json.JsValue],
             usrExtId: Option[String]
         )
         case class Res$User$Add(
@@ -1337,8 +1374,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "usrExtId" → req.usrExtId
                 )
 
-                checkProperties(req.properties)
-
+                val propsJs = extractJson(req.properties, "properties")
                 val admUsr = authenticateAsAdmin(req.acsTok)
 
                 val id = NCUserManager.addUser(
@@ -1349,7 +1385,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.lastName,
                     req.avatarUrl,
                     req.isAdmin,
-                    req.properties,
+                    propsJs,
                     req.usrExtId,
                     span
                 )
@@ -1375,7 +1411,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             firstName: String,
             lastName: String,
             avatarUrl: Option[String],
-            properties: Option[Map[String, String]]
+            properties: Option[spray.json.JsValue]
         )
         case class Res$User$Update(
             status: String
@@ -1393,8 +1429,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     "avatarUrl" → req.avatarUrl
                 )
 
-                checkProperties(req.properties)
-
+                val propsJs = extractJson(req.properties, "properties")
                 val acsUsr = authenticate(req.acsTok)
 
                 NCUserManager.updateUser(
@@ -1402,7 +1437,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     req.firstName,
                     req.lastName,
                     req.avatarUrl,
-                    req.properties,
+                    propsJs,
                     span
                 )
 
@@ -1454,7 +1489,6 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
 
                     NCUserManager.
                         getAllUsers(acsUsr.companyId, span).
-                        keys.
                         filter(_.id != acsUsr.id).
                         map(_.id).
                         foreach(delete)
@@ -1575,7 +1609,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             avatarUrl: Option[String],
             isAdmin: Boolean,
             companyId: Long,
-            properties: Option[Map[String, String]]
+            properties: Option[JavaMeta]
         )
         case class Res$User$All(
             status: String,
@@ -1583,8 +1617,6 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
         )
 
         implicit val reqFmt: RootJsonFormat[Req$User$All] = 
jsonFormat1(Req$User$All)
-        implicit val usrFmt: RootJsonFormat[ResUser_User$All] = 
jsonFormat9(ResUser_User$All)
-        implicit val resFmt: RootJsonFormat[Res$User$All] = 
jsonFormat2(Res$User$All)
 
         entity(as[Req$User$All]) { req ⇒
             startScopedSpan("user$All", "acsTok" → req.acsTok) { span ⇒
@@ -1592,23 +1624,23 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
 
                 val admUSr = authenticateAsAdmin(req.acsTok)
 
-                val usrLst =
-                    NCUserManager.getAllUsers(admUSr.companyId, span).map { 
case (u, props) ⇒
-                        ResUser_User$All(
-                            u.id,
-                            u.email,
-                            u.extId,
-                            u.firstName,
-                            u.lastName,
-                            u.avatarUrl,
-                            u.isAdmin,
-                            u.companyId,
-                            if (props.isEmpty) None else Some(props.map(p ⇒ 
p.property → p.value).toMap)
+                completeJs {
+                    Res$User$All(
+                        API_OK,
+                        NCUserManager.getAllUsers(admUSr.companyId, 
span).map(u ⇒
+                            ResUser_User$All(
+                                u.id,
+                                u.email,
+                                u.extId,
+                                u.firstName,
+                                u.lastName,
+                                u.avatarUrl,
+                                u.isAdmin,
+                                u.companyId,
+                                unzipProperties(u.propertiesGzip)
+                            )
                         )
-                    }.toSeq
-
-                complete {
-                    Res$User$All(API_OK, usrLst)
+                    )
                 }
             }
         }
@@ -1634,11 +1666,10 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
             lastName: Option[String],
             avatarUrl: Option[String],
             isAdmin: Boolean,
-            properties: Option[Map[String, String]]
+            properties: Option[JavaMeta]
         )
 
         implicit val reqFmt: RootJsonFormat[Req$User$Get] = 
jsonFormat3(Req$User$Get)
-        implicit val resFmt: RootJsonFormat[Res$User$Get] = 
jsonFormat9(Res$User$Get)
 
         entity(as[Req$User$Get]) { req ⇒
             startScopedSpan(
@@ -1653,9 +1684,8 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     throw AdminRequired(acsUsr.email.get)
 
                 val usr = NCUserManager.getUserById(usrId, 
span).getOrElse(throw new NCE(s"User not found: $usrId"))
-                val props = NCUserManager.getUserProperties(usrId, span)
 
-                complete {
+                completeJs {
                     Res$User$Get(API_OK,
                         usr.id,
                         usr.email,
@@ -1664,7 +1694,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                         usr.lastName,
                         usr.avatarUrl,
                         usr.isAdmin,
-                        if (props.isEmpty) None else Some(props.map(p ⇒ 
p.property → p.value).toMap)
+                        unzipProperties(usr.propertiesGzip)
                     )
                 }
             }
@@ -1771,7 +1801,7 @@ class NCBasicRestApi extends NCRestApi with LazyLogging 
with NCOpenCensusTrace w
                     status = statusCode,
                     entity = HttpEntity(
                         ContentTypes.`application/json`,
-                        GSON.toJson(Map("code" → errCode, "msg" → 
errMsg).asJava)
+                        toJs(Map("code" → errCode, "msg" → errMsg).asJava)
                     )
                 )
             )
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
index 00fe81b..50ef05d 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sql/NCSqlManager.scala
@@ -17,8 +17,10 @@
 
 package org.apache.nlpcraft.server.sql
 
+import com.fasterxml.jackson.core.JsonProcessingException
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
 import io.opencensus.trace.Span
-import org.apache.ignite.IgniteAtomicSequence
 import org.apache.nlpcraft.common.config.NCConfigurable
 import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.server.apicodes.NCApiStatusCode._
@@ -27,7 +29,6 @@ import org.apache.nlpcraft.server.mdo._
 import org.apache.nlpcraft.server.sql.NCSql.Implicits._
 
 import java.sql.Timestamp
-import scala.util.control.Exception.catching
 
 /**
   * Provides basic CRUD and often used operations on RDBMS.
@@ -35,7 +36,7 @@ import scala.util.control.Exception.catching
   */
 object NCSqlManager extends NCService with NCIgniteInstance {
     private final val DB_TABLES = Seq(
-        "nc_company", "nc_company_property", "nc_user", "nc_user_property", 
"passwd_pool", "proc_log", "feedback"
+        "nc_company", "nc_user", "passwd_pool", "proc_log", "feedback"
     )
     private final val CACHE_2_CLEAR = Seq(
         "user-token-signin-cache",
@@ -45,13 +46,45 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
         "stanford-cache",
         "opennlp-cache"
     )
-    
+
+    private final val JS_MAPPER = new ObjectMapper()
+
+    JS_MAPPER.registerModule(DefaultScalaModule)
+
     private object Config extends NCConfigurable {
         def init: Boolean = 
getBoolOpt("nlpcraft.server.database.igniteDbInitialize").getOrElse(false)
     }
 
-    @volatile private var usersPropsSeq: IgniteAtomicSequence = _
-    @volatile private var compPropsSeq: IgniteAtomicSequence = _
+    /**
+      *
+      * @param opt
+      */
+    @throws[NCE]
+    private def gzip(opt: Option[String]): String =
+        opt match {
+            case Some(s) ⇒ U.compress(s)
+            case None ⇒ null
+        }
+
+    /**
+      *
+      * @param nullable
+      */
+    @throws[NCE]
+    private def gzip(nullable: String): String = if (nullable != null) 
U.compress(nullable) else null
+
+    /**
+      *
+      * @param m
+      */
+    @throws[NCE]
+    private def gzip(m: JavaMeta): String = {
+        try
+            if (m != null) U.compress(JS_MAPPER.writeValueAsString(m)) else 
null
+        catch {
+            case e: JsonProcessingException ⇒ throw new NCE(s"JSON 
serialization error for: $m", e)
+        }
+    }
 
     /**
      *
@@ -68,11 +101,6 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
         if (NCSql.isIgniteDb)
             prepareIgniteSchema()
      
-        catching(wrapIE) {
-            usersPropsSeq = NCSql.mkSeq(ignite, "usersPropsSeq", 
"nc_user_property", "id")
-            compPropsSeq = NCSql.mkSeq(ignite, "companiesPropsSeq", 
"nc_company_property", "id")
-        }
-     
         ackStarted()
     }
 
@@ -163,7 +191,6 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
     @throws[NCE]
     def deleteUser(id: Long, parent: Span): Int =
         startScopedSpan("deleteUser", parent, "usrId" → id) { _ ⇒
-            NCSql.delete("DELETE FROM nc_user_property WHERE user_id = ?", id)
             NCSql.delete("DELETE FROM nc_user WHERE id = ?", id)
         }
 
@@ -176,7 +203,6 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
     @throws[NCE]
     def deleteCompany(id: Long, parent: Span): Int =
         startScopedSpan("deleteCompany", parent, "compId" → id) { _ ⇒
-            NCSql.delete("DELETE FROM nc_user_property WHERE user_id IN 
(SELECT id FROM nc_user WHERE company_id = ?)", id)
             NCSql.delete("DELETE FROM nc_user WHERE company_id = ?", id)
             NCSql.delete("DELETE FROM nc_company WHERE id = ?", id)
         }
@@ -197,33 +223,28 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         firstName: String,
         lastName: String,
         avatarUrl: Option[String],
-        propsOpt: Option[Map[String, String]],
+        propsOpt: Option[String],
         parent: Span
     ): Int =
-        startScopedSpan("updateUser", parent, "usrId" → id) { span ⇒
-            val n =
-                NCSql.update(
-                    s"""
-                       |UPDATE nc_user
-                       |SET
-                       |    first_name = ?,
-                       |    last_name = ?,
-                       |    avatar_url = ?,
-                       |    last_modified_on = ?
-                       |WHERE id = ?
-                        """.stripMargin,
-                    firstName,
-                    lastName,
-                    avatarUrl.orNull,
-                    U.nowUtcTs(),
-                    id
-                )
-         
-            NCSql.delete("DELETE FROM nc_user_property WHERE user_id = ?", id)
-         
-            addUserProperties(id, propsOpt, span)
-         
-            n
+        startScopedSpan("updateUser", parent, "usrId" → id) { _ ⇒
+            NCSql.update(
+                s"""
+                   |UPDATE nc_user
+                   |SET
+                   |    first_name = ?,
+                   |    last_name = ?,
+                   |    avatar_url = ?,
+                   |    properties_gzip = ?,
+                   |    last_modified_on = ?
+                   |WHERE id = ?
+                    """.stripMargin,
+                firstName,
+                lastName,
+                avatarUrl.orNull,
+                gzip(propsOpt),
+                U.nowUtcTs(),
+                id
+            )
         }
 
     /**
@@ -246,97 +267,32 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         firstName: String,
         lastName: String,
         avatarUrl: Option[String],
-        propsOpt: Option[Map[String, String]],
+        propsOpt: Option[String],
         parent: Span
     ): Int =
         startScopedSpan("updateUser", parent, "usrId" → id) { span ⇒
-            val n =
-                NCSql.update(
-                    s"""
-                       |UPDATE nc_user
-                       |SET
-                       |    email = ?,
-                       |    passwd_salt = ?,
-                       |    first_name = ?,
-                       |    last_name = ?,
-                       |    avatar_url = ?,
-                       |    last_modified_on = ?
-                       |WHERE id = ?
-                        """.stripMargin,
-                    email,
-                    passwdSalt,
-                    firstName,
-                    lastName,
-                    avatarUrl.orNull,
-                    U.nowUtcTs(),
-                    id
-                )
-
-            NCSql.delete("DELETE FROM nc_user_property WHERE user_id = ?", id)
-
-            addUserProperties(id, propsOpt, span)
-
-            n
-        }
-
-    /**
-      *
-      * @param id
-      * @param propsOpt
-      * @param parent Optional parent span.
-      */
-    private def addUserProperties(id: Long, propsOpt: Option[Map[String, 
String]], parent: Span): Unit =
-        startScopedSpan("addUserProperties", parent, "usrId" → id) { _ ⇒
-            propsOpt match {
-                case Some(props) ⇒
-                    val now = U.nowUtcTs()
-         
-                    props.foreach { case (k, v) ⇒
-                        NCSql.insert(
-                            s"""
-                               |INSERT INTO nc_user_property (id, user_id, 
property, value, created_on, last_modified_on)
-                               |VALUES(?, ?, ?, ?, ?, ?)
-                            """.stripMargin,
-                            usersPropsSeq.getAndIncrement(),
-                            id,
-                            k,
-                            v,
-                            now,
-                            now
-                        )
-                    }
-                case None ⇒ // No-op.
-            }
-        }
-
-    /**
-      *
-      * @param id
-      * @param propsOpt
-      * @param parent Optional parent span.
-      */
-    private def addCompanyProperties(id: Long, propsOpt: Option[Map[String, 
String]], parent: Span): Unit =
-        startScopedSpan("addCompanyProperties", parent, "companyId" → id) { _ ⇒
-            propsOpt match {
-                case Some(props) ⇒
-                    val now = U.nowUtcTs()
-
-                    props.foreach { case (k, v) ⇒
-                        NCSql.insert(
-                            s"""
-                               |INSERT INTO nc_company_property (id, 
company_id, property, value, created_on, last_modified_on)
-                               |VALUES(?, ?, ?, ?, ?, ?)
-                            """.stripMargin,
-                            compPropsSeq.getAndIncrement(),
-                            id,
-                            k,
-                            v,
-                            now,
-                            now
-                        )
-                    }
-                case None ⇒ // No-op.
-            }
+            NCSql.update(
+                s"""
+                   |UPDATE nc_user
+                   |SET
+                   |    email = ?,
+                   |    passwd_salt = ?,
+                   |    first_name = ?,
+                   |    last_name = ?,
+                   |    avatar_url = ?,
+                   |    properties_gzip = ?,
+                   |    last_modified_on = ?
+                   |WHERE id = ?
+                    """.stripMargin,
+                email,
+                passwdSalt,
+                firstName,
+                lastName,
+                avatarUrl.orNull,
+                gzip(propsOpt),
+                U.nowUtcTs(),
+                id
+            )
         }
 
     /**
@@ -387,11 +343,11 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
-        propsOpt: Option[Map[String, String]],
+        propsOpt: Option[String],
         parent: Span
     ): Int =
         startScopedSpan("updateCompany", parent, "compId" → id) { _ ⇒
-            val res = NCSql.update(
+            NCSql.update(
                 s"""
                    |UPDATE nc_company
                    |SET
@@ -402,6 +358,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
                    |    city = ?,
                    |    address = ?,
                    |    postal_code = ?,
+                   |    properties_gzip = ?,
                    |    last_modified_on = ?
                    |WHERE id = ?
                     """.stripMargin,
@@ -412,15 +369,10 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                 city.orNull,
                 address.orNull,
                 postalCode.orNull,
+                gzip(propsOpt),
                 U.nowUtcTs(),
                 id
             )
-
-            NCSql.delete("DELETE FROM nc_company_property WHERE company_id = 
?", id)
-
-            addCompanyProperties(id, propsOpt, parent)
-
-            res
         }
 
     /**
@@ -530,34 +482,6 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         }
 
     /**
-      * Gets user properties for given ID.
-      *
-      * @param id User ID.
-      * @param parent Optional parent span.
-      * @return User properties.
-      *
-      */
-    @throws[NCE]
-    def getUserProperties(id: Long, parent: Span): Seq[NCUserPropertyMdo] =
-        startScopedSpan("getUserProperties", parent, "usrId" → id) { _ ⇒
-            NCSql.select[NCUserPropertyMdo]("SELECT * FROM nc_user_property 
WHERE user_id = ?", id)
-        }
-
-    /**
-      * Gets company properties for given ID.
-      *
-      * @param id User ID.
-      * @param parent Optional parent span.
-      * @return Company properties.
-      *
-      */
-    @throws[NCE]
-    def getCompanyProperties(id: Long, parent: Span): 
Seq[NCCompanyPropertyMdo] =
-        startScopedSpan("getCompanyProperties", parent, "usrId" → id) { _ ⇒
-            NCSql.select[NCCompanyPropertyMdo]("SELECT * FROM 
nc_company_property WHERE company_id = ?", id)
-        }
-
-    /**
       * Gets user properties for given external ID.
       *
       * @param companyId Company ID.
@@ -593,15 +517,9 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
       * @param parent Optional parent span.
       */
     @throws[NCE]
-    def getAllUsers(compId: Long, parent: Span): Map[NCUserMdo, 
Seq[NCUserPropertyMdo]] =
+    def getAllUsers(compId: Long, parent: Span): Seq[NCUserMdo] =
         startScopedSpan("getAllUsers", parent, "compId" → compId) { _ ⇒
-            val props = NCSql.select[NCUserPropertyMdo](
-                "SELECT * FROM nc_user_property WHERE user_id IN (SELECT id 
FROM nc_user WHERE company_id = ?)",
-                compId
-            ).groupBy(_.userId)
-         
-            NCSql.select[NCUserMdo]("SELECT * FROM nc_user WHERE company_id = 
?", compId).
-                map(p ⇒ p → props.getOrElse(p.id, Nil)).toMap
+            NCSql.select[NCUserMdo]("SELECT * FROM nc_user WHERE company_id = 
?", compId)
         }
 
     /**
@@ -643,7 +561,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
         city: Option[String],
         address: Option[String],
         postalCode: Option[String],
-        propsOpt: Option[Map[String, String]],
+        propsOpt: Option[String],
         parent: Span
     ): Unit =
         startScopedSpan("addCompany", parent, "compId" → id, "name" → name, 
"tkn" → tkn) { _ ⇒
@@ -663,10 +581,11 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                   |    postal_code,
                   |    auth_token,
                   |    auth_token_hash,
+                  |    properties_gzip,
                   |    created_on,
                   |    last_modified_on
                   |)
-                  | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+                  | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
               """.stripMargin,
                 id,
                 name,
@@ -678,11 +597,10 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                 postalCode.orNull,
                 tkn,
                 U.mkSha256Hash(tkn),
+                gzip(propsOpt),
                 now,
                 now
             )
-
-            addCompanyProperties(id, propsOpt, parent)
         }
 
     /**
@@ -711,7 +629,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
         avatarUrl: Option[String],
         passwdSalt: Option[String],
         isAdmin: Boolean,
-        propsOpt: Option[Map[String, String]],
+        propsOpt: Option[String],
         parent: Span
     ): Unit = {
         require(usrExtId.isDefined ^ email.isDefined)
@@ -739,11 +657,12 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                   |    email,
                   |    passwd_salt,
                   |    avatar_url,
+                  |    properties_gzip,
                   |    is_admin,
                   |    created_on,
                   |    last_modified_on
                   | )
-                  | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+                  | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                 """.stripMargin,
                 id,
                 compId,
@@ -753,12 +672,11 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
                 email.orNull,
                 passwdSalt.orNull,
                 avatarUrl.orNull,
+                gzip(propsOpt),
                 isAdmin,
                 now,
                 now
             )
-
-            addUserProperties(id, propsOpt, span)
         }
     }
     
@@ -852,6 +770,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
       * @param errMsg
       * @param resType
       * @param resBody
+      * @param resMeta
       * @param intentId
       * @param tstamp
       * @param parent Optional parent span.
@@ -862,9 +781,11 @@ object NCSqlManager extends NCService with 
NCIgniteInstance {
         errMsg: String,
         resType: String,
         resBody: String,
+        resMeta: JavaMeta,
         intentId: String,
         tstamp: Timestamp,
-        parent: Span): Unit =
+        parent: Span
+    ): Unit =
         startScopedSpan("updateReadyProcessingLog", parent, "srvReqId" → 
srvReqId) { _ ⇒
             NCSql.update(
                 """
@@ -874,6 +795,7 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
                   |    error = ?,
                   |    res_type = ?,
                   |    res_body_gzip = ?,
+                  |    res_meta_gzip = ?,
                   |    intent_id = ?,
                   |    resp_tstamp = ?
                   |WHERE srv_req_id = ?
@@ -881,7 +803,8 @@ object NCSqlManager extends NCService with NCIgniteInstance 
{
                 QRY_READY.toString,
                 errMsg,
                 resType,
-                if (resBody == null) null else U.compress(resBody),
+                gzip(resBody),
+                gzip(resMeta),
                 intentId,
                 tstamp,
                 srvReqId
diff --git 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
index 4471b31..7107085 100644
--- 
a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
+++ 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/user/NCUserManager.scala
@@ -27,7 +27,7 @@ import org.apache.nlpcraft.common.config.NCConfigurable
 import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.server.ignite.NCIgniteHelpers._
 import org.apache.nlpcraft.server.ignite.NCIgniteInstance
-import org.apache.nlpcraft.server.mdo.{NCUserMdo, NCUserPropertyMdo}
+import org.apache.nlpcraft.server.mdo.NCUserMdo
 import org.apache.nlpcraft.server.sql.{NCSql, NCSqlManager}
 import org.apache.nlpcraft.server.tx.NCTxManager
 
@@ -190,7 +190,7 @@ object NCUserManager extends NCService with 
NCIgniteInstance {
       * @param parent Optional parent span.
       */
     @throws[NCE]
-    def getAllUsers(compId: Long, parent: Span = null): Map[NCUserMdo, 
Seq[NCUserPropertyMdo]] =
+    def getAllUsers(compId: Long, parent: Span = null): Seq[NCUserMdo] =
         NCSql.sql {
             NCSqlManager.getAllUsers(compId, parent)
         }
@@ -313,20 +313,6 @@ object NCUserManager extends NCService with 
NCIgniteInstance {
         }
 
     /**
-      * Gets user properties for given user ID.
-      *
-      * @param id User ID.
-      * @param parent Optional parent span.
-      */
-    @throws[NCE]
-    def getUserProperties(id: Long, parent: Span = null): 
Seq[NCUserPropertyMdo] =
-        startScopedSpan("getUserProperties", parent, "usrId" → id) { span ⇒
-            NCSql.sql {
-                NCSqlManager.getUserProperties(id, span)
-            }
-        }
-
-    /**
       *
       * @param email User email (as username).
       * @param passwd User password.
@@ -391,7 +377,7 @@ object NCUserManager extends NCService with 
NCIgniteInstance {
         firstName: String,
         lastName: String,
         avatarUrl: Option[String],
-        props: Option[Map[String, String]],
+        props: Option[String],
         parent: Span = null
     ): Unit =
         startScopedSpan("updateUser", parent, "usrId" → id) { span ⇒
@@ -488,7 +474,7 @@ object NCUserManager extends NCService with 
NCIgniteInstance {
         lastName: String,
         avatarUrl: Option[String],
         isAdmin: Boolean,
-        props: Option[Map[String, String]],
+        props: Option[String],
         usrExtIdOpt: Option[String],
         parent: Span = null
     ): Long =
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/utils/README.md 
b/nlpcraft/src/main/scala/org/apache/nlpcraft/utils/README.md
index 36ad1e9..69334bf 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/utils/README.md
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/utils/README.md
@@ -15,8 +15,6 @@
  limitations under the License.
 -->
 
-TODO: add here that we need it for tests (examples).
-
 <img src="https://nlpcraft.apache.org/images/nlpcraft_logo_black.gif"; 
height="80px">
 <br>
 
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaResultSpec.scala 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaResultSpec.scala
new file mode 100644
index 0000000..8aec5cb
--- /dev/null
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/meta/NCMetaResultSpec.scala
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.model.meta
+
+import org.apache.nlpcraft.model.`abstract`.NCAbstractTokensModel
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+import org.apache.nlpcraft.{NCTestElement, NCTestEnvironment}
+import org.junit.jupiter.api.Test
+
+import java.util
+
+/**
+  * Test model.
+  */
+object NCMetaResultSpecModel {
+    final val K1 = "k1"
+    final val K2 = "k2"
+    final val K3 = "k3"
+
+    final val V1 = "v1"
+    final val V2 = 2.2.asInstanceOf[AnyRef]
+    final val V3 = new util.HashMap[String, AnyRef]()
+
+    V3.put(K1, V1)
+    V3.put(K2, V2)
+}
+
+import org.apache.nlpcraft.model.meta.NCMetaResultSpecModel._
+
+class NCMetaResultSpecModel extends NCAbstractTokensModel {
+    override def getElements: util.Set[NCElement] = Set(NCTestElement("a"))
+
+    @NCIntent("intent=i term(t)={tok_id() == 'a'}")
+    def onIntent(): NCResult = {
+        val res = NCResult.text("OK")
+
+        res.getMetadata.put(K1, V1)
+        res.getMetadata.put(K2, V2)
+        res.getMetadata.put(K3, V3)
+
+        res
+    }
+}
+
+@NCTestEnvironment(model = classOf[NCMetaResultSpecModel], startClient = true)
+class NCMetaResultSpec extends NCMetaSpecAdapter {
+    @Test
+    def test(): Unit = {
+        val res = getClient.ask("a")
+
+        require(res.isOk)
+        require(res.getResultMeta.isPresent)
+
+        val meta = res.getResultMeta.get()
+
+        println(s"Meta received: $meta")
+
+        require(meta.get(K1) == V1)
+        require(meta.get(K2) == V2)
+        require(meta.get(K3) == V3)
+    }
+}
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestAskSpec.scala 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestAskSpec.scala
index 56fc5ba..8013c0a 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestAskSpec.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestAskSpec.scala
@@ -152,4 +152,20 @@ class NCRestAskSpec extends NCRestSpec {
 
         post("cancel")()
     }
+
+    @Test
+    def testSyncMeta(): Unit = {
+        post(
+            "ask/sync",
+            "txt" → "meta",
+            "mdlId" → "rest.test.model"
+        )(
+            ("$.state.status", (status: String) ⇒ assertEquals("QRY_READY", 
status)),
+            ("$.state.resMeta", (meta: java.util.Map[String, Object]) ⇒ {
+                import RestTestModel._
+
+                assertEquals(Map(K1 → V1, K2 → V2, K3 → V3).asJava, meta)
+            })
+        )
+    }
 }
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
index 98bc498..098b582 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
@@ -48,6 +48,9 @@ object NCRestSpec {
             val code = resp.getStatusLine.getStatusCode
             val js = mkJs(code, resp.getEntity)
 
+            if (js == null)
+                throw new RuntimeException(s"Unexpected response [code=$code, 
response=$js]")
+
             code match {
                 case 200 ⇒ GSON.fromJson(js, TYPE_RESP)
 
diff --git 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
index 115caa5..621be18 100644
--- 
a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
+++ 
b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
@@ -18,11 +18,29 @@
 package org.apache.nlpcraft.server.rest
 
 import org.apache.nlpcraft.NCTestElement
-import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentMatch, 
NCIntentRef, NCIntentSample, NCModelAdapter, NCResult}
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentSample, 
NCModelAdapter, NCResult}
 
 import java.util
 
 /**
+  * REST test model helper.
+  */
+object RestTestModel {
+    final val K1 = "k1"
+    final val K2 = "k2"
+    final val K3 = "k3"
+
+    final val V1 = "v1"
+    final val V2 = 2.2.asInstanceOf[AnyRef]
+    final val V3 = new util.HashMap[String, AnyRef]()
+
+    V3.put(K1, V1)
+    V3.put(K2, V2)
+}
+
+import RestTestModel._
+
+/**
   * REST test model.
   */
 class RestTestModel extends NCModelAdapter("rest.test.model", "REST test 
model", "1.0.0") {
@@ -30,14 +48,27 @@ class RestTestModel extends 
NCModelAdapter("rest.test.model", "REST test model",
         Set(
             NCTestElement("a"),
             NCTestElement("b"),
-            NCTestElement("x", "cat")
+            NCTestElement("x", "cat"),
+            NCTestElement("meta")
         )
 
     @NCIntent("intent=onA term(t)={tok_id() == 'a'}")
     @NCIntentSample(Array("My A"))
-    private def a(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
+    private def a(): NCResult = NCResult.text("OK")
 
     @NCIntent("intent=onB term(t)={tok_id() == 'b'}")
     @NCIntentSample(Array("My B"))
-    private def b(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
+    private def b(): NCResult = NCResult.text("OK")
+
+    @NCIntent("intent=onMeta term(t)={tok_id() == 'meta'}")
+    @NCIntentSample(Array("meta"))
+    private def meta(): NCResult = {
+        val res = NCResult.text("OK")
+
+        res.getMetadata.put(K1, V1)
+        res.getMetadata.put(K2, V2)
+        res.getMetadata.put(K3, V3)
+
+        res
+    }
 }
diff --git a/openapi/nlpcraft_swagger.yml b/openapi/nlpcraft_swagger.yml
index f8b0114..8b239c3 100644
--- a/openapi/nlpcraft_swagger.yml
+++ b/openapi/nlpcraft_swagger.yml
@@ -352,6 +352,11 @@ paths:
                         Optional body (string or JSON object) of the result if 
returned by model
                         (provided only if status is <code>QRY_READY</code> and 
processing was
                         not rejected or terminated due to an exception).
+                    resMeta:
+                      type: object
+                      description: >-
+                        Optional result metadata as JSON object (provided only 
if status is
+                        <code>QRY_READY</code> and processing was not rejected 
or terminated due to an exception).
                     error:
                       type: string
                       description: >-
@@ -650,6 +655,10 @@ paths:
                       Optional body (string or JSON object) of the result if 
returned by model
                       (provided only if status is <code>QRY_READY</code> and 
processing was
                       not rejected or terminated due to an exception).
+                  resMeta:
+                    type: object
+                    description: >-
+                      Optional meta JSON object. TODO:
                   error:
                     type: string
                     description: >-
@@ -871,7 +880,7 @@ paths:
                 maxLength: 512000
               properties:
                 type: object
-                description: <em>Optional.</em> Additional user properties.
+                description: <em>Optional.</em> Additional user properties 
with maximum JSON length of 512000 bytes.
                 additionalProperties:
                   type: string
       responses:
@@ -1064,7 +1073,7 @@ paths:
                 description: Admin flag.
               properties:
                 type: object
-                description: <em>Optional.</em> Additional user properties.
+                description: <em>Optional.</em> Additional user properties 
with maximum JSON length of 512000 bytes.
                 additionalProperties:
                   type: string
               usrExtId:
@@ -1240,7 +1249,7 @@ paths:
                 maxLength: 512000
               properties:
                 type: object
-                description: <em>Optional.</em> Additional company properties.
+                description: <em>Optional.</em> Additional company properties 
with maximum JSON length of 512000 bytes.
                 additionalProperties:
                   type: string
       responses:
@@ -1323,7 +1332,7 @@ paths:
                 maxLength: 512
               properties:
                 type: object
-                description: <em>Optional.</em> Additional company properties.
+                description: <em>Optional.</em> Additional company properties 
with maximum JSON length of 512000 bytes.
                 additionalProperties:
                   type: string
       responses:
diff --git a/sql/mysql/drop_schema.sql b/sql/mysql/drop_schema.sql
index fba01aa..e1ead27 100644
--- a/sql/mysql/drop_schema.sql
+++ b/sql/mysql/drop_schema.sql
@@ -18,9 +18,7 @@
 USE nlpcraft;
 
 DROP TABLE IF EXISTS proc_log;
-DROP TABLE IF EXISTS nc_user_property;
 DROP TABLE IF EXISTS nc_user;
-DROP TABLE IF EXISTS nc_company_property;
 DROP TABLE IF EXISTS nc_company;
 DROP TABLE IF EXISTS passwd_pool;
 DROP TABLE IF EXISTS feedback;
diff --git a/sql/mysql/schema.sql b/sql/mysql/schema.sql
index cac0bf8..9022ab6 100644
--- a/sql/mysql/schema.sql
+++ b/sql/mysql/schema.sql
@@ -39,6 +39,7 @@ CREATE TABLE nc_company (
     postal_code VARCHAR(32),
     auth_token VARCHAR(64) NOT NULL,
     auth_token_hash VARCHAR(64) NOT NULL,
+    properties_gzip TEXT NULL,
     created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3)
 );
@@ -48,19 +49,6 @@ CREATE UNIQUE INDEX nc_company_idx_2 ON 
nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
 --
--- Company properties table.
---
-CREATE TABLE nc_company_property (
-    id SERIAL PRIMARY KEY,
-    company_id BIGINT UNSIGNED NOT NULL,
-    property VARCHAR(64) NOT NULL,
-    value VARCHAR(512) NULL,
-    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    FOREIGN KEY (company_id) REFERENCES nc_company(id)
-);
-
---
 -- User table.
 --
 CREATE TABLE nc_user (
@@ -73,6 +61,7 @@ CREATE TABLE nc_user (
     last_name VARCHAR(64) NULL,
     is_admin BOOLEAN NOT NULL, -- Whether or not created with admin token.
     passwd_salt VARCHAR(64) NULL,
+    properties_gzip TEXT NULL,
     created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     FOREIGN KEY (company_id) REFERENCES nc_company(id)
@@ -82,19 +71,6 @@ CREATE UNIQUE INDEX nc_user_idx_1 ON nc_user(email);
 CREATE UNIQUE INDEX nc_user_idx_2 ON nc_user(company_id, ext_id);
 
 --
--- User properties table.
---
-CREATE TABLE nc_user_property (
-    id SERIAL PRIMARY KEY,
-    user_id BIGINT UNSIGNED NOT NULL,
-    property VARCHAR(64) NOT NULL,
-    value VARCHAR(512) NULL,
-    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    FOREIGN KEY (user_id) REFERENCES nc_user(id)
-);
-
---
 -- Pool of password hashes.
 --
 CREATE TABLE passwd_pool (
@@ -122,6 +98,7 @@ CREATE TABLE proc_log (
     -- Result parts.
     res_type VARCHAR(32) NULL,
     res_body_gzip TEXT NULL, -- GZIP-ed result body.
+    res_body_meta TEXT NULL, -- GZIP-ed result meta.
     intent_id VARCHAR(256) NULL,
     error TEXT NULL,
     -- Probe information for this request.
diff --git a/sql/oracle/drop_schema.sql b/sql/oracle/drop_schema.sql
index 652bdb8..78bd027 100644
--- a/sql/oracle/drop_schema.sql
+++ b/sql/oracle/drop_schema.sql
@@ -24,14 +24,6 @@ EXCEPTION WHEN OTHERS THEN
 END;
 
 BEGIN
-  EXECUTE IMMEDIATE 'DROP TABLE nc_user_property';
-EXCEPTION WHEN OTHERS THEN
-  IF SQLCODE != -942 THEN
-    RAISE;
-  END IF;
-END;
-
-BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE nc_user';
 EXCEPTION WHEN OTHERS THEN
   IF SQLCODE != -942 THEN
@@ -40,14 +32,6 @@ EXCEPTION WHEN OTHERS THEN
 END;
 
 BEGIN
-  EXECUTE IMMEDIATE 'DROP TABLE nc_company_property';
-EXCEPTION WHEN OTHERS THEN
-  IF SQLCODE != -942 THEN
-    RAISE;
-END IF;
-END;
-
-BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE nc_company';
 EXCEPTION WHEN OTHERS THEN
   IF SQLCODE != -942 THEN
diff --git a/sql/oracle/schema.sql b/sql/oracle/schema.sql
index 1ad495c..da243b9 100644
--- a/sql/oracle/schema.sql
+++ b/sql/oracle/schema.sql
@@ -37,6 +37,7 @@ CREATE TABLE nc_company (
     postal_code VARCHAR2(32),
     auth_token VARCHAR2(64) NOT NULL,
     auth_token_hash VARCHAR2(64) NOT NULL,
+    properties_gzip CLOB NULL,
     created_on DATE DEFAULT sysdate NOT NULL,
     last_modified_on DATE DEFAULT sysdate NOT NULL
 );
@@ -46,21 +47,6 @@ CREATE UNIQUE INDEX nc_company_idx_2 ON 
nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
 --
--- Company properties table.
---
-CREATE TABLE nc_company_property (
-    id NUMBER PRIMARY KEY,
-    company_id NUMBER NOT NULL,
-    property VARCHAR2(64) NOT NULL,
-    value VARCHAR2(512) NULL,
-    created_on DATE DEFAULT sysdate NOT NULL,
-    last_modified_on DATE DEFAULT sysdate NOT NULL,
-    CONSTRAINT fk_company_id_company_property FOREIGN KEY (company_id) 
REFERENCES nc_company(id)
-);
-
-CREATE INDEX nc_company_property_idx1 ON nc_company_property(company_id);
-
---
 -- User table.
 --
 CREATE TABLE nc_user (
@@ -73,6 +59,7 @@ CREATE TABLE nc_user (
     last_name VARCHAR2(64) NULL,
     is_admin NUMBER(1) NOT NULL, -- Whether or not created with admin token.
     passwd_salt VARCHAR2(64) NULL,
+    properties_gzip CLOB NULL,
     created_on DATE DEFAULT sysdate NOT NULL,
     last_modified_on DATE DEFAULT sysdate NOT NULL,
     CONSTRAINT fk_company_id_user FOREIGN KEY (company_id) REFERENCES 
nc_company(id)
@@ -83,21 +70,6 @@ CREATE UNIQUE INDEX nc_user_idx_2 ON nc_user(company_id, 
ext_id);
 CREATE INDEX nc_user_idx_3 ON nc_user(company_id);
 
 --
--- User properties table.
---
-CREATE TABLE nc_user_property (
-    id NUMBER PRIMARY KEY,
-    user_id NUMBER NOT NULL,
-    property VARCHAR2(64) NOT NULL,
-    value VARCHAR2(512) NULL,
-    created_on DATE DEFAULT sysdate NOT NULL,
-    last_modified_on DATE DEFAULT sysdate NOT NULL,
-    CONSTRAINT fk_user_id_user_property FOREIGN KEY (user_id) REFERENCES 
nc_user(id)
-);
-
-CREATE INDEX nc_user_property_idx1 ON nc_user_property(user_id);
-
---
 -- Pool of password hashes.
 --
 CREATE TABLE passwd_pool (
@@ -125,6 +97,7 @@ CREATE TABLE proc_log (
     -- Result parts.
     res_type VARCHAR2(32) NULL,
     res_body_gzip CLOB NULL, -- GZIP-ed result body.
+    res_body_meta CLOB NULL, -- GZIP-ed result meta.
     intent_id VARCHAR2(256) NULL,
     error CLOB NULL,
     -- Probe information for this request.
diff --git a/sql/postgres/drop_schema.sql b/sql/postgres/drop_schema.sql
index b1ce0b9..f985bae 100644
--- a/sql/postgres/drop_schema.sql
+++ b/sql/postgres/drop_schema.sql
@@ -16,9 +16,7 @@
 --
 
 DROP TABLE IF EXISTS proc_log;
-DROP TABLE IF EXISTS nc_user_property;
 DROP TABLE IF EXISTS nc_user;
-DROP TABLE IF EXISTS nc_company_property;
 DROP TABLE IF EXISTS nc_company;
 DROP TABLE IF EXISTS passwd_pool;
 DROP TABLE IF EXISTS feedback;
\ No newline at end of file
diff --git a/sql/postgres/schema.sql b/sql/postgres/schema.sql
index 561418e..8329ed7 100644
--- a/sql/postgres/schema.sql
+++ b/sql/postgres/schema.sql
@@ -37,6 +37,7 @@ CREATE TABLE nc_company (
     postal_code VARCHAR(32),
     auth_token VARCHAR(64) NOT NULL,
     auth_token_hash VARCHAR(64) NOT NULL,
+    properties_gzip TEXT NULL,
     created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3)
 );
@@ -46,21 +47,6 @@ CREATE UNIQUE INDEX nc_company_idx_2 ON 
nc_company(auth_token);
 CREATE UNIQUE INDEX nc_company_idx_3 ON nc_company(auth_token_hash);
 
 --
--- Company properties table.
---
-CREATE TABLE nc_company_property (
-    id SERIAL PRIMARY KEY,
-    company_id BIGINT NOT NULL,
-    property VARCHAR(64) NOT NULL,
-    value VARCHAR(512) NULL,
-    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    FOREIGN KEY (company_id) REFERENCES nc_company(id)
-);
-
-CREATE INDEX nc_company_property_idx1 ON nc_company_property(company_id);
-
---
 --
 -- User table.
 --
@@ -74,6 +60,7 @@ CREATE TABLE nc_user (
     last_name VARCHAR(64) NULL,
     is_admin BOOL NOT NULL, -- Whether or not created with admin token.
     passwd_salt VARCHAR(64) NULL,
+    properties_gzip TEXT NULL,
     created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
     FOREIGN KEY (company_id) REFERENCES nc_company(id)
@@ -92,21 +79,6 @@ CREATE TABLE passwd_pool (
 );
 
 --
--- User properties table.
---
-CREATE TABLE nc_user_property (
-    id SERIAL PRIMARY KEY,
-    user_id BIGINT NOT NULL,
-    property VARCHAR(64) NOT NULL,
-    value VARCHAR(512) NULL,
-    created_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    last_modified_on TIMESTAMP(3) NOT NULL DEFAULT current_timestamp(3),
-    FOREIGN KEY (user_id) REFERENCES nc_user(id)
-);
-
-CREATE INDEX nc_user_property_idx1 ON nc_user_property(user_id);
-
---
 -- Processing log.
 --
 CREATE TABLE proc_log (
@@ -126,6 +98,7 @@ CREATE TABLE proc_log (
     -- Result parts.
     res_type VARCHAR(32) NULL,
     res_body_gzip TEXT NULL, -- GZIP-ed result body.
+    res_body_meta TEXT NULL, -- GZIP-ed result body.
     intent_id VARCHAR(256) NULL,
     error TEXT NULL,
     -- Probe information for this request.

Reply via email to