Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
apoc.map.Maps Maven / Gradle / Ivy
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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 apoc.map;
import static java.util.regex.Pattern.quote;
import apoc.util.Util;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.graphdb.*;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.UserFunction;
public class Maps {
@Context
public Transaction tx;
@UserFunction("apoc.map.groupBy")
@Description("Creates a `MAP` of the `LIST` keyed by the given property, with single values.")
public Map groupBy(@Name("values") List values, @Name("key") String key) {
Map result = new LinkedHashMap<>(values.size());
for (Object value : values) {
Object id = getKey(key, value);
if (id != null) result.put(id.toString(), value);
}
return result;
}
@UserFunction("apoc.map.groupByMulti")
@Description("Creates a `MAP` of the `LIST` values keyed by the given property, with the `LIST` values.")
public Map> groupByMulti(@Name("values") List values, @Name("key") String key) {
Map> result = new LinkedHashMap<>(values.size());
for (Object value : values) {
Object id = getKey(key, value);
if (id != null)
result.compute(id.toString(), (k, list) -> {
if (list == null) list = new ArrayList<>();
list.add(value);
return list;
});
}
return result;
}
public Object getKey(@Name("key") String key, Object value) {
Object id = null;
if (value instanceof Map) {
id = ((Map) value).get(key);
}
if (value instanceof Entity) {
id = ((Entity) value).getProperty(key, null);
}
return id;
}
@UserFunction("apoc.map.fromNodes")
@Description("Returns a `MAP` of the given prop to the node of the given label.")
public Map fromNodes(@Name("label") String label, @Name("prop") String property) {
Map result = new LinkedHashMap<>(10000);
try (ResourceIterator nodes = tx.findNodes(Label.label(label))) {
while (nodes.hasNext()) {
Node node = nodes.next();
Object key = node.getProperty(property, null);
if (key != null) {
result.put(key.toString(), node);
}
}
}
return result;
}
@UserFunction("apoc.map.fromPairs")
@Description("Creates a `MAP` from the given `LIST>` of key-value pairs.")
public Map fromPairs(@Name("pairs") List> pairs) {
return Util.mapFromPairs(pairs);
}
@UserFunction("apoc.map.fromLists")
@Description("Creates a `MAP` from the keys and values in the given `LIST` values.")
public Map fromLists(@Name("keys") List keys, @Name("values") List values) {
return Util.mapFromLists(keys, values);
}
@UserFunction("apoc.map.values")
@Description("Returns a `LIST` indicated by the given keys (returns a null value if a given key is missing).")
public List values(
@Name("map") Map map,
@Name(value = "keys", defaultValue = "[]") List keys,
@Name(value = "addNullsForMissing", defaultValue = "false") boolean addNullsForMissing) {
if (keys == null || keys.isEmpty()) return Collections.emptyList();
List values = new ArrayList<>(keys.size());
for (String key : keys) {
if (addNullsForMissing || map.containsKey(key)) values.add(map.get(key));
}
return values;
}
@UserFunction("apoc.map.fromValues")
@Description("Creates a `MAP` from the alternating keys and values in the given `LIST`.")
public Map fromValues(@Name("values") List values) {
return Util.map(values);
}
@UserFunction("apoc.map.merge")
@Description("Merges the two given `MAP` values into one `MAP`.")
public Map merge(
@Name("map1") Map first, @Name("map2") Map second) {
return Util.merge(first, second);
}
@UserFunction("apoc.map.mergeList")
@Description("Merges all `MAP` values in the given `LIST>` into one `MAP`.")
public Map mergeList(@Name("maps") List> maps) {
Map result = new LinkedHashMap<>(maps.size());
for (Map map : maps) {
result.putAll(map);
}
return result;
}
@UserFunction("apoc.map.get")
@Description("Returns a value for the given key.\n"
+ "If the given key does not exist, or lacks a default value, this function will throw an exception.")
public Object get(
@Name("map") Map map,
@Name("key") String key,
@Name(value = "value", defaultValue = "null") Object value,
@Name(value = "fail", defaultValue = "true") boolean fail) {
if (fail && value == null && !map.containsKey(key))
throw new IllegalArgumentException("Key " + key + " is not of one of the existing keys " + map.keySet());
return map.getOrDefault(key, value);
}
@UserFunction("apoc.map.mget")
@Description("Returns a `LIST` for the given keys.\n"
+ "If one of the keys does not exist, or lacks a default value, this function will throw an exception.")
public List mget(
@Name("map") Map map,
@Name("keys") List keys,
@Name(value = "values", defaultValue = "[]") List values,
@Name(value = "fail", defaultValue = "true") boolean fail) {
if (keys == null || map == null) return null;
int keySize = keys.size();
List result = new ArrayList<>(keySize);
int valuesSize = values == null ? -1 : values.size();
for (int i = 0; i < keySize; i++) {
result.add(get(map, keys.get(i), i < valuesSize ? values.get(i) : null, fail));
}
return result;
}
@UserFunction("apoc.map.submap")
@Description("Returns a sub-map for the given keys.\n"
+ "If one of the keys does not exist, or lacks a default value, this function will throw an exception.")
public Map submap(
@Name("map") Map map,
@Name("keys") List keys,
@Name(value = "values", defaultValue = "[]") List values,
@Name(value = "fail", defaultValue = "true") boolean fail) {
if (keys == null || map == null) return null;
int keySize = keys.size();
Map result = new LinkedHashMap<>(keySize);
int valuesSize = values == null ? -1 : values.size();
for (int i = 0; i < keySize; i++) {
String key = keys.get(i);
result.put(key, get(map, key, i < valuesSize ? values.get(i) : null, fail));
}
return result;
}
@UserFunction("apoc.map.setKey")
@Description("Adds or updates the given entry in the `MAP`.")
public Map setKey(
@Name("map") Map map, @Name("key") String key, @Name("value") Object value) {
return Util.merge(map, Util.map(key, value));
}
@UserFunction("apoc.map.setEntry")
@Description("Adds or updates the given entry in the `MAP`.")
public Map setEntry(
@Name("map") Map map, @Name("key") String key, @Name("value") Object value) {
return Util.merge(map, Util.map(key, value));
}
@UserFunction("apoc.map.setPairs")
@Description("Adds or updates the given key/value pairs (e.g. [key1,value1],[key2,value2]) in a `MAP`.")
public Map setPairs(@Name("map") Map map, @Name("pairs") List> pairs) {
return Util.merge(map, Util.mapFromPairs(pairs));
}
@UserFunction("apoc.map.setLists")
@Description(
"Adds or updates the given keys/value pairs provided in `LIST` format (e.g. [key1, key2],[value1, value2]) in a `MAP`.")
public Map setLists(
@Name("map") Map map,
@Name("keys") List keys,
@Name("values") List values) {
return Util.merge(map, Util.mapFromLists(keys, values));
}
@UserFunction("apoc.map.setValues")
@Description("Adds or updates the alternating key/value pairs (e.g. [key1,value1,key2,value2]) in a `MAP`.")
public Map setValues(@Name("map") Map map, @Name("pairs") List pairs) {
return Util.merge(map, Util.map(pairs));
}
@UserFunction("apoc.map.removeKey")
@Description("Removes the given key from the `MAP` (recursively if recursive is true).")
public Map removeKey(
@Name("map") Map map,
@Name("key") String key,
@Name(value = "config", defaultValue = "{}") Map config) {
if (!map.containsKey(key)) {
return map;
}
return removeKeys(map, Collections.singletonList(key), config);
}
@UserFunction("apoc.map.removeKeys")
@Description("Removes the given keys from the `MAP` (recursively if recursive is true).")
public Map removeKeys(
@Name("map") Map map,
@Name("keys") List keys,
@Name(value = "config", defaultValue = "{}") Map config) {
Map res = new LinkedHashMap<>(map);
res.keySet().removeAll(keys);
Map checkedConfig = config == null ? Collections.emptyMap() : config;
boolean removeRecursively = Util.toBoolean(checkedConfig.getOrDefault("recursive", false));
if (removeRecursively) {
for (Iterator> iterator = res.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry entry = iterator.next();
if (entry.getValue() instanceof Map) {
Map updatedMap =
removeKeys((Map) entry.getValue(), keys, checkedConfig);
if (updatedMap.isEmpty()) {
iterator.remove();
} else if (!updatedMap.equals(entry.getValue())) {
entry.setValue(updatedMap);
}
} else if (entry.getValue() instanceof Collection) {
Collection values = (Collection) entry.getValue();
List updatedValues = values.stream()
.map(value -> value instanceof Map
? removeKeys((Map) value, keys, checkedConfig)
: value)
.filter(value -> value instanceof Map ? !((Map) value).isEmpty() : true)
.collect(Collectors.toList());
if (updatedValues.isEmpty()) {
iterator.remove();
} else {
entry.setValue(updatedValues);
}
}
}
}
return res;
}
@UserFunction("apoc.map.clean")
@Description("Filters the keys and values contained in the given `LIST` values.")
public Map clean(
@Name("map") Map map,
@Name("keys") List keys,
@Name("values") List values) {
HashSet keySet = new HashSet<>(keys);
HashSet valueSet = new HashSet<>(values);
LinkedHashMap res = new LinkedHashMap<>(map.size());
for (Map.Entry entry : map.entrySet()) {
Object value = entry.getValue();
if (keySet.contains(entry.getKey())
|| value == null
|| valueSet.contains(value)
|| valueSet.contains(value.toString())) continue;
res.put(entry.getKey(), value);
}
return res;
}
@UserFunction("apoc.map.updateTree")
@Description("Adds the data `MAP` on each level of the nested tree, where the key-value pairs match.")
public Map updateTree(
@Name("tree") Map tree, @Name("key") String key, @Name("data") List> data) {
Map> map = new HashMap<>(data.size());
for (List datum : data) {
if (datum.size() < 2 || !((datum.get(1) instanceof Map)))
throw new IllegalArgumentException("Wrong data list entry: " + datum);
map.put(datum.get(0), (Map) datum.get(1));
}
return visit(tree, (m) -> {
Map entry = map.get(m.get(key));
if (entry != null) {
m.putAll(entry);
}
return m;
});
}
Map visit(Map tree, Function, Map> mapper) {
Map result = mapper.apply(new LinkedHashMap<>(tree));
result.entrySet().forEach(e -> {
if (e.getValue() instanceof List) {
List list = (List) e.getValue();
List newList = list.stream()
.map(v -> {
if (v instanceof Map) {
Map map = (Map) v;
return visit(map, mapper);
}
return v;
})
.collect(Collectors.toList());
e.setValue(newList);
} else if (e.getValue() instanceof Map) {
Map map = (Map) e.getValue();
e.setValue(visit(map, mapper));
}
});
return result;
}
@UserFunction("apoc.map.flatten")
@Description("Flattens nested items in the given `MAP`.\n"
+ "This function is the reverse of the `apoc.map.unflatten` function.")
public Map flatten(
@Name("map") Map map, @Name(value = "delimiter", defaultValue = ".") String delimiter) {
Map flattenedMap = new HashMap<>();
flattenMapRecursively(flattenedMap, map, "", delimiter == null ? "." : delimiter);
return flattenedMap;
}
@SuppressWarnings("unchecked")
private void flattenMapRecursively(
Map flattenedMap, Map map, String prefix, String delimiter) {
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() instanceof Map) {
flattenMapRecursively(
flattenedMap,
(Map) entry.getValue(),
prefix + entry.getKey() + delimiter,
delimiter);
} else {
flattenedMap.put(prefix + entry.getKey(), entry.getValue());
}
}
}
@UserFunction("apoc.map.unflatten")
@Description("Unflattens items in the given `MAP` to nested items.\n"
+ "This function is the reverse of the `apoc.map.flatten` function.")
public Map unflatten(
@Name("map") Map map, @Name(value = "delimiter", defaultValue = ".") String delimiter) {
return unflattenMapRecursively(map, StringUtils.isBlank(delimiter) ? "." : delimiter);
}
private Map unflattenMapRecursively(Map inputMap, String delimiter) {
Map resultMap = new HashMap<>();
for (Map.Entry entry : inputMap.entrySet()) {
unflatEntry(resultMap, entry.getValue(), entry.getKey(), delimiter);
}
return resultMap;
}
public static void unflatEntry(Map map, Object value, String key, String delimiter) {
final String[] keys = key.split(quote(delimiter), 2);
final String firstPart = keys[0];
if (keys.length == 1) {
map.put(firstPart, value);
} else {
final Map currentMap =
(Map) map.computeIfAbsent(firstPart, k -> new HashMap());
unflatEntry(currentMap, value, keys[1], delimiter);
}
}
@UserFunction("apoc.map.sortedProperties")
@Description("Returns a `LIST` of key/value pairs.\n"
+ "The pairs are sorted by alphabetically by key, with optional case sensitivity.")
public List> sortedProperties(
@Name("map") Map map,
@Name(value = "ignoreCase", defaultValue = "true") boolean ignoreCase) {
List> sortedProperties = new ArrayList<>();
List keys = new ArrayList<>(map.keySet());
if (ignoreCase) {
Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);
} else {
Collections.sort(keys);
}
for (String key : keys) {
sortedProperties.add(Arrays.asList(key, map.get(key)));
}
return sortedProperties;
}
}