Skip to content

Commit

Permalink
Optimize contains() on large arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
larsga committed Jan 28, 2019
1 parent 9ed4436 commit e5eddd7
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

// Copyright 2018 Schibsted Marketplaces Products & Technology As
//
// 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 com.schibsted.spt.data.jslt.impl;

import com.schibsted.spt.data.jslt.Function;

public abstract class AbstractFunction extends AbstractCallable implements Function {

public AbstractFunction(String name, int min, int max) {
super(name, min, max);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,6 @@ public class BuiltinFunctions {
macros.put("fallback", new BuiltinFunctions.Fallback());
}

private static abstract class AbstractFunction extends AbstractCallable implements Function {

public AbstractFunction(String name, int min, int max) {
super(name, min, max);
}
}

private static abstract class AbstractMacro extends AbstractCallable implements Macro {

public AbstractMacro(String name, int min, int max) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,25 @@ public JsonNode apply(Scope scope, JsonNode input) {
else
return function.call(input, params);
}

private static final int OPTIMIZE_ARRAY_CONTAINS_MIN = 10;
public ExpressionNode optimize() {
super.optimize();

// if the second argument to contains() is an array with a large
// number of elements, don't do a linear search. instead, use an
// optimized version of the function that uses a HashSet
if (function == BuiltinFunctions.functions.get("contains") &&
arguments.length == 2 &&
(arguments[1] instanceof LiteralExpression)) {

JsonNode v = arguments[1].apply(null, null);
if (v.isArray() && v.size() > OPTIMIZE_ARRAY_CONTAINS_MIN) {
// we use resolve to make sure all references are updated
resolve(new OptimizedStaticContainsFunction(v));
}
}

return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

// Copyright 2019 Schibsted Marketplaces Products & Technology As
//
// 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 com.schibsted.spt.data.jslt.impl;

import java.util.Set;
import java.util.HashSet;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.BooleanNode;

/**
* An optimized version of contains(a, b) which is used when b is an
* array literal with a large number of values, so that a linear
* search becomes a performance drag.
*/
public class OptimizedStaticContainsFunction extends AbstractFunction {
private Set<JsonNode> values;

public OptimizedStaticContainsFunction(JsonNode array) {
super("optimized-static-contains", 2, 2);

this.values = new HashSet();
for (int ix = 0; ix < array.size(); ix++)
values.add(array.get(ix));
}

public JsonNode call(JsonNode input, JsonNode[] arguments) {
if (values.contains(arguments[0]))
return BooleanNode.TRUE;
else
return BooleanNode.FALSE;
}
}

0 comments on commit e5eddd7

Please sign in to comment.