You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by se...@apache.org on 2021/09/09 19:13:27 UTC
[incubator-nlpcraft] branch master updated: NCElement 'greedy'
parameter added. Date model extended.
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 e6dfeba NCElement 'greedy' parameter added. Date model extended.
e6dfeba is described below
commit e6dfeba029d88fb3e8b1cd26b65de48f867a4069
Author: Sergey Kamov <sk...@gmail.com>
AuthorDate: Thu Sep 9 22:12:53 2021 +0300
NCElement 'greedy' parameter added.
Date model extended.
---
.../nlpcraft/common/nlp/numeric/NCNumeric.scala | 13 ++-
.../common/nlp/numeric/NCNumericManager.scala | 26 +++--
.../scala/org/apache/nlpcraft/model/NCElement.java | 5 +
.../apache/nlpcraft/model/NCModelFileAdapter.java | 10 ++
.../org/apache/nlpcraft/model/NCModelView.java | 9 ++
.../nlpcraft/model/impl/json/NCElementJson.java | 8 ++
.../nlpcraft/model/impl/json/NCModelJson.java | 7 ++
.../nlpcraft/probe/mgrs/cmd/NCCommandManager.scala | 6 ++
.../mgrs/nlp/enrichers/model/NCModelEnricher.scala | 42 ++++----
.../probe/mgrs/sentence/NCSentenceManager.scala | 27 +++--
.../nlp/enrichers/numeric/NCNumericEnricher.scala | 115 ++++++++++++---------
.../scala/org/apache/nlpcraft/NCTestElement.scala | 17 ++-
.../mgrs/nlp/enrichers/NCEnrichersTestBeans.scala | 18 +++-
.../model/NCEnricherNestedModelSpec2.scala | 3 +-
.../model/NCEnricherNestedModelSpec3.scala | 12 +--
...ec41.scala => NCEnricherNestedModelSpec4.scala} | 4 +-
.../model/NCEnricherNestedModelSpec5.scala | 6 +-
...pec5.scala => NCEnricherNestedModelSpec6.scala} | 39 +++----
.../enrichers/model/anyword/NCNestedAnySpec.scala | 102 ++++++++++++++++++
.../anyword/NCNestedTestModelsAnyAlphaNum.scala | 35 +++++++
.../anyword/NCNestedTestModelsAnyNotSpace.scala | 35 +++++++
.../model/anyword/NCNestedTestModelsAnyRegex.scala | 35 +++++++
.../adapters/NCNestedModelAnyAdapter.scala} | 37 +++----
.../anyword/adapters/NCNestedTestModelAny1.scala | 51 +++++++++
.../anyword/adapters/NCNestedTestModelAny2.scala | 52 ++++++++++
.../anyword/adapters/NCNestedTestModelAny3.scala | 49 +++++++++
.../anyword/adapters/NCNestedTestModelAny4.scala | 55 ++++++++++
.../anyword/adapters/NCNestedTestModelAny5.scala | 55 ++++++++++
.../anyword/adapters/NCNestedTestModelAny6.scala | 55 ++++++++++
.../anyword/adapters/NCNestedTestModelAny7.scala | 55 ++++++++++
.../enrichers/numeric/NCEnricherNumericSpec.scala | 75 ++++++++++++++
31 files changed, 917 insertions(+), 141 deletions(-)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala
index 75a3365..1de9d4b 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala
@@ -28,14 +28,21 @@ case class NCNumericUnit(name: String, unitType: String)
/**
*
+ * @param unit
+ * @param tokens
+ */
+case class NCNumericUnitData(unit: NCNumericUnit, tokens: Seq[NCNlpSentenceToken])
+
+/**
+ *
* @param tokens
* @param value
* @param isFractional
- * @param unit
+ * @param unitData
*/
case class NCNumeric(
tokens: Seq[NCNlpSentenceToken],
value: Double,
isFractional: Boolean,
- unit: Option[NCNumericUnit]
-)
+ unitData: Option[NCNumericUnitData]
+)
\ No newline at end of file
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumericManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumericManager.scala
index a428ab9..3d767dc 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumericManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumericManager.scala
@@ -85,14 +85,23 @@ object NCNumericManager extends NCService {
* @return
*/
private def mkSolidNumUnit(t: NCNlpSentenceToken): Option[NCNumeric] = {
- val s = t.origText
+ val s = t.normText
val num = s.takeWhile(_.isDigit)
val after = s.drop(num.length)
if (num.nonEmpty && after.nonEmpty) {
- def mkNumeric(u: NCNumericUnit): Option[NCNumeric] =
- Some(NCNumeric(Seq(t), java.lang.Double.valueOf(num), isFractional = isFractional(num), unit = Some(u)))
+ def mkNumeric(u: NCNumericUnit): Option[NCNumeric] = {
+ val toks = Seq(t)
+
+ Some(
+ NCNumeric(
+ toks,
+ java.lang.Double.valueOf(num),
+ isFractional = isFractional(num),
+ unitData = Some(NCNumericUnitData(u, toks)))
+ )
+ }
unitsOrigs.get(after) match {
case Some(u) => mkNumeric(u)
@@ -123,12 +132,14 @@ object NCNumericManager extends NCService {
senWords.indexOfSlice(dtWords) match {
case -1 => None
case idx =>
+ val toks = senToks.slice(idx, idx + dtWords.length)
+
Some(
NCNumeric(
- tokens = senToks.slice(idx, idx + dtWords.length),
+ tokens = toks,
value = dtPeriod.value,
isFractional = false,
- unit = Some(dtPeriod.unit)
+ unitData = Some(NCNumericUnitData(dtPeriod.unit, toks))
)
)
}
@@ -404,7 +415,10 @@ object NCNumericManager extends NCService {
None
}).headOption match {
case Some((unit, unitToks)) =>
- val numWithUnit = NCNumeric(seq ++ unitToks, v, isFractional = isFractional, Some(unit))
+ val numWithUnit =
+ NCNumeric(
+ seq ++ unitToks, v, isFractional = isFractional, Some(NCNumericUnitData(unit, unitToks))
+ )
// If unit name is same as user element name,
// it returns both variants: numeric with unit and without.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCElement.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCElement.java
index 9f5872a..b5b6cbd 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCElement.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCElement.java
@@ -382,4 +382,9 @@ public interface NCElement extends NCMetadata, Serializable {
default Optional<Boolean> isSparse() {
return Optional.empty();
}
+
+ // TODO:
+ default Optional<Boolean> isGreedy() {
+ return Optional.empty();
+ }
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelFileAdapter.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelFileAdapter.java
index c313bf7..efa2b68 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelFileAdapter.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelFileAdapter.java
@@ -357,6 +357,11 @@ abstract public class NCModelFileAdapter extends NCModelAdapter {
return nvl(js.isSparse(), proxy.isSparse());
}
+ @Override
+ public Optional<Boolean> isGreedy() {
+ return nvl(js.isGreedy(), proxy.isGreedy());
+ }
+
private<T> Optional<T> nvl(T t, T dflt) {
return Optional.of(t != null ? t : dflt);
}
@@ -484,6 +489,11 @@ abstract public class NCModelFileAdapter extends NCModelAdapter {
}
@Override
+ public boolean isGreedy() {
+ return proxy.isGreedy();
+ }
+
+ @Override
public Map<String, Object> getMetadata() {
return metadata;
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
index c213098..30a2b40 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
@@ -169,6 +169,11 @@ public interface NCModelView extends NCMetadata {
boolean DFLT_IS_SPARSE = false;
/**
+ * Default value for {@link #isGreedy()} method.
+ */
+ boolean DFLT_IS_GREEDY = true;
+
+ /**
* Default value for {@link #getMaxElementSynonyms()} method.
*/
int DFLT_MAX_ELEMENT_SYNONYMS = 1000;
@@ -818,6 +823,10 @@ public interface NCModelView extends NCMetadata {
return DFLT_IS_SPARSE;
}
+ default boolean isGreedy() {
+ return DFLT_IS_GREEDY;
+ }
+
/**
* Gets optional user defined model metadata that can be set by the developer and accessed later.
* By default, it returns an empty map. Note that this metadata is mutable and can be
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCElementJson.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCElementJson.java
index addca45..be7b995 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCElementJson.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCElementJson.java
@@ -36,6 +36,8 @@ public class NCElementJson {
private Boolean isPermutateSynonyms;
// Can be null.
private Boolean isSparse;
+ // Can be null.
+ private Boolean isGreedy;
public String getParentId() {
return parentId;
@@ -97,4 +99,10 @@ public class NCElementJson {
public void setSparse(Boolean sparse) {
isSparse = sparse;
}
+ public Boolean isGreedy() {
+ return isGreedy;
+ }
+ public void setGreedy(Boolean greedy) {
+ isGreedy = greedy;
+ }
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCModelJson.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCModelJson.java
index d2459d3..f332e08 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCModelJson.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/impl/json/NCModelJson.java
@@ -58,6 +58,7 @@ public class NCModelJson {
private int maxTotalSynonyms = DFLT_MAX_TOTAL_SYNONYMS;
private boolean isPermutateSynonyms = DFLT_IS_PERMUTATE_SYNONYMS;
private boolean isSparse = DFLT_IS_SPARSE;
+ private boolean isGreedy = DFLT_IS_GREEDY;
private int maxElementSynonyms = DFLT_MAX_TOTAL_SYNONYMS;
private boolean maxSynonymsThresholdError = DFLT_MAX_SYNONYMS_THRESHOLD_ERROR;
private long conversationTimeout = DFLT_CONV_TIMEOUT_MS;
@@ -202,6 +203,12 @@ public class NCModelJson {
public boolean isSparse() {
return isSparse;
}
+ public boolean isGreedy() {
+ return isGreedy;
+ }
+ public void setGreedy(boolean greedy) {
+ isGreedy = greedy;
+ }
public void setSparse(boolean sparse) {
isSparse = sparse;
}
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
index 1d923ce..431097c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
@@ -242,6 +242,7 @@ object NCCommandManager extends NCService {
override def getMaxTotalSynonyms: Int = mdl.getMaxTotalSynonyms
override def isNoUserTokensAllowed: Boolean = mdl.isNoUserTokensAllowed
override def isSparse: Boolean = mdl.isSparse
+ override def isGreedy: Boolean = mdl.isGreedy
override def getMetadata: util.Map[String, AnyRef] = mdl.getMetadata
override def getAdditionalStopWords: util.Set[String] = mdl.getAdditionalStopWords
override def getExcludedStopWords: util.Set[String] = mdl.getExcludedStopWords
@@ -269,6 +270,9 @@ object NCCommandManager extends NCService {
// New method instead of `isSparse`
def getSparse: lang.Boolean
+
+ // New method instead of `isGreedy`
+ def getGreedy: lang.Boolean
}
val elm: NCElement =
@@ -288,10 +292,12 @@ object NCCommandManager extends NCService {
// Hidden.
override def isPermutateSynonyms: Optional[lang.Boolean] = null
override def isSparse: Optional[lang.Boolean] = null
+ override def isGreedy: Optional[lang.Boolean] = null
// Wrapped.
override def getPermutateSynonyms: lang.Boolean = e.isPermutateSynonyms.orElse(null)
override def getSparse: lang.Boolean = e.isSparse.orElse(null)
+ override def getGreedy: lang.Boolean = e.isGreedy.orElse(null)
}
elm
}).asJava
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
index 1f81711..6e6f7d1 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
@@ -151,7 +151,7 @@ object NCModelEnricher extends NCProbeEnricher {
/**
*
* @param ns
- * @param elem
+ * @param elemId
* @param toks
* @param direct
* @param syn
@@ -160,7 +160,7 @@ object NCModelEnricher extends NCProbeEnricher {
*/
private def mark(
ns: Sentence,
- elem: NCElement,
+ elemId: String,
toks: Seq[NlpToken],
direct: Boolean,
syn: Option[Synonym] = None,
@@ -189,7 +189,7 @@ object NCModelEnricher extends NCProbeEnricher {
val idxs = toks.map(_.index).sorted
- val note = NlpNote(idxs, elem.getId, params: _*)
+ val note = NlpNote(idxs, elemId, params: _*)
toks.foreach(_.add(note))
@@ -269,7 +269,7 @@ object NCModelEnricher extends NCProbeEnricher {
if (!alreadyMarked(ns, elmId, matchedToks, matchedToks.map(_.index).sorted))
mark(
ns,
- elem = mdl.elements.getOrElse(elmId, throw new NCE(s"Custom model parser returned unknown element: $elmId")),
+ elemId = elmId,
toks = matchedToks,
direct = true,
metaOpt = Some(e.getMetadata.asScala.toMap)
@@ -410,27 +410,30 @@ object NCModelEnricher extends NCProbeEnricher {
dbgType: String,
ns: Sentence,
contCache: Cache,
- elem: NCElement,
+ elemId: String,
+ greedy: Boolean,
elemToks: Seq[NlpToken],
sliceToksIdxs: Seq[Int],
syn: Synonym,
- parts: Seq[TokType] = Seq.empty)
- : Unit = {
+ parts: Seq[TokType] = Seq.empty
+ ): Unit = {
val resIdxs = elemToks.map(_.index)
val resIdxsSorted = resIdxs.sorted
if (resIdxsSorted == sliceToksIdxs && U.isContinuous(resIdxsSorted))
- contCache(elem.getId) += sliceToksIdxs
+ contCache(elemId) += sliceToksIdxs
- val ok = !alreadyMarked(ns, elem.getId, elemToks, sliceToksIdxs)
+ val ok =
+ (!greedy || !alreadyMarked(ns, elemId, elemToks, sliceToksIdxs)) &&
+ ( parts.isEmpty || !parts.exists { case (t, _) => t.getId == elemId })
if (ok)
- mark(ns, elem, elemToks, direct = syn.isDirect && U.isIncreased(resIdxs), syn = Some(syn), parts = parts)
+ mark(ns, elemId, elemToks, direct = syn.isDirect && U.isIncreased(resIdxs), syn = Some(syn), parts = parts)
if (DEEP_DEBUG)
logger.trace(
s"${if (ok) "Added" else "Skipped"} element [" +
- s"id=${elem.getId}, " +
+ s"id=$elemId, " +
s"type=$dbgType, " +
s"text='${elemToks.map(_.origText).mkString(" ")}', " +
s"indexes=${resIdxs.mkString("[", ",", "]")}, " +
@@ -466,10 +469,11 @@ object NCModelEnricher extends NCProbeEnricher {
toks <- combToks;
idxs = toks.map(_.index);
e <- mdl.elements.values;
- eId = e.getId
+ eId = e.getId;
+ greedy = e.isGreedy.orElse(mdl.model.isGreedy)
if
- !contCache(eId).exists(_.containsSlice(idxs)) &&
- !alreadyMarked(ns, eId, toks, idxs)
+ !greedy ||
+ !contCache(eId).exists(_.containsSlice(idxs)) && !alreadyMarked(ns, eId, toks, idxs)
) {
// 1. SIMPLE.
if (simpleEnabled && (if (idlEnabled) mdl.hasIdlSynonyms(eId) else !mdl.hasIdlSynonyms(eId))) {
@@ -485,7 +489,7 @@ object NCModelEnricher extends NCProbeEnricher {
syns.get(tokStems) match {
case Some(s) =>
found = true
- add("simple continuous", ns, contCache, e, toks, idxs, s)
+ add("simple continuous", ns, contCache, eId, greedy, toks, idxs, s)
case None => notFound()
}
@@ -493,7 +497,7 @@ object NCModelEnricher extends NCProbeEnricher {
for (s <- syns if !found)
if (s.isMatch(toks)) {
found = true
- add("simple continuous scan", ns, contCache, e, toks, idxs, s)
+ add("simple continuous scan", ns, contCache, eId, greedy, toks, idxs, s)
}
tryMap(
@@ -512,7 +516,7 @@ object NCModelEnricher extends NCProbeEnricher {
if (!found && mdl.hasSparseSynonyms)
for (s <- get(mdl.sparseSynonyms, eId))
s.sparseMatch(toks) match {
- case Some(res) => add("simple sparse", ns, contCache, e, res, idxs, s)
+ case Some(res) => add("simple sparse", ns, contCache, eId, greedy, res, idxs, s)
case None => // No-op.
}
}
@@ -534,7 +538,7 @@ object NCModelEnricher extends NCProbeEnricher {
data = comb.map(_.data)
)
if (s.isMatch(data, req)) {
- add("IDL continuous", ns, contCache, e, toks, idxs, s, toParts(data, s))
+ add("IDL continuous", ns, contCache, eId, greedy, toks, idxs, s, toParts(data, s))
idlCache += comb
@@ -551,7 +555,7 @@ object NCModelEnricher extends NCProbeEnricher {
case Some(res) =>
val typ = if (s.sparse) "IDL sparse" else "IDL continuous"
- add(typ, ns, contCache, e, toTokens(res, ns), idxs, s, toParts(res, s))
+ add(typ, ns, contCache, eId, greedy, toTokens(res, ns), idxs, s, toParts(res, s))
idlCache += comb
case None => // No-op.
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/sentence/NCSentenceManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/sentence/NCSentenceManager.scala
index 74ead87..d5dfc1e 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/sentence/NCSentenceManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/sentence/NCSentenceManager.scala
@@ -30,7 +30,7 @@ import java.util
import java.util.{List => JList}
import scala.collection.mutable
import scala.collection.parallel.CollectionConverters._
-import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava, SetHasAsJava}
+import scala.jdk.CollectionConverters.{ListHasAsScala, SeqHasAsJava, SetHasAsJava, SetHasAsScala}
import scala.language.implicitConversions
/**
@@ -656,8 +656,8 @@ object NCSentenceManager extends NCService {
delCombs.filter(_ != note).flatMap(n => if (getPartKeys(n).contains(key)) Some(n) else None)
if (
- delCombOthers.exists(o => noteWordsIdxs == o.wordIndexes.toSet) ||
- delCombOthers.nonEmpty && !delCombOthers.exists(o => noteWordsIdxs.subsetOf(o.wordIndexes.toSet))
+ delCombOthers.nonEmpty &&
+ !delCombOthers.exists(o => noteWordsIdxs.subsetOf(o.wordIndexes.toSet))
)
Some(note)
else
@@ -732,18 +732,25 @@ object NCSentenceManager extends NCService {
)
)
+
def notNlpNotes(s: NCNlpSentence): Seq[NCNlpSentenceNote] = s.flatten.filter(!_.isNlp)
// Drops similar sentences (with same notes structure). Keeps with more found.
+ val notGreedyElems =
+ mdl.getElements.asScala.flatMap(e => if (!e.isGreedy.orElse(mdl.isGreedy)) Some(e.getId) else None).toSet
+
sens = sens.groupBy(notNlpNotes(_).groupBy(_.noteType).keys.toSeq.sorted.distinct).
- flatMap(p => {
- val m: Map[NCNlpSentence, Int] = p._2.map(p => p -> notNlpNotes(p).size).toMap
+ flatMap { case (types, sensSeq) =>
+ if (types.exists(notGreedyElems.contains))
+ sensSeq
+ else {
+ val m: Map[NCNlpSentence, Int] = sensSeq.map(p => p -> notNlpNotes(p).size).toMap
- val max = m.values.max
+ val max = m.values.max
- m.filter(_._2 == max).keys
- }).
- toSeq
+ m.filter(_._2 == max).keys
+ }
+ }.toSeq
sens =
sens.filter(s => {
@@ -791,4 +798,4 @@ object NCSentenceManager extends NCService {
* @param srvReqId
*/
def clearCache(srvReqId: String): Unit = combCache -= srvReqId
-}
+}
\ No newline at end of file
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/numeric/NCNumericEnricher.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/numeric/NCNumericEnricher.scala
index b28f198..670a4dc 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/numeric/NCNumericEnricher.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/nlp/enrichers/numeric/NCNumericEnricher.scala
@@ -198,7 +198,7 @@ object NCNumericEnricher extends NCServerEnricher {
private def toString(seq: Seq[NCNlpSentenceToken], sep: String = " ", stem: Boolean = false) =
seq.map(t => if (stem) t.stem else t.normText).mkString(sep)
- private def mkNote(
+ private def mkNotes(
toks: Seq[NCNlpSentenceToken],
from: Double,
fromIncl: Boolean,
@@ -206,9 +206,10 @@ object NCNumericEnricher extends NCServerEnricher {
to: Double,
toIncl: Boolean,
toFractional: Boolean,
- unitOpt: Option[NCNumericUnit]
- ): NCNlpSentenceNote = {
- val params = mutable.ArrayBuffer.empty[(String, Any)] ++
+ unitDataOpt: Option[NCNumericUnitData],
+ ): Seq[NCNlpSentenceNote] = {
+ val params =
+ mutable.ArrayBuffer.empty[(String, Any)] ++
Seq(
"from" -> from,
"fromIncl" -> fromIncl,
@@ -222,14 +223,36 @@ object NCNumericEnricher extends NCServerEnricher {
"isToPositiveInfinity" -> (to == MAX_VALUE)
)
- unitOpt match {
- case Some(unit) =>
- params += "unit" -> unit.name
- params += "unitType" -> unit.unitType
- case None => // No-op.
+ def mkAndAssign(toks: Seq[NCNlpSentenceToken], typ: String, params: (String, Any)*):NCNlpSentenceNote = {
+ val note = NCNlpSentenceNote(toks.map(_.index), "nlpcraft:num", params:_*)
+
+ toks.foreach(_.add(note))
+
+ note
+ }
+
+ unitDataOpt match {
+ case Some(unitData) =>
+ def extend(): Seq[(String, Any)] = {
+ params += "unit" -> unitData.unit.name
+ params += "unitType" -> unitData.unit.unitType
+
+ params
+ }
+
+ if (unitData.tokens == toks)
+ Seq(mkAndAssign(toks, "nlpcraft:num", extend():_*))
+ else {
+ Seq(
+ mkAndAssign(
+ toks.filter(t => !unitData.tokens.contains(t)), "nlpcraft:num", params.clone():_*
+ ),
+ mkAndAssign(toks, "nlpcraft:num", extend():_*)
+ )
+ }
+
+ case None => Seq(mkAndAssign(toks, "nlpcraft:num", params:_*))
}
-
- NCNlpSentenceNote(toks.map(_.index), "nlpcraft:num", params:_*)
}
/**
@@ -274,25 +297,28 @@ object NCNumericEnricher extends NCServerEnricher {
val prepToks = Seq(getBefore(ts1)) ++ ts1 ++ Seq(getBefore(ts2)) ++ ts2
- val badRange = num1.unit.isDefined && num2.unit.isDefined && num1.unit != num2.unit
+ val badRange =
+ num1.unitData.isDefined &&
+ num2.unitData.isDefined &&
+ num1.unitData.get.unit != num2.unitData.get.unit
if (!badRange) {
val unit =
- if (num1.unit.isDefined && num2.unit.isEmpty)
- num1.unit
- else if (num1.unit.isEmpty && num2.unit.isDefined)
- num2.unit
- else if (num1.unit.isEmpty && num2.unit.isEmpty)
+ if (num1.unitData.isDefined && num2.unitData.isEmpty)
+ num1.unitData
+ else if (num1.unitData.isEmpty && num2.unitData.isDefined)
+ num2.unitData
+ else if (num1.unitData.isEmpty && num2.unitData.isEmpty)
None
- else{
- require(num1.unit == num2.unit)
-
- num1.unit
+ else {
+ require(num1.unitData.get.unit == num2.unitData.get.unit)
+
+ Some(NCNumericUnitData(num1.unitData.get.unit, num1.tokens ++ num2.tokens))
}
- val note = p._2 match {
+ val notes = p._2 match {
case BETWEEN_EXCLUSIVE =>
- mkNote(
+ mkNotes(
prepToks,
d1,
fromIncl = false,
@@ -303,7 +329,7 @@ object NCNumericEnricher extends NCServerEnricher {
unit
)
case BETWEEN_INCLUSIVE =>
- mkNote(
+ mkNotes(
prepToks,
d1,
fromIncl = true,
@@ -315,9 +341,7 @@ object NCNumericEnricher extends NCServerEnricher {
)
case _ => throw new AssertionError(s"Illegal note type: ${p._2}.")
}
-
- prepToks.foreach(_.add(note))
-
+
processed ++= ts1
processed ++= ts2
}
@@ -340,10 +364,10 @@ object NCNumericEnricher extends NCServerEnricher {
processed ++= toks
- val note =
+ val notes =
prep.prepositionType match {
case MORE =>
- mkNote(
+ mkNotes(
toks,
num.value,
fromIncl = false,
@@ -351,10 +375,10 @@ object NCNumericEnricher extends NCServerEnricher {
to = MAX_VALUE,
toIncl = true,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case MORE_OR_EQUAL =>
- mkNote(
+ mkNotes(
toks,
num.value,
fromIncl = true,
@@ -362,10 +386,10 @@ object NCNumericEnricher extends NCServerEnricher {
to = MAX_VALUE,
toIncl = true,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case LESS =>
- mkNote(
+ mkNotes(
toks,
MIN_VALUE,
fromIncl = true,
@@ -373,10 +397,10 @@ object NCNumericEnricher extends NCServerEnricher {
to = num.value,
toIncl = false,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case LESS_OR_EQUAL =>
- mkNote(
+ mkNotes(
toks,
MIN_VALUE,
fromIncl = true,
@@ -384,10 +408,10 @@ object NCNumericEnricher extends NCServerEnricher {
to = num.value,
toIncl = true,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case EQUAL =>
- mkNote(
+ mkNotes(
toks,
num.value,
fromIncl = true,
@@ -395,10 +419,10 @@ object NCNumericEnricher extends NCServerEnricher {
to = num.value,
toIncl = true,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case NOT_EQUAL =>
- mkNote(
+ mkNotes(
toks,
num.value,
fromIncl = false,
@@ -406,12 +430,13 @@ object NCNumericEnricher extends NCServerEnricher {
to = num.value,
toIncl = false,
toFractional = num.isFractional,
- num.unit
+ num.unitData
)
case _ => throw new AssertionError(s"Illegal note type: ${prep.prepositionType}.")
}
-
- toks.foreach(_.add(note))
+
+ for (note <- notes)
+ toks.foreach(_.add(note))
}
}
@@ -423,7 +448,7 @@ object NCNumericEnricher extends NCServerEnricher {
// Numeric without conditions.
for (num <- nums if !processed.exists(num.tokens.contains)) {
- val note = mkNote(
+ val notes = mkNotes(
num.tokens,
num.value,
fromIncl = true,
@@ -431,12 +456,10 @@ object NCNumericEnricher extends NCServerEnricher {
num.value,
toIncl = true,
num.isFractional,
- num.unit
+ num.unitData
)
processed ++= num.tokens
-
- num.tokens.foreach(_.add(note))
}
}
}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
index 3598d2e..daf1ab0 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
@@ -19,7 +19,8 @@ package org.apache.nlpcraft
import org.apache.nlpcraft.model.{NCElement, NCValue}
-import java.util
+import java.{lang, util}
+import java.util.Optional
import scala.jdk.CollectionConverters.{SeqHasAsJava, SetHasAsJava}
import scala.language.implicitConversions
@@ -29,9 +30,23 @@ import scala.language.implicitConversions
case class NCTestElement(id: String, syns: String*) extends NCElement {
private val values = new util.ArrayList[NCValue]
+ var metadata: util.Map[String, AnyRef] = super.getMetadata
+ var description: String = super.getDescription
+ var parentId: String = super.getParentId
+ var permutateSynonyms: Optional[lang.Boolean] = super.isPermutateSynonyms
+ var sparse: Optional[lang.Boolean] = super.isSparse
+ var greedy: Optional[lang.Boolean] = super.isGreedy
+
override def getId: String = id
override def getSynonyms: util.List[String] = (syns :+ id).asJava
override def getValues: util.List[NCValue] = values
+
+ override def getMetadata: util.Map[String, AnyRef] = metadata
+ override def getDescription: String = description
+ override def getParentId: String = parentId
+ override def isPermutateSynonyms: Optional[lang.Boolean] = permutateSynonyms
+ override def isSparse: Optional[lang.Boolean] = sparse
+ override def isGreedy: Optional[lang.Boolean] = greedy
}
/**
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/NCEnrichersTestBeans.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/NCEnrichersTestBeans.scala
index fa54b2d..b4d2f71 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/NCEnrichersTestBeans.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/NCEnrichersTestBeans.scala
@@ -64,11 +64,20 @@ case class NCTestCoordinateToken(text: String, latitude: Double, longitude: Doub
override def toString: String = s"$text(coordinate)<lon=$longitude, lat=$longitude>"
}
-case class NCTestNumericToken(text: String, from: Double, to: Double) extends NCTestToken {
+case class NCTestNumericToken(text: String, from: Double, to: Double, unit: Option[String] = None) extends NCTestToken {
require(text != null)
override def id: String = "nlpcraft:num"
- override def toString: String = s"$text(num)<from=$from, to=$to>"
+ override def toString: String = {
+ var s = s"$text(num)<from=$from, to=$to>"
+
+ unit match {
+ case Some(u) => s = s"$s($u)"
+ case None => // No-op.
+ }
+
+ s
+ }
}
case class NCTestCityToken(text: String, city: String) extends NCTestToken {
@@ -305,10 +314,13 @@ object NCTestToken {
longitude = t.meta("nlpcraft:coordinate:longitude")
)
case "nlpcraft:num" =>
+ val unit: Optional[String] = t.metaOpt("nlpcraft:num:unit")
+
NCTestNumericToken(
txt,
from = t.meta("nlpcraft:num:from"),
- to = t.meta("nlpcraft:num:to")
+ to = t.meta("nlpcraft:num:to"),
+ unit = unit.asScala
)
case "nlpcraft:date" => NCTestDateToken(txt)
case "nlpcraft:city" => NCTestCityToken(txt, city = t.meta("nlpcraft:city:city"))
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec2.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec2.scala
index 45c1690..ce2fc90 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec2.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec2.scala
@@ -68,5 +68,4 @@ class NCNestedTestModel22 extends NCNestedTestModel21 {
* Nested elements model enricher test.
*/
@NCTestEnvironment(model = classOf[NCNestedTestModel22], startClient = true)
-class NCEnricherNestedModelSpec22 extends NCEnricherNestedModelSpec21
-
+class NCEnricherNestedModelSpec22 extends NCEnricherNestedModelSpec21
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec3.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec3.scala
index 60e83c6..2303e30 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec3.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec3.scala
@@ -17,7 +17,7 @@
package org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model
-import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentMatch, NCModelAdapter, NCResult}
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCModelAdapter, NCResult}
import org.apache.nlpcraft.{NCTestContext, NCTestElement, NCTestEnvironment}
import org.junit.jupiter.api.Test
@@ -27,20 +27,18 @@ import scala.jdk.CollectionConverters.SetHasAsJava
/**
* Nested Elements test model.
*/
-class NCNestedTestModel3 extends NCModelAdapter(
- "nlpcraft.nested3.test.mdl", "Nested Data Test Model", "1.0"
-) {
+class NCNestedTestModel3 extends NCModelAdapter("nlpcraft.nested3.test.mdl", "Nested Test Model", "1.0") {
override def getElements: util.Set[NCElement] =
Set(
NCTestElement("e1", "//[a-zA-Z0-9]+//"),
- NCTestElement("e2", "^^{# == 'e1'}^^"),
+ NCTestElement("e2", "^^{# == 'e1'}^^")
)
override def getAbstractTokens: util.Set[String] = Set("e1").asJava
override def getEnabledBuiltInTokens: util.Set[String] = Set.empty[String].asJava
@NCIntent("intent=onE2 term(t1)={# == 'e2'}[12, 100]")
- def onAB(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
+ def onAB(): NCResult = NCResult.text("OK")
}
/**
@@ -49,5 +47,5 @@ class NCNestedTestModel3 extends NCModelAdapter(
@NCTestEnvironment(model = classOf[NCNestedTestModel3], startClient = true)
class NCEnricherNestedModelSpec3 extends NCTestContext {
@Test
- def test(): Unit = checkIntent("a " * 18, "onE2")
+ def test(): Unit = checkIntent("a " * 15, "onE2")
}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec41.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec4.scala
similarity index 95%
rename from nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec41.scala
rename to nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec4.scala
index e049a39..27082f1 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec41.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec4.scala
@@ -27,9 +27,7 @@ import scala.jdk.CollectionConverters.SetHasAsJava
/**
* Nested Elements test model.
*/
-class NCNestedTestModel41 extends NCModelAdapter(
- "nlpcraft.nested4.test.mdl", "Nested Data Test Model", "1.0"
-) {
+class NCNestedTestModel41 extends NCModelAdapter("nlpcraft.nested4.test.mdl", "Nested Test Model", "1.0") {
override def getElements: util.Set[NCElement] =
Set(
NCTestElement("e1", "//[a-zA-Z0-9]+//"),
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
index 72d6589..48d0441 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
@@ -26,9 +26,7 @@ import java.util
/**
* Nested Elements test model.
*/
-class NCNestedTestModel5 extends NCModelAdapter(
- "nlpcraft.nested5.test.mdl", "Nested Data Test Model", "1.0"
-) {
+class NCNestedTestModel5 extends NCModelAdapter("nlpcraft.nested5.test.mdl", "Nested Test Model", "1.0") {
override def getElements: util.Set[NCElement] =
Set(
NCTestElement("cityWrapper", "^^[cityAlias]{# == 'nlpcraft:city'}^^"),
@@ -40,7 +38,7 @@ class NCNestedTestModel5 extends NCModelAdapter(
" get(meta_part('cityAlias', 'nlpcraft:city:citymeta'), 'population') >= 10381222" +
"}"
)
- private def onBigCity(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
+ private def onBigCity(): NCResult = NCResult.text("OK")
}
/**
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec6.scala
similarity index 55%
copy from nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
copy to nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec6.scala
index 72d6589..fd21e8a 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec5.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCEnricherNestedModelSpec6.scala
@@ -17,37 +17,38 @@
package org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model
-import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentMatch, NCModelAdapter, NCResult}
+import org.apache.nlpcraft.model.{NCContext, NCElement, NCModelAdapter, NCResult}
import org.apache.nlpcraft.{NCTestContext, NCTestElement, NCTestEnvironment}
import org.junit.jupiter.api.Test
import java.util
+import scala.jdk.CollectionConverters.{IterableHasAsScala, SetHasAsJava}
/**
* Nested Elements test model.
*/
-class NCNestedTestModel5 extends NCModelAdapter(
- "nlpcraft.nested5.test.mdl", "Nested Data Test Model", "1.0"
-) {
+class NCNestedTestModel6 extends NCModelAdapter("nlpcraft.nested6.test.mdl", "Nested Test Model", "1.0") {
+ override def getAbstractTokens: util.Set[String] = Set("nlpcraft:date").asJava
+
override def getElements: util.Set[NCElement] =
- Set(
- NCTestElement("cityWrapper", "^^[cityAlias]{# == 'nlpcraft:city'}^^"),
- )
- @NCIntent(
- "intent=bigCity " +
- "term(city)={" +
- " # == 'cityWrapper' && " +
- " get(meta_part('cityAlias', 'nlpcraft:city:citymeta'), 'population') >= 10381222" +
- "}"
- )
- private def onBigCity(ctx: NCIntentMatch): NCResult = NCResult.text("OK")
-}
+ Set(NCTestElement("dateWrapper", "^^{# == 'nlpcraft:date'}^^"))
+
+ override def onContext(ctx: NCContext): NCResult = {
+ require(ctx.getRequest.getNormalizedText == "today")
+ println(s"Variants:\n${ctx.getVariants.asScala.mkString("\n")}")
+
+ // `nlpcraft:date` will be deleted.
+ require(ctx.getVariants.size() == 1)
+
+ NCResult.text("OK")
+ }
+}
/**
*
*/
-@NCTestEnvironment(model = classOf[NCNestedTestModel5], startClient = true)
-class NCEnricherNestedModelSpec5 extends NCTestContext {
+@NCTestEnvironment(model = classOf[NCNestedTestModel6], startClient = true)
+class NCEnricherNestedModelSpec6 extends NCTestContext {
@Test
- def test(): Unit = checkIntent("moscow", "bigCity")
+ def test(): Unit = checkResult("today", "OK")
}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedAnySpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedAnySpec.scala
new file mode 100644
index 0000000..8d8c769
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedAnySpec.scala
@@ -0,0 +1,102 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword
+
+import org.apache.nlpcraft.{NCTestContext, NCTestEnvironment}
+import org.junit.jupiter.api.Test
+
+/**
+ *
+ */
+class NCNestedAnySpec extends NCTestContext {
+ private def test(): Unit = {
+ // 1, 2 and 3 any words should be suitable.
+ checkIntent("a t1 t2 t3 b", "compose")
+ checkIntent("a t1 t2 b", "compose")
+ checkIntent("a t1 b", "compose")
+
+ // Too many 'any words'.
+ checkFail("a t1 t2 t3 t4 b")
+
+ // Missed 'any words'.
+ checkFail("a b")
+ }
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex1], startClient = true)
+ def testRegex1(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex2], startClient = true)
+ def testRegex2(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex3], startClient = true)
+ def testRegex3(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex5], startClient = true)
+ def testRegex4(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex5], startClient = true)
+ def testRegex5(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex6], startClient = true)
+ def testRegex6(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyRegex7], startClient = true)
+ def testRegex7(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum1], startClient = true)
+ def testAlphaNum1(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum2], startClient = true)
+ def testAlphaNum2(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum3], startClient = true)
+ def testAlphaNum3(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum4], startClient = true)
+ def testAlphaNum4(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum5], startClient = true)
+ def testAlphaNum5(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum6], startClient = true)
+ def testAlphaNum6(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyAlphaNum7], startClient = true)
+ def testAlphaNum7(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace1], startClient = true)
+ def testNotSpaceNum1(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace2], startClient = true)
+ def testNotSpaceNum2(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace3], startClient = true)
+ def testNotSpaceNum3(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace4], startClient = true)
+ def testNotSpaceNum4(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace5], startClient = true)
+ def testNotSpaceNum5(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace6], startClient = true)
+ def testNotSpaceNum6(): Unit = test()
+
+ @Test @NCTestEnvironment(model = classOf[NCNestedTestModelAnyNotSpace7], startClient = true)
+ def testNotSpaceNum7(): Unit = test()
+}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyAlphaNum.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyAlphaNum.scala
new file mode 100644
index 0000000..32df509
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyAlphaNum.scala
@@ -0,0 +1,35 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword
+
+import org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model.anyword.adapters._
+
+/**
+ *
+ */
+trait NCNestedTestModelAnyAlphaNum extends NCNestedModelAnyAdapter {
+ override def anyDefinition: String = "{^^{is_alphanum(tok_txt)}^^}"
+}
+
+class NCNestedTestModelAnyAlphaNum1 extends NCNestedTestModelAny1 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum2 extends NCNestedTestModelAny2 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum3 extends NCNestedTestModelAny3 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum4 extends NCNestedTestModelAny4 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum5 extends NCNestedTestModelAny5 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum6 extends NCNestedTestModelAny6 with NCNestedTestModelAnyAlphaNum
+class NCNestedTestModelAnyAlphaNum7 extends NCNestedTestModelAny7 with NCNestedTestModelAnyAlphaNum
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyNotSpace.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyNotSpace.scala
new file mode 100644
index 0000000..dd5ba28
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyNotSpace.scala
@@ -0,0 +1,35 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword
+
+import org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model.anyword.adapters._
+
+/**
+ *
+ */
+trait NCNestedTestModelAnyNotSpace extends NCNestedModelAnyAdapter {
+ override def anyDefinition: String = "{^^{tok_txt != ' '}^^}"
+}
+
+class NCNestedTestModelAnyNotSpace1 extends NCNestedTestModelAny1 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace2 extends NCNestedTestModelAny2 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace3 extends NCNestedTestModelAny3 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace4 extends NCNestedTestModelAny4 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace5 extends NCNestedTestModelAny5 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace6 extends NCNestedTestModelAny6 with NCNestedTestModelAnyNotSpace
+class NCNestedTestModelAnyNotSpace7 extends NCNestedTestModelAny7 with NCNestedTestModelAnyNotSpace
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyRegex.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyRegex.scala
new file mode 100644
index 0000000..7a1fd71
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/NCNestedTestModelsAnyRegex.scala
@@ -0,0 +1,35 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword
+
+import org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model.anyword.adapters._
+
+/**
+ *
+ */
+trait NCNestedTestModelAnyRegex extends NCNestedModelAnyAdapter {
+ override def anyDefinition: String = "{//[a-zA-Z0-9]+//}"
+}
+
+class NCNestedTestModelAnyRegex1 extends NCNestedTestModelAny1 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex2 extends NCNestedTestModelAny2 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex3 extends NCNestedTestModelAny3 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex4 extends NCNestedTestModelAny4 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex5 extends NCNestedTestModelAny5 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex6 extends NCNestedTestModelAny6 with NCNestedTestModelAnyRegex
+class NCNestedTestModelAnyRegex7 extends NCNestedTestModelAny7 with NCNestedTestModelAnyRegex
\ No newline at end of file
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedModelAnyAdapter.scala
similarity index 55%
copy from nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala
copy to nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedModelAnyAdapter.scala
index 75a3365..0627c6e 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/nlp/numeric/NCNumeric.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedModelAnyAdapter.scala
@@ -15,27 +15,28 @@
* limitations under the License.
*/
-package org.apache.nlpcraft.common.nlp.numeric
+package org.apache.nlpcraft.probe.mgrs.nlp.enrichers.model.anyword.adapters
-import org.apache.nlpcraft.common.nlp.NCNlpSentenceToken
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.NCModelAdapter
-/**
- *
- * @param name
- * @param unitType
- */
-case class NCNumericUnit(name: String, unitType: String)
+import java.util
+import java.util.Optional
+import scala.jdk.CollectionConverters.SetHasAsJava
/**
*
- * @param tokens
- * @param value
- * @param isFractional
- * @param unit
*/
-case class NCNumeric(
- tokens: Seq[NCNlpSentenceToken],
- value: Double,
- isFractional: Boolean,
- unit: Option[NCNumericUnit]
-)
+abstract class NCNestedModelAnyAdapter extends NCModelAdapter("nlpcraft.test.mdl", "Test Model", "1.0") {
+ override def getEnabledBuiltInTokens: util.Set[String] = Set.empty[String].asJava
+
+ protected def mkNotGreedy(id: String, syn: String): NCTestElement = {
+ val e = NCTestElement(id, syn)
+
+ e.greedy = Optional.of(false)
+
+ e
+ }
+
+ def anyDefinition: String
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny1.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny1.scala
new file mode 100644
index 0000000..de70e71
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny1.scala
@@ -0,0 +1,51 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * 'compose' element.
+ * intent without DSL.
+ * 'any' element's position is not restricted.
+ */
+abstract class NCNestedTestModelAny1 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set("a", "b", "any").asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 1
+ // a t1 t2 b - 1
+ // a t1 b - 1
+ // a t1 t2 t3 t4 b - 1
+ // a b - 1
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", s"$anyDefinition[1, 3]"),
+ NCTestElement("compose", "^^{# == 'a'}^^ ^^{# == 'any'}^^ ^^{# == 'b'}^^")
+ )
+
+ @NCIntent("intent=compose term(x)={# == 'compose'}")
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny2.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny2.scala
new file mode 100644
index 0000000..74218ee
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny2.scala
@@ -0,0 +1,52 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * 'compose' element.
+ * intent without DSL.
+ * 'any' element's position is restricted.
+ */
+abstract class NCNestedTestModelAny2 extends NCNestedModelAnyAdapter {
+ //override def getAbstractTokens: util.Set[String] = Set("a", "b", "any").asJava
+
+ // Variants:
+ // a t1 t2 t3 b -
+ // a t1 t2 b -
+ // a t1 b -
+ // a t1 t2 t3 t4 b -
+ // a b -
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", s"$anyDefinition[1, 3]"),
+ NCTestElement(
+ "compose", "^^{# == 'a'}^^ ^^{# == 'any' && tok_is_between_ids('a', 'b') == true}^^ ^^{# == 'b'}^^"
+ )
+ )
+
+ @NCIntent("intent=compose term(x)={# == 'compose'}")
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny3.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny3.scala
new file mode 100644
index 0000000..ce3dcf8
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny3.scala
@@ -0,0 +1,49 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'compose' element with IDL (regex)
+ * intent without DSL.
+ * 'any' element's position is restricted implicitly via synonym definition.
+ */
+abstract class NCNestedTestModelAny3 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set("a", "b").asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 1
+ // a t1 t2 b - 1
+ // a t1 b - 1
+ // a t1 t2 t3 t4 b - 1
+ // a b - 1
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ NCTestElement("compose", s"^^{# == 'a'}^^ $anyDefinition[1, 3] ^^{# == 'b'}^^")
+ )
+
+ @NCIntent("intent=compose term(x)={# == 'compose'}")
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny4.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny4.scala
new file mode 100644
index 0000000..e1fc89d
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny4.scala
@@ -0,0 +1,55 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * no 'compose' element.
+ * intent with DSL.
+ * 'any' element's position is not restricted (IDL).
+ */
+abstract class NCNestedTestModelAny4 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set.empty[String].asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 13
+ // a t1 t2 b - 11
+ // a t1 b - 8
+ // a t1 t2 t3 t4 b - 16
+ // a b - 5
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", s"$anyDefinition[1, 3]")
+ )
+
+ @NCIntent(
+ "intent=compose options={'ordered': true, 'unused_free_words': false} " +
+ " term(a)={# == 'a'}" +
+ " term(any)={# == 'any'} " +
+ " term(b)={# == 'b'}"
+ )
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny5.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny5.scala
new file mode 100644
index 0000000..8bcdd7f
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny5.scala
@@ -0,0 +1,55 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * no 'compose' element.
+ * intent with DSL.
+ * 'any' element's position is not restricted (IDL).
+ */
+abstract class NCNestedTestModelAny5 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set.empty[String].asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 4
+ // a t1 t2 b - 4
+ // a t1 b - 4
+ // a t1 t2 t3 t4 b - 4
+ // a b - 4
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", anyDefinition)
+ )
+
+ @NCIntent(
+ "intent=compose options={'ordered': true, 'unused_free_words': false} " +
+ " term(a)={# == 'a'}" +
+ " term(any)={# == 'any'}[1, 3] " +
+ " term(b)={# == 'b'}"
+ )
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny6.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny6.scala
new file mode 100644
index 0000000..d63151b
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny6.scala
@@ -0,0 +1,55 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * no 'compose' element.
+ * intent with DSL.
+ * 'any' element's position is restricted (IDL).
+ */
+abstract class NCNestedTestModelAny6 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set.empty[String].asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 13
+ // a t1 t2 b - 11
+ // a t1 b - 8
+ // a t1 t2 t3 t4 b - 16
+ // a b - 5
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", s"$anyDefinition[1, 3]")
+ )
+
+ @NCIntent(
+ "intent=compose options={'ordered': true, 'unused_free_words': false} " +
+ " term(a)={# == 'a'}" +
+ " term(any)={# == 'any' && tok_is_between_ids('a', 'b') == true} " +
+ " term(b)={# == 'b'}"
+ )
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny7.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny7.scala
new file mode 100644
index 0000000..384cb26
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/anyword/adapters/NCNestedTestModelAny7.scala
@@ -0,0 +1,55 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.model.anyword.adapters
+
+import org.apache.nlpcraft.NCTestElement
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCResult}
+
+import java.util
+import scala.jdk.CollectionConverters.SetHasAsJava
+
+/**
+ * 'any' ('not greedy') element - regex.
+ * no 'compose' element.
+ * intent with DSL.
+ * 'any' element's position is restricted (IDL).
+ */
+abstract class NCNestedTestModelAny7 extends NCNestedModelAnyAdapter {
+ override def getAbstractTokens: util.Set[String] = Set.empty[String].asJava
+
+ // Variants:
+ // a t1 t2 t3 b - 4
+ // a t1 t2 b - 4
+ // a t1 b - 4
+ // a t1 t2 t3 t4 b - 4
+ // a b - 4
+ override def getElements: util.Set[NCElement] =
+ Set(
+ NCTestElement("a"),
+ NCTestElement("b"),
+ mkNotGreedy("any", anyDefinition)
+ )
+
+ @NCIntent(
+ "intent=compose options={'ordered': true, 'unused_free_words': false} " +
+ " term(a)={# == 'a'}" +
+ " term(any)={# == 'any' && tok_is_between_ids('a', 'b') == true}[1, 3] " +
+ " term(b)={# == 'b'}"
+ )
+ def onCompose(): NCResult = NCResult.text("OK")
+}
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/numeric/NCEnricherNumericSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/numeric/NCEnricherNumericSpec.scala
new file mode 100644
index 0000000..7e25cb1
--- /dev/null
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/numeric/NCEnricherNumericSpec.scala
@@ -0,0 +1,75 @@
+/*
+ * 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
+ *
+ * https://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.probe.mgrs.nlp.enrichers.numeric
+
+import org.apache.nlpcraft.NCTestEnvironment
+import org.apache.nlpcraft.probe.mgrs.nlp.enrichers.{NCDefaultTestModel, NCEnricherBaseSpec, NCTestNlpToken => nlp, NCTestNumericToken => num}
+import org.junit.jupiter.api.Test
+
+/**
+ * Limit enricher test.
+ */
+@NCTestEnvironment(model = classOf[NCDefaultTestModel], startClient = true)
+class NCEnricherNumericSpec extends NCEnricherBaseSpec {
+ /**
+ *
+ */
+ @Test
+ def test(): Unit =
+ runBatch(
+ // Two variants.
+ _ => checkAll(
+ "23 meters",
+ Seq(
+ num(text = "23 meters", 23, 23, unit = Some("meter"))
+ ),
+ Seq(
+ num(text = "23", 23, 23),
+ nlp(text = "meters")
+ )
+ ),
+
+ // Two variants.
+ _ => checkAll(
+ "23 m",
+ Seq(
+ num(text = "23 m", 23, 23, unit = Some("meter"))
+ ),
+ Seq(
+ num(text = "23", 23, 23),
+ nlp(text = "m")
+ )
+ ),
+
+ // One variant.
+ _ => checkAll(
+ "23m",
+ Seq(
+ num(text = "23m", 23, 23, unit = Some("meter"))
+ )
+ ),
+
+ // One variant.
+ _ => checkAll(
+ "23M",
+ Seq(
+ num(text = "23M", 23, 23, unit = Some("meter"))
+ )
+ )
+ )
+}