
apoc.load.LoadXls Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apoc Show documentation
Show all versions of apoc Show documentation
A collection of useful Neo4j Procedures
/*
* 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.load;
import static apoc.export.xls.ExportXlsHandler.XLS_MISSING_DEPS_ERROR;
import static apoc.load.LoadXlsHandler.getXlsSpliterator;
import static apoc.util.DateParseUtil.dateParse;
import static apoc.util.Util.*;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import apoc.Extended;
import apoc.export.util.CountingInputStream;
import apoc.meta.Meta;
import apoc.util.FileUtils;
import apoc.util.MissingDependencyException;
import apoc.util.Util;
import java.time.*;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
@Extended
public class LoadXls {
public static final char DEFAULT_ARRAY_SEP = ';';
@Context
public GraphDatabaseService db;
enum Results {
map,
list,
strings,
stringMap
}
// Sheet1!$A$1:$F$1
static class Selection {
private static final Pattern PATTERN =
Pattern.compile("([a-z]+)(\\d+)?(?::([a-z]+)(\\d+)?)?", Pattern.CASE_INSENSITIVE);
private static final int DEFAULT = -1;
String sheet;
int top = DEFAULT;
int left = DEFAULT;
int bottom = DEFAULT;
int right = DEFAULT;
public Selection(String selector) {
String[] parts = selector.split("!");
sheet = parts[0];
if (parts.length > 1 && !parts[1].trim().isEmpty()) {
String range = parts[1].trim().replace("$", "");
Matcher matcher = PATTERN.matcher(range);
if (matcher.matches()) {
left = toCol(matcher.group(1), DEFAULT);
top = toRow(matcher.group(2), DEFAULT);
right = toCol(matcher.group(3), left);
if (right != DEFAULT) right++;
bottom = toRow(matcher.group(4), DEFAULT);
}
}
}
int getOrDefault(int value, int given) {
return value == DEFAULT ? given : value;
}
private int toCol(String col, int defaultValue) {
if (col == null || col.trim().isEmpty()) return defaultValue;
AtomicInteger index = new AtomicInteger(0);
return Stream.of(col.trim().toUpperCase().split(""))
.map(str -> new AbstractMap.SimpleEntry<>(index.getAndIncrement(), str.charAt(0) - 65))
.map(e -> (26 * e.getKey()) + e.getValue())
.reduce(0, Math::addExact);
}
private int toRow(String row, int defaultValue) {
if (row == null || row.trim().isEmpty()) return defaultValue;
row = row.trim();
try {
return Integer.parseInt(row) - 1;
} catch (NumberFormatException nfe) {
return (short) defaultValue;
}
}
public void updateVertical(int firstRowNum, int lastRowNum) {
if (top == DEFAULT) top = firstRowNum;
if (bottom == DEFAULT) bottom = lastRowNum;
}
public void updateHorizontal(short firstCellNum, short lastCellNum) {
if (left == DEFAULT) left = firstCellNum;
if (right == DEFAULT) right = lastCellNum;
}
}
@Procedure("apoc.load.xls")
@Description(
"apoc.load.xls('url','selector',{config}) YIELD lineNo, list, map - load XLS fom URL as stream of row values,\n config contains any of: {skip:1,limit:5,header:false,ignore:['tmp'],arraySep:';',mapping:{years:{type:'int',arraySep:'-',array:false,name:'age',ignore:false, dateFormat:'iso_date', dateParse:['dd-MM-yyyy']}}")
public Stream xls(
@Name("url") String url,
@Name("selector") String selector,
@Name(value = "config", defaultValue = "{}") Map config) {
boolean failOnError = booleanValue(config, "failOnError", true);
try (CountingInputStream stream = FileUtils.inputStreamFor(url, null, null, null)) {
Selection selection = new Selection(selector);
char arraySep = separator(config, "arraySep", DEFAULT_ARRAY_SEP);
long skip = longValue(config, "skip", 0L);
boolean hasHeader = booleanValue(config, "header", true);
boolean skipNulls = booleanValue(config, "skipNulls", false);
long limit = longValue(config, "limit", Long.MAX_VALUE);
List ignore = value(config, "ignore", emptyList());
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy