Skip to content

Commit

Permalink
#3948 Add and and or functions
Browse files Browse the repository at this point in the history
  • Loading branch information
stroomdev66 committed Dec 14, 2023
1 parent 0defaff commit 1d44bcd
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2017 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package stroom.query.language.functions;

import stroom.query.language.functions.ref.StoredValues;
import stroom.query.language.token.Param;

import java.text.ParseException;
import java.util.function.Supplier;

@SuppressWarnings("unused") //Used by FunctionFactory
@FunctionDef(
name = And.NAME,
commonCategory = FunctionCategory.LOGIC,
commonReturnType = ValBoolean.class,
commonReturnDescription = "If all supplied arguments evaluate to true then return true else false.",
signatures = @FunctionSignature(
description = "Logical 'and' operator.",
args = {
@FunctionArg(
name = "boolean1",
description = "First boolean argument.",
argType = ValBoolean.class),
@FunctionArg(
name = "booleanN",
description = "Additional boolean arguments.",
argType = ValBoolean.class)}))
class And extends AbstractManyChildFunction {

static final String NAME = "and";

public And(final String name) {
super(name, 1, Integer.MAX_VALUE);
}

@Override
public void setParams(final Param[] params) throws ParseException {
super.setParams(params);
}

@Override
protected Generator createGenerator(final Generator[] childGenerators) {
return new Gen(childGenerators);
}

@Override
public Type getCommonReturnType() {
return Type.BOOLEAN;
}

private static final class Gen extends AbstractManyChildGenerator {

Gen(final Generator[] childGenerators) {
super(childGenerators);
}

@Override
public Val eval(final StoredValues storedValues, final Supplier<ChildData> childDataSupplier) {
try {
for (final Generator generator : childGenerators) {
if (generator == null) {
return ValBoolean.FALSE;
}
final Val val = generator.eval(storedValues, childDataSupplier);
if (val == null || !val.type().isValue() || val.toBoolean() == null || !val.toBoolean()) {
return ValBoolean.FALSE;
}
}
return ValBoolean.TRUE;
} catch (final RuntimeException e) {
return ValErr.create(e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;

public class ExpressionParser {
Expand Down Expand Up @@ -63,6 +64,11 @@ public class ExpressionParser {
TokenType.PLUS,
TokenType.MINUS);

private static final Set<TokenType> LOGICAL_OPERATORS = Set.of(
TokenType.NOT,
TokenType.OR,
TokenType.AND);

private final ParamFactory paramFactory;

public ExpressionParser(final ParamFactory paramFactory) {
Expand Down Expand Up @@ -125,13 +131,14 @@ private List<Param> processObjects(final List<AbstractToken> tokens,
} else if (token instanceof final KeywordGroup keywordGroup) {

// Add a special case for the NOT keyword that can exist as a function in the context of an expression.
if (keywordGroup.getTokenType().equals(TokenType.NOT)
if (LOGICAL_OPERATORS.contains(keywordGroup.getTokenType())
&& keywordGroup.getChildren().size() == 1
&& keywordGroup.getChildren().get(0) instanceof final TokenGroup tokenGroup) {
final String functionName = keywordGroup.getTokenType().toString().toLowerCase(Locale.ROOT);
final Function function = getFunction(
keywordGroup,
tokenGroup.getChildren(),
"not",
functionName,
expressionContext,
fieldIndex);
output.add(function);
Expand Down Expand Up @@ -425,8 +432,8 @@ private List<Object> applySigns(final List<Object> objects,
}

public Param applyBODMAS(final List<Object> objects,
final ExpressionContext expressionContext,
final FieldIndex fieldIndex) {
final ExpressionContext expressionContext,
final FieldIndex fieldIndex) {
boolean complete = false;
List<Object> result = new ArrayList<>(objects);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2017 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package stroom.query.language.functions;

import stroom.query.language.functions.ref.StoredValues;
import stroom.query.language.token.Param;

import java.text.ParseException;
import java.util.function.Supplier;

@SuppressWarnings("unused") //Used by FunctionFactory
@FunctionDef(
name = Or.NAME,
commonCategory = FunctionCategory.LOGIC,
commonReturnType = ValBoolean.class,
commonReturnDescription = "If one or more of the supplied arguments evaluate to true then return true else false.",
signatures = @FunctionSignature(
description = "Logical 'or' operator.",
args = {
@FunctionArg(
name = "boolean1",
description = "First boolean argument.",
argType = ValBoolean.class),
@FunctionArg(
name = "booleanN",
description = "Additional boolean arguments.",
argType = ValBoolean.class)}))
class Or extends AbstractManyChildFunction {

static final String NAME = "or";

public Or(final String name) {
super(name, 1, Integer.MAX_VALUE);
}

@Override
public void setParams(final Param[] params) throws ParseException {
super.setParams(params);
}

@Override
protected Generator createGenerator(final Generator[] childGenerators) {
return new Gen(childGenerators);
}

@Override
public Type getCommonReturnType() {
return Type.BOOLEAN;
}

private static final class Gen extends AbstractManyChildGenerator {

Gen(final Generator[] childGenerators) {
super(childGenerators);
}

@Override
public Val eval(final StoredValues storedValues, final Supplier<ChildData> childDataSupplier) {
try {
for (final Generator generator : childGenerators) {
if (generator != null) {
final Val val = generator.eval(storedValues, childDataSupplier);
if (val != null && val.type().isValue() && val.toBoolean() != null && val.toBoolean()) {
return ValBoolean.TRUE;
}
}
}
return ValBoolean.FALSE;
} catch (final RuntimeException e) {
return ValErr.create(e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package stroom.query.language.functions;

import java.util.stream.Stream;

class TestAnd extends AbstractFunctionTest<And> {

@Override
Class<And> getFunctionType() {
return And.class;
}

@Override
Stream<TestCase> getTestCases() {
return Stream.of(
TestCase.of(
"both false",
ValBoolean.FALSE,
ValBoolean.FALSE,
ValBoolean.FALSE),
TestCase.of(
"one false",
ValBoolean.FALSE,
ValBoolean.FALSE,
ValBoolean.TRUE),
TestCase.of(
"both true",
ValBoolean.TRUE,
ValBoolean.TRUE,
ValBoolean.TRUE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,42 @@ void testNotFalse() {
out -> assertThat(out.toBoolean()).isTrue());
}

@Test
void testAnd1() {
compute("and(false(), false())",
out -> assertThat(out.toBoolean()).isFalse());
}

@Test
void testAnd2() {
compute("and(false(), true())",
out -> assertThat(out.toBoolean()).isFalse());
}

@Test
void testAnd3() {
compute("and(true(), true())",
out -> assertThat(out.toBoolean()).isTrue());
}

@Test
void testOr1() {
compute("or(false(), false())",
out -> assertThat(out.toBoolean()).isFalse());
}

@Test
void testOr2() {
compute("or(false(), true())",
out -> assertThat(out.toBoolean()).isTrue());
}

@Test
void testOr3() {
compute("or(true(), true())",
out -> assertThat(out.toBoolean()).isTrue());
}

@Test
void testIf1() {
compute("if(true(), 'this', 'that')",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package stroom.query.language.functions;

import java.util.stream.Stream;

class TestOr extends AbstractFunctionTest<Or> {

@Override
Class<Or> getFunctionType() {
return Or.class;
}

@Override
Stream<TestCase> getTestCases() {
return Stream.of(
TestCase.of(
"both false",
ValBoolean.FALSE,
ValBoolean.FALSE,
ValBoolean.FALSE),
TestCase.of(
"one true",
ValBoolean.TRUE,
ValBoolean.FALSE,
ValBoolean.TRUE),
TestCase.of(
"both true",
ValBoolean.TRUE,
ValBoolean.TRUE,
ValBoolean.TRUE));
}
}
24 changes: 24 additions & 0 deletions unreleased_changes/20231214_144628_780__3948.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
* Issue **#3948** : Add `and` and `or` functions.


```sh
# ********************************************************************************
# Issue title: Expressions, how do I implement an `and` or `or` condition
# Issue link: https://github.com/gchq/stroom/issues/3948
# ********************************************************************************

# ONLY the top line will be included as a change entry in the CHANGELOG.
# The entry should be in GitHub flavour markdown and should be written on a SINGLE
# line with no hard breaks. You can have multiple change files for a single GitHub issue.
# The entry should be written in the imperative mood, i.e. 'Fix nasty bug' rather than
# 'Fixed nasty bug'.
#
# Examples of acceptable entries are:
#
#
# * Issue **123** : Fix bug with an associated GitHub issue in this repository
#
# * Issue **namespace/other-repo#456** : Fix bug with an associated GitHub issue in another repository
#
# * Fix bug with no associated GitHub issue.
```

0 comments on commit 1d44bcd

Please sign in to comment.