You are viewing the documentation for Blueriq 17. Documentation for other versions is available in our documentation directory.

Learn more about the collection functions EXISTS, EACH, ALL, COLLECT FROM WHERE, COLLECT FROM NAMED WHERE, COLLECT, UNPACK, LIST, SIZE, UNIQUE, SUBSET OF, UNION, INTERSECTION, DIFFERENCE, SYMMETRIC_DIFFERENCE. 

Overview

FunctionDescription
EXISTSDetermines whether an instance of a specified entity exists, optionally meeting certain criteria.
EACHDetermines whether all instances of a specified entity meet a certain criteria. 
ALLCreates a collection of all instances of a specified entity.
COLLECT FROM [WHERE]Creates a collection of entity or attribute instances (meeting certain criteria).
COLLECT FROM NAMED [WHERE]Version of COLLECT FROM [WHERE] for complex nested selections with an alias.
UNPACKExtracts the value from a single valued collection or list. Is the inverse of the LIST function.
LISTCreates a list based on a value. Is the inverse of the UNPACK function.
SIZEDetermines the size of a collection.
UNIQUEFilters duplicate items from a collection resulting from a subexpression in a larger expression.
SUBSET OFReturns TRUE if the items in a collection are all present in another collection.
UNIONAdds two collections of the same base type to a new collection.
INTERSECTIONDetermines the intersection of two collections. 
DIFFERENCEDetermines the difference between 2 collections. Returns a collection containing all the items from collection1 that are not present in collection2. 
SYMMETRIC_DIFFERENCEDetermines the symmetric difference between two collections. Returns a collection with the elements of the provided collections which are in either one of the collections, but not in both. 

A note on collections and duplicates. 

Functions

EXISTS


Determines whether an instance of a specified entity exists, optionally meeting certain criteria.


Syntax

EXISTS instances [ WHERE ( condition ) ]


Inputs
  • instances - Instances to search.
  • condition - boolean expression that represents the criterion the instance has to meet.


Return type

  • boolean


Examples

Suppose the following data model.


Person.namePerson.genderPerson.age
“Kim”?23
“Rick”“m”35
“Bob”“m”42
“John”“m”19
“Mary”“f”33
ExpressionResultType
EXISTS PersonTRUEboolean
EXISTS Person WHERE ( Person.age < 18 ) FALSEboolean
EXISTS Person WHERE ( Person.gender = "m" AND Person.age > 35 )TRUEboolean
EXISTS Person WHERE ( Person.gender = "f" AND Person.age < 25 )UNKNOWNboolean
EXISTS Person WHERE ( Person.age < 25 AND Person.gender = "f" )FALSEboolean
EXISTS Person WHERE ( Person.gender = ? )TRUEboolean

Please note that you can use a COLLECT expression as well for collecting instances for which the EXISTS should hold. E.g., EXISTS (COLLECT Person FROM ALL Person WHERE (Person.gender = "f")) WHERE ( Person.age < 25 ).

In case that the list of instances for which the EXISTS should hold is an EMPTY list, then the EXISTS expression returns FALSE.

Back to Top



EACH


Determines whether all instances of a specified entity meet a certain criteria. 


Syntax

EACH instances WHERE ( condition )



Inputs
  • instances - A collection of instances to search.
  • condition - Boolean expression that represents the criterion the instance has to meet.


Return type

  • Boolean


Examples

Suppose the following data model.


Person.namePerson.genderPerson.age
“Kim”?23
“Rick”“m”35
“Bob”“m”42
“John”“m”19
ExpressionResultType
EACH Person WHERE ( Person.age < 18 ) FALSEBoolean
EACH Person WHERE ( Person.age > 18 )TRUEBoolean
EACH Person WHERE ( Person.age < 20 )FALSEBoolean
EACH Person WHERE ( Person.gender = "m" OR Person.age > 35 )UNKNOWNBoolean
EACH Person WHERE ( Person.gender = "m" OR Person.age > 20 )TRUEBoolean

Please note that you can use a COLLECT expression as well for collecting instances for which the EACH should hold. E.g., EACH (COLLECT Person FROM ALL Person WHERE (Person.gender = "f")) WHERE ( Person.age < 25 ).

In case that the list of instances for which the EACH should hold is an EMPTY list, then the EACH expression returns TRUE.

Back to Top


ALL


Creates a collection of all instances of a specified entity.


Syntax

 ALL entity


Input
  • entity - Entity of which to collect all its instances.


Return type

  • collection of entity instances


Examples

Suppose the following data model. The Parent and Child entities both have Person as base entity.


Parent instanceChild instance
Parent_1Child_1
Parent_1Child_2
Parent_1Child_3
Parent_2Child_4
ExpressionResultType
ALL Parent[ Parent_1 , Parent_2 ]Collection of Parent instances
ALL Child[ Child_1 , Child_2 , Child_3 , Child_4 ]Collection of Child instances
ALL Person[ Parent_1 , Parent_2 , Child_1 , Child_2 , Child_3 , Child_4 ]Collection of Person instances


Back to Top


COLLECT FROM [WHERE]


Creates a collection of entity or attribute instances (meeting certain criteria).


Syntax

 COLLECT entity | attribute FROM collection [ WHERE ( expression ) ]


Inputs
  • entity or attribute - Entity or attribute to collect.
  • collection - A collection of entity instances.
  • expression - Boolean expression that represents the criterion the instance has to meet.


Return type

  • collection of entity instances
  • collection of attribute values

A collection contains no duplicates. Intermediary COLLECT expressions can contain duplicates however, please see the note on collections and duplicates.


Examples

Suppose the following model. Entity Teacher has a multivalued relation with entity Child via the relation Teacher.teaches_Children.


Teacher instanceChild instanceChild.nameChild.hobbies
Teacher_1Child_1“Kim”“Reading”, “Dancing”
Teacher_1Child_2“Rick”“Tennis”, “Dancing”
Teacher_1Child_3“Bob”“Painting”, “Basketball”, “Reading”
Teacher_2Child_1“Kim”“Reading”, “Dancing”
Teacher_2Child_3“Bob”“Painting”, “Basketball”, “Reading”
Teacher_2Child_4“Mary”“Football”
ExpressionResultType
COLLECT Child.name FROM ALL Child[ “Kim”, “Rick”, “Bob”, “Mary” ]String (multivalued)
COLLECT Child FROM Teacher[Teacher_2].teaches_Children[ Child_1, Child_3, Child_4 ]Collection of Child instances
COLLECT Child.hobbies FROM Teacher[Teacher_1].teaches_Children[ “Reading”, “Dancing”, “Tennis”, “Painting”, “Basketball” ]String (multivalued)

COLLECT Child.name FROM ALL Child
WHERE ( Child.hobbies = "Reading" )

[ “Kim”, “Bob” ]String (multivalued)
COLLECT Child.hobbies FROM ALL Child
WHERE ( Child.name = "Mary" )
[ “Football” ]String (multivalued)



Back to Top


COLLECT FROM NAMED [WHERE]


Version of the COLLECT FROM [WHERE] function for complex nested selections with an alias.


Syntax

 COLLECT entity | attribute FROM collection NAMED alias [ WHERE ( expression ) ]


Inputs
  • entity or attribute - Entity or attribute to collect. This should contain the alias, e.g. alias.Name.
  • collection - A collection of entity instances.
  • alias - A name for the collection.
  • expression - Boolean expression that represents the criterion the instance has to meet.


Return type

  • collection of entity instances
  • collection of attribute values

A collections contains no duplicates. Intermediary COLLECT expressions can contain duplicates however, please see the note on collections and duplicates.


Examples

Suppose the following model with only instances of entity Person. Person.hasChildren is a relation from Person to Person.


Person instancePerson.NamePerson.AgePerson.hasChildren
Person_1“Kim”16
Person_2“Rick”38Person_3, Person_5
Person_3“Bob”8
Person_4“Julia”42Person_1
Person_5“Sam”3
Person_6“Joan”34Person_3, Person_5


To retrieve all the parent names, we use this expression:

COLLECT Parent.name 
FROM
  COLLECT Person 
  FROM ALL Person 
  WHERE ( Person.hasChildren != ? ) NAMED Parent


This expression results in “Rick”, “Julia” and “Joan”.

To retrieve all children names, we use this expression:

COLLECT Child
FROM (
  COLLECT Person.hasChildren 
  FROM ALL Person 
  WHERE ( Person.hasChildren != ? ) ) NAMED Child


This expression results in a collection of three instances of Person (i.e Person_1, Person_3 and Person_5, with names Kim, Bob and Sam).

To retrieve all children younger than 15, we use this expression:

COLLECT Child 
FROM (
  COLLECT Person.hasChildren
  FROM ALL Person 
  WHERE ( Person.hasChildren != ? ) ) NAMED Child
WHERE ( Child.Age < 15 )


This expression results in a collection of two instances of Person (i.e Person_3 and Person_5, with names Bob and Sam).

To retrieve the children’s names with a parent older than 40 years:

COLLECT Child.Name 
FROM (
  COLLECT Person.hasChildren 
  FROM ALL Person 
  WHERE ( Person.hasChildren != ? AND Person.Age > 40 ) ) NAMED Child  


This expression results in a collection with only the name “Kim”.

You can make it as complex as you like, for example:

COLLECT Child.Name 
FROM (
  COLLECT Parent.hasChildren
  FROM (
    COLLECT Person
    FROM ALL Person 
    WHERE ( Person.hasChildren != ? ) ) NAMED Parent
  WHERE ( Parent.Age > 40 )
) NAMED Child 
WHERE ( Child.Age < 18 ) 


This expression results also in a collection with only the name “Kim”.

In some occasions, a local variable could be a good alternative for using an alias. E.g. if you want a collection of all persons except the person that is currently active, this would be simple and transparent:

thisPerson := Person ;
 
COLLECT Person
    FROM ALL Person 
    WHERE ( Person != thisPerson )

Back to Top


UNPACK


Extracts the value from a single valued collection or list. Is the inverse of the LIST function.


Syntax

UNPACK ( collection/list )


Input
  • collection/list - A collection or list of one entity or attribute instance.


Return type

  • entity instance
  • attribute value of any type


Examples

Suppose the following data model.


Person.namePerson.SequenceNumber
“Bob”654
“Jane”523
“Mary”667
“Rick”500
“Ron”490
“Jenny”765
  • UNPACK ( COLLECT Person.name FROM ALL Person WHERE ( Person.SequenceNumber = MIN ( COLLECT Person.SequenceNumber FROM ALL Person ) ) ) = “Ron”

  • In case a second entry "Ron","490" exists, the expression UNPACK ( COLLECT Person.name FROM ALL Person WHERE ( Person.SequenceNumber = MIN ( COLLECT Person.SequenceNumber FROM ALL Person ) ) ) will fail, because the UNPACK cannot resolve a list with two elements. To solve this the UNIQUE function has to be used: UNPACK ( UNIQUE ( COLLECT Person.name FROM ALL Person WHERE ( Person.SequenceNumber = MIN ( COLLECT Person.SequenceNumber FROM ALL Person ) ) ) ) = "Ron".
  • UNPACK ( COLLECT Person.name FROM ALL Person WHERE ( Person.SequenceNumber = MAX ( COLLECT Person.SequenceNumber FROM ALL Person ) ) ) = “Jenny”

Back to Top


LIST


Creates a list based on a value. Is the inverse of the UNPACK function.


Syntax

LIST ( attribute/collection )



Inputs
  • attribute - An attribute of any data type.
  • collection/list - A collection or list of values.


Return type

  • a multivalued list with entries of any type


Examples

ExpressionResultType
LIST ( "Blueriq" )[ "Blueriq" ]String (multivalued)
LIST ( 5 )[ 5 ]Integer (multivalued)
LIST ( ? )[]Any (multivalued)


Suppose the following data model.


Person.namePerson.SequenceNumber
“Bob”654
“Jane”523
“Mary”667
“Rick”500
“Ron”490
“Jenny”?
  • LIST ( COLLECT Person.name FROM ALL Person ) = [ "Bob" , "Jane" , "Mary" , "Rick" , "Ron" , "Jenny" ]
    • In this example the LIST function adds no value, as the result of the COLLECT is already a list.
  • LIST ( COLLECT Person.SequenceNumber FROM ALL Person ) = []
    • As Jenny has an unknown sequence number, the result of the COLLECT expression is ?. The LIST function creates an empty list in case the parameter has ? value.

Back to Top


SIZE


Determines the size of a collection.


Syntax

SIZE ( collection )



Inputs
  • collection - A collection of attribute or entity instances. This can be an expression or a relation attribute for instance.


Return type

  • integer


Example

Suppose you have a Parent and a Child entity, where Parent has a multivalued relation with Child via the relation Parent.has_Children. With this model the following instances are created:


Parent instanceChild instanceChild.nameChild.hobbies
Parent_1Child_1“Kim”“Reading”, “Dancing”
Parent_1Child_2“Rick”“Tennis”, “Dancing”
Parent_1Child_3“Bob”“Painting”, “Basketball”, “Reading”
Parent_2Child_4“Mary”“Football”

Then:

Active instanceExpressionResultType
Parent_1SIZE ( Parent.has_Children )3Integer
Parent_2SIZE ( Parent.has_Children )1Integer
noneSIZE ( Parent.has_Children )Error
Child_1SIZE ( Child.hobbies )2Integer
Child_3SIZE ( Child.hobbies )3Integer
Child_4SIZE ( Child.hobbies )1Integer

SIZE( ? )0Integer

SIZE and COUNT are similar except for ?:

SIZE ( ? ) results in 0, while COUNT ( ? ) results in ?


UNIQUE


Filters duplicate items from a collection resulting from a subexpression in a larger expression. An expression resulting in a collection never contains duplicate values. A subexpression with the COLLECT statement however, can contain duplicates. See the note on collections and duplicates for more info.


Syntax

 UNIQUE ( collection )


Input
  • collection - A collection of attribute or entity instances.


Return type

  • collection


Examples

Suppose the following model.


Person instancePerson.name
Person_1“Kim”
Person_2“Rick”
Person_3“Bob”
Person_4“Rick”
ExpressionResultTypeNote
COLLECT Person.name FROM ALL Person[ "Kim" , "Rick" , "Bob" ]String (multivalued)A result never contains duplicate values
SIZE ( COLLECT Person.name FROM ALL Person )4IntegerA subexpression can contain duplicate values
SIZE ( UNIQUE ( COLLECT Person.name FROM ALL Person ) )3IntegerThe collection holds three unique values



Back to Top


SUBSET OF


Returns TRUE if the items in a collection are all present in another collection.


Syntax

 collection1 SUBSET OF collection2


Inputs
  • collection1 - The collection that is tested to be a subset of the second collection.
  • collection2 - The collection that is tested to hold all the items in the first collection.


Return type

  • boolean


Venn diagram


Examples

ExpressionResultTypeNote
( 'a' , 'b' , 'c' ) SUBSET OF ( 'a' , 'b' , 'c' , 'd' )TRUEboolean
( 'a' , 'b' , 'c' , 'd' ) SUBSET OF ( 'a' , 'b' , 'c' )FALSEboolean
Person.hobbies SUBSET OF [ "Tennis" , "Soccer" , "Music" ]
Given Person.hobbies = [ "Tennis" , "Soccer" ]
TRUEboolean
Person.hobbies SUBSET OF [ "Tennis" , "Soccer" , "Music" ]
Given Person.hobbies = [ ? ]
?boolean
Person.hobbies SUBSET OF [ "Tennis" , "Soccer" , ? ]
Given Person.hobbies = [ "Tennis" , "Soccer" ]
FALSEboolean[ "Tennis" , "Soccer" , ? ] evaluates to [ ? ]
Person.hobbies SUBSET OF [ ? ] = FALSE
( 'a' , 'b' , 'c' ) SUBSET OF ( [ 'a' , 'b' , 'c' , 'd' ] )Error

1 SUBSET OF Address.Numbers

Given Adress.Numbers = ?

FALSEboolean

Values between single quotes are considered value list items. For backwards compatibility reasons, a comma separated sequence of value list items is treated as a collection. That's why there is no need to enclose the values between square brackets. In fact if you do add the square brackets you create a matrix rather than a list.


Back to Top


UNION


Adds two collections of the same base type to a new collection.


Syntax

 UNION ( collection1 , collection2 )



Inputs
  • collection1 - First collection to be added to the new collection.
  • collection2 - Second collection to be added to the new collection.


Return type

  • collection


Venn diagram



Examples

Suppose you have a Parent and a Child entity, where Parent has a multivalued relation with Child via the relation Parent.has_Children. With this model the following instances are created:


Parent instanceChild instanceChild.nameChild.ageChild.hobbies
Parent_1Child_1“Kim”11“Reading”, “Dancing”
Parent_1Child_2“Rick”9“Tennis”, “Dancing”
Parent_1Child_3“Bob”?“Painting”, “Basketball”, “Reading”
Parent_2Child_4“Mary”10“Football”
ExpressionResultTypeRemarks
UNION ( Parent[Parent_1].has_Children ,
Parent[Parent_2].Has_Children )
[ Child_1, Child_2 , Child_3, Child_4 ]Collection of Child instances
UNION ( Parent[Parent_1].has_Children.name ,
Parent[Parent_2].Has_Children.name )
[ “Kim”, “Rick”, “Bob”, “Mary” ]String (multivalued)
UNION ( Child[Child_2].hobbies , "Reading" )[ “Tennis”, “Dancing”, “Reading” ]String (multivalued)
UNION ( Child[Child_1].hobbies , Child[Child_2].hobbies )[ “Reading”, “Dancing”, “Tennis” ]String (multivalued)
UNION( Child[Child_3].age, [1, 2] )UNKNOWNInteger (multivalued)Since the age of Child_3 is unknown, the expression will result in unknown.
UNION( LIST(Child[Child_3].age), [1, 2] )

[1, 2]

Integer (multivalued)The result of LIST(Child[Child_3].age) is an empty list. 


Back to Top


INTERSECTION


Determines the intersection of two collections. It returns a collection containing the items that are present in both specified collections.


Syntax

INTERSECTION ( collection1 , collection2 )



Inputs
  • collection1, collection2 - Collections to be intersected. These collections must be of the same base type.


Return type

  • collection


Venn diagram



Examples

Suppose the following model. Entity Teacher has a multivalued relation with entity Child via the relation Teacher.teaches_Children.


Teacher instanceChild instanceChild.nameChild.hobbies
Teacher_1Child_1“Kim”“Reading”, “Dancing”
Teacher_1Child_2“Rick”“Tennis”, “Dancing”
Teacher_1Child_3“Bob”“Painting”, “Basketball”, “Reading”
Teacher_2Child_1“Kim”“Reading”, “Dancing”
Teacher_2Child_3“Bob”“Painting”, “Basketball”, “Reading”
Teacher_2Child_4“Mary”“Football”
ExpressionResultType
INTERSECTION ( Teacher[Teacher_1].teaches_Children , Teacher[Teacher_2].teaches_Children )[ Child_1 , Child_3 ]Collection of Child instances
INTERSECTION ( Teacher[Teacher_1].teaches_Children.name , Teacher[Teacher_2].teaches_Children.name )[ "Kim" , "Bob" ]String (multivalued)
INTERSECTION ( Child[Child_1].hobbies , Child[Child_3].hobbies )[ "Reading" ]String (multivalued)
INTERSECTION ( Child[Child_2].hobbies , Child[Child_3].hobbies )[ ]String (multivalued)
INTERSECTION( ?, [ 1, 2 ] )UNKNOWNInteger (multivalued)


Back to Top



DIFFERENCE


Determines the difference between 2 collections. Returns a collection containing all the items from collection1 that are not present in collection2.


Syntax

DIFFERENCE ( collection1 , collection2 )


Inputs
  • collection1, collection2 - Collections to be compared. These collections must be of the same base type.


Return type

  • collection


Venn diagram



Examples

ExpressionResultType
DIFFERENCE ( [ "a", "b", "c"] , [ "c", "d", "e" ] )[ "a" , "b" ]String (multivalued)
DIFFERENCE ( [ "nv" , "bv" ] , [ "NV" ] )[ "bv" ]String (multivalued)
DIFFERENCE ( 1 , 1 )[ ]Integer (multivalued)
DIFFERENCE( [ 1, 2 ], ? )?Integer (multivalued)

See also Collection function SYMMETRIC_DIFFERENCE




SYMMETRIC_DIFFERENCE


Determines the symmetric difference between two collections. Returns a collection with the elements of the provided collections which are in either one of the collections, but not in both.


Syntax

SYMMETRIC_DIFFERENCE ( collection1 , collection2 )


Inputs
  • collection1, collection2 - Collections to be compared. These collections must be of the same base type.


Return type

  • collection


Venn diagram



Examples

ExpressionResultType
SYMMETRIC_DIFFERENCE ( [ "a" , "b" , "c" ] , [ "c" , "d" , "e" ] )[ "a", "b", "d", "e" ]String (multivalued)
SYMMETRIC_DIFFERENCE ( [ "nv" , "bv" ] , [ "NV" ] )[ "bv" ]String (multivalued)
SYMMETRIC_DIFFERENCE ( 1 , 1 )[ ]Integer (multivalued)



Back to Top


A note on collections and duplicates

An expression resulting in a collection does not contain duplicates. Please be aware however, that intermediary results of a COLLECT statement can contain duplicates. You have to be aware of this when using the SIZE or UNPACK function, or when using TSL.

This is best illustrated with the following examples.

Person instancePerson.NamePerson.Age
Person_1Kim24
Person_2Rick25
Person_3Bob25
ExpressionResultTypeNote
COLLECT Person.Age FROM ALL Person[ 24 , 25 ]String (multivalued)
SIZE ( COLLECT Person.Age FROM ALL Person )3IntegerThe intermediary collection is [ 24, 25, 25 ]

 TSL:

The ages present are: [[[ COLLECT Person.Age FROM ALL Person ]]].

The ages present are: 24, 25, 25.StringThe intermediary collection is [ 24, 25, 25 ]
SIZE ( UNIQUE ( COLLECT Person.Age FROM ALL Person ) )2IntegerThe duplicates in the intermediary collection are filtered by the UNIQUE function


Now an example with the UNPACK function. We leave out the first instance from the previous example.

Person instancePerson.NamePerson.Age
Person_2Rick25
Person_3Bob25
ExpressionResultTypeNote
COLLECT Person.Age FROM ALL Person[ 25 ]Integer (multivalued)
UNPACK ( COLLECT Person.Age FROM ALL Person )Error
The collection contains 2 elements [ 25 , 25 ]
UNPACK ( UNIQUE ( COLLECT Person.Age FROM ALL Person ) )25


 Only the intermediary results of a COLLECT statement can contain duplicates. The functions UNIQUE, UNION, INTERSECTION, DIFFERENCE and SYMMETRIC_DIFFERENCE always return collections without duplicates.

Back to Top


27 Comments

  1. In some occasions, a local variable could be a good alternative for using an alias
    In a condition node, a local valiable cannot be used. Why not? 
    Please elaborate some more in which cases local variables can/cannot be used, and why.
  2. At the section UNPACK, examples are missing, though the text suggests that they should be there.

    1. Fixed it; it had gone missing somehow

  3. Within our project we are trying to check the number of seconds a migration of aggregates takes before it is done for a specific set. We want to add the results to a multivalued attribute using a UNION statement. Is it true that the UNION statement only stores unique values in the string? If we use the same for adding timestamps it works fine since they are unique.


    Is it possible to use a UNION statement to store all returned values even if they already exist in target attribute?

    1. No, it is not possible to store duplicate values using a UNION statement, as the result of a UNION will always filter out duplicates. See also this section: A note on collections and duplicates.

  4. Is it possible to expand this article with behaviour of functions and expected return values? When modelling we encountered that each function behaves differently in how many arguments it returns. For example:

    entityAgeCategory
    Person1181
    Person2182
    Person3182
    Person4182

    Collect person.age from all person will result in [ 18 ]

    Size(collect person.age from all person) will result in a 4

    Union((Collect person.age from all person where person.category =1), Collect person.age from all person where person.category =2)) will result in [ 18 ]

    Size(Union((Collect person.age from all person where person.category =1), Collect person.age from all person where person.category =2))) Will result in 3

    As you can see, sometimes functions will result in a answer which isn't entirely expected. Why for example will Collect person.age from all person not return [ 18, 18, 18, 18 ]


    If possible, please expand this article with how collections will be compressed. Sometimes the answer is obvious, but it might not always be apparent that size(union([ 18 ], [ 18, 18, 18 ])) will be 3 (instead of 1).

    1. It is allready there: Have a look at  A note on collections and duplicates.

      1. Hey Han! I've seen this part, but I feel it's incomplete. For example, from reading this I wouldn't know what UNION's properties are.

        1. In general, the UNION and other collection functions are implemented as sets. As can be read in the note on collections and duplicates, intermediate results behave different, and cannot be trusted as being sets. I think it would be nice if the tooling would be fixed for this. Now, the Blueriq modeller has to cope with this. 

  5. I noticed this remark: 

    Values between single quotes are considered value list items.  For backwards compatibility reasons, a comma separated sequence of value list items is treated as a collection.........


    This leads me to two questions:

    1. What does that first sentence even mean? How does a value in an expression behave differently depending on it being considered a value list item or not?
    2. Backwards compatibility smells like bad practice to still use it. Am I correct in assuming that it's best practice to use ["A","B"] rather than 'A', 'B' at this time?


    1. Since it is treated as a value list item, a validation rule is triggered.

      Entity.Attribute = 'ValueInDomain','ValueOutsideDomain'  will give a validation error,

      Entity.Attribute = ["ValueInDomain","ValueOutsideDomain"] will not.



      1. That would be really curious behavior as this would not allow you to get a negative result in a comparison. Only a TRUE or an error.


        I've tried a simple expression in our debugbar:

        "a" = 'b', 'c'


        As per your comment I would expect an error, however it just returns FALSE. This returns neither an error at runtime in the log nor a validation error at designtime.


        1. That's not exactly what I meant. The domain check only makes sense if you compare it with an attribute. If it's an expression (like "a" in your example) it's an open domain. i.e. any value is possible as long as it has the correct datatype.

          1. Still just a valid FALSE if I do in the debugbar:

            Dossier.Product = 'ADP', 'VVB'

            Where the the attribute has a valuelist containing both options after the =, but the value isnt ADP or VVB.


            1. The domain validation I'm talking about is a studio-validation, so that won't affect the debug bar.  I spoke too soon here


              Your example uses valid domain values so no error is thrown. Nothing unusual there.

              1. So, I would have to give an attribute with a value list a value that is invalid in the actual value list, then compare it to a collection written to behave like a value list to get a validation error is what you're saying?

                1. Try to evaluate: Dossier.Product = 'ADP','VVB','nonexistingproduct' 

                  This will give an error since nonexistingproduct is not in your valuelist.


                  Dossier.Product =  ['ADP','VVB','nonexistingproduct'] will evaluate to FALSE

                  1. Aaaaaah. Now I get it (smile) Thanks for the clarification.  This is not something I would understand from the way the documentation is currently written, nor do I find it intuitive. 

  6. Hi! I'm looking for a way to determine whether all values from L1 can be found in L2 and the other way around, so basically the symettric difference, but with taking into account the duplicates. Is there any simple way to do this, without having to check every single value with the other list? 

    For example:

    L1 = [1,2,3,3,4], L2 = [1,2,3,4,4] → Result should be either FALSE or a List [3,4] (so I can check the SIZE of the result)

    L1 = [1,2,3,4,4], L2 = [1,2,3,4]  → Result should be FALSE or a List [4]

    L1 =[1,2,3,3,4], L2 = [3,2,4,3,1] → Result should be TRUE or an EMPTY LIST

    1. I don't think there's an easy way to do it.

      It might not be possible at all, since duplicates are removed as soon as an attribute value is derived.  


      Maybe if you tell the underlying problem that you are trying to solve, we can come up with a different approach.

      1. So the problem is as follows:
        We have an application that has employments (entity) and a document that contains information about a set of employments (the "employments" on the documents are a different entity).
        We have no certain way to match the employments from the document with the employments in the applictation. We do however want to know whether certain fields in the document are used in the application, as the document contains "the truth" about the applicants employments. A seperate check for every field is made, so we can inform the user where they have to review the data.
        The problem we have here is the check for the Dutch KvK-number. We want to check whether all KvK-numbers that are mentioned in the document are being used in the application and that all KvK-numbers in the application can be found in the document. In it's simplest form this can be done without checking for duplicates. However if you have two seperate employments at the same employer (for example you have two different functions with different contracts at the same employer, or you are both employee and owner of a company), you will get duplicates in the KvK-numbers.
        We would like to be able to check whether all KvK-numbers for all seperate employments in the document can be found in the application. If you have the rare case of 3 employments in your document where 2 of them have the same KvK-number ( 1,2,2 ), we want to make sure that you will also have 3 employments in your application where 2 of them will have the same KvK-number (1,2,2). 1,1,2 should not be allowed, because that doesn't agree with the information in the document.
        This method would of course still fail if the user shuffles all numbers around, but that is an accepted risk as a result of not directly matching the employments in the document with the employments in the application.

        1. Unknown User (a.jansen)

          Hi, Louis Wouters, thanks for the great follow-up question, and Nienke Nobel , thanks for supplying some additional info. Could a relatively simple solution to your problem be to Text functions#CONCATENATE the KvK numbers alphabetically into a String attribute using a flow with repeat expression sorted alphabetically on the KvK numbers, and then comparing the 2 strings using Text functions#EQUALS ? If not, please let us know. We would gladly continue to think of alternate solutions. 

          1. Hi Aäron. Thanks for the suggestion! This will probably work! Unfortunatly most of our checks are done "realtime" in decision tables", so this would deviate from that, and we will have to use a FlowOnRefresh. Unless you know a way to sort a List or String in an expression?

            1. New idea: Would it maybe be possible to COUNT all occurences for every value using FOREACH in both lists and say they have to be the same?

              1. What I would do:

                Make an attribute in Employment called "InDocument" with default expression:

                ThisEmployment := Employment

                COUNT( COLLECT Employment FROM ALL Employment WHERE( Employment.KvKNumber = ThisEmployment.KvKNumber )  )

                =

                COUNT( COLLECT EmploymentDocument FROM ALL EmploymentDocument WHERE( EmploymentDocument.KvKNumber = ThisEmployment.KvKNumber ) )


                Then you can make an attribute like Document.Complete with default

                EACH Employment WHERE( Employment.InDocument )

                AND

                COUNT( ALL EmploymentDocument ) = COUNT( ALL Employment )

                  1. Unknown User (a.jansen)

                    Wow Louis Wouters, great stuff! Nienke Nobel: if somehow this still doesn't solve your problem or if you have any more interesting questions, puzzles or challenges, please let us know. (smile)