use of org.neo4j.values.storable.PointValue in project neo4j by neo4j.
the class GenericAccessorPointsTest method mustHandlePointArraysWithinSameTile.
/**
* This test verify that we correctly handle unique point arrays where every point in every array belong to the same tile on the space filling curve.
* We verify this by asserting that we always get exactly one hit on an exact match and that the value is what we expect.
*/
@Test
void mustHandlePointArraysWithinSameTile() throws IndexEntryConflictException, IndexNotApplicableKernelException {
// given
// Many random points that all are close enough to each other to belong to the same tile on the space filling curve.
int nbrOfValues = 10000;
PointValue origin = Values.pointValue(WGS84, 0.0, 0.0);
Long derivedValueForCenterPoint = curve.derivedValueFor(origin.coordinate());
double[] centerPoint = curve.centerPointFor(derivedValueForCenterPoint);
double xWidthMultiplier = curve.getTileWidth(0, curve.getMaxLevel()) / 2;
double yWidthMultiplier = curve.getTileWidth(1, curve.getMaxLevel()) / 2;
List<Value> pointArrays = new ArrayList<>();
List<IndexEntryUpdate<?>> updates = new ArrayList<>();
for (int i = 0; i < nbrOfValues; i++) {
int arrayLength = random.nextInt(5) + 1;
PointValue[] pointValues = new PointValue[arrayLength];
for (int j = 0; j < arrayLength; j++) {
double x = (random.nextDouble() * 2 - 1) * xWidthMultiplier;
double y = (random.nextDouble() * 2 - 1) * yWidthMultiplier;
PointValue value = Values.pointValue(WGS84, centerPoint[0] + x, centerPoint[1] + y);
assertDerivedValue(derivedValueForCenterPoint, value);
pointValues[j] = value;
}
PointArray array = Values.pointArray(pointValues);
pointArrays.add(array);
updates.add(IndexEntryUpdate.add(i, descriptor, array));
}
processAll(updates);
// then
exactMatchOnAllValues(pointArrays);
}
use of org.neo4j.values.storable.PointValue in project neo4j by neo4j.
the class GenericAccessorPointsTest method mustHandlePointsWithinSameTile.
/**
* This test verify that we correctly handle unique points that all belong to the same tile on the space filling curve.
* All points share at least one dimension coordinate with another point to exercise minimal splitter.
* We verify this by asserting that we always get exactly one hit on an exact match and that the value is what we expect.
*/
@Test
void mustHandlePointsWithinSameTile() throws IndexEntryConflictException, IndexNotApplicableKernelException {
// given
// Many random points that all are close enough to each other to belong to the same tile on the space filling curve.
int nbrOfValues = 10000;
PointValue origin = Values.pointValue(WGS84, 0.0, 0.0);
Long derivedValueForCenterPoint = curve.derivedValueFor(origin.coordinate());
double[] centerPoint = curve.centerPointFor(derivedValueForCenterPoint);
double xWidthMultiplier = curve.getTileWidth(0, curve.getMaxLevel()) / 2;
double yWidthMultiplier = curve.getTileWidth(1, curve.getMaxLevel()) / 2;
List<Value> pointValues = new ArrayList<>();
List<IndexEntryUpdate<?>> updates = new ArrayList<>();
long nodeId = 1;
for (int i = 0; i < nbrOfValues / 4; i++) {
double x1 = (random.nextDouble() * 2 - 1) * xWidthMultiplier;
double x2 = (random.nextDouble() * 2 - 1) * xWidthMultiplier;
double y1 = (random.nextDouble() * 2 - 1) * yWidthMultiplier;
double y2 = (random.nextDouble() * 2 - 1) * yWidthMultiplier;
PointValue value11 = Values.pointValue(WGS84, centerPoint[0] + x1, centerPoint[1] + y1);
PointValue value12 = Values.pointValue(WGS84, centerPoint[0] + x1, centerPoint[1] + y2);
PointValue value21 = Values.pointValue(WGS84, centerPoint[0] + x2, centerPoint[1] + y1);
PointValue value22 = Values.pointValue(WGS84, centerPoint[0] + x2, centerPoint[1] + y2);
assertDerivedValue(derivedValueForCenterPoint, value11, value12, value21, value22);
nodeId = addPointsToLists(pointValues, updates, nodeId, value11, value12, value21, value22);
}
processAll(updates);
// then
exactMatchOnAllValues(pointValues);
}
use of org.neo4j.values.storable.PointValue in project neo4j by neo4j.
the class GenericAccessorPointsTest method shouldNotGetFalsePositivesForRangesSpanningMultipleTiles.
/**
* Given how the spatial index works, if a point is on a tile that intersect with the search area,
* but the point itself is outside the search area, it is a candidate for a false positive.
* The reason is that such a point is returned from the index itself,
* because a space index compares (and generally works with) only values of a space-filling curve
* and not the points themselves.
* Therefore {@link IndexReader} implementation must post-process raw index results and filter out such false positives.
*/
@Test
void shouldNotGetFalsePositivesForRangesSpanningMultipleTiles() throws IndexNotApplicableKernelException, IndexEntryConflictException {
PointValue origin = Values.pointValue(WGS84, 0.0, 0.0);
long derivedValueForCenterPoint = curve.derivedValueFor(origin.coordinate());
double[] searchStart = curve.centerPointFor(derivedValueForCenterPoint);
double xTileWidth = curve.getTileWidth(0, curve.getMaxLevel());
// to make it easier to imagine this, the search start is a center point of one tile and the limit is a center point of the next tile on the x-axis
PointValue limitPoint = Values.pointValue(WGS84, searchStart[0] + xTileWidth, searchStart[1]);
int nbrOfValues = 10_000;
List<PointValue> pointsInside = new ArrayList<>();
List<IndexEntryUpdate<?>> updates = new ArrayList<>();
for (int i = 0; i < nbrOfValues; i++) {
double distanceMultiplier = random.nextDouble() * 2;
PointValue point = Values.pointValue(WGS84, searchStart[0] + distanceMultiplier * xTileWidth, searchStart[1]);
updates.add(IndexEntryUpdate.add(0, descriptor, point));
if (distanceMultiplier <= 1) {
pointsInside.add(point);
}
}
processAll(updates);
try (var indexReader = accessor.newValueReader()) {
SimpleEntityValueClient client = new SimpleEntityValueClient();
var range = PropertyIndexQuery.range(descriptor.schema().getPropertyId(), Values.pointValue(WGS84, searchStart), true, limitPoint, true);
indexReader.query(QueryContext.NULL_CONTEXT, client, unorderedValues(), range);
List<Value> queryResult = new ArrayList<>();
while (client.next()) {
queryResult.add(client.values[0]);
}
assertThat(queryResult).containsExactlyInAnyOrderElementsOf(pointsInside);
}
}
use of org.neo4j.values.storable.PointValue in project neo4j by neo4j.
the class NativeIndexAccessorTests method getValues.
@Test
void getValues() throws IndexEntryConflictException, IndexNotApplicableKernelException {
// given
int nUpdates = 10000;
Iterator<ValueIndexEntryUpdate<IndexDescriptor>> randomUpdateGenerator = valueCreatorUtil.randomUpdateGenerator(random);
// noinspection unchecked
ValueIndexEntryUpdate<IndexDescriptor>[] someUpdates = new ValueIndexEntryUpdate[nUpdates];
for (int i = 0; i < nUpdates; i++) {
someUpdates[i] = randomUpdateGenerator.next();
}
processAll(someUpdates);
Value[] allValues = ValueCreatorUtil.extractValuesFromUpdates(someUpdates);
// Pick one out of all added values and do a range query for the value group of that value
Value value = random.among(allValues);
ValueGroup valueGroup = value.valueGroup();
IndexValueCapability valueCapability = indexCapability().valueCapability(valueGroup.category());
if (!valueCapability.equals(IndexValueCapability.YES)) {
// We don't need to do this test
return;
}
PropertyIndexQuery.RangePredicate<?> supportedQuery;
List<Value> expectedValues;
if (Values.isGeometryValue(value)) {
// Unless it's a point value in which case we query for the specific coordinate reference system instead
CoordinateReferenceSystem crs = ((PointValue) value).getCoordinateReferenceSystem();
supportedQuery = PropertyIndexQuery.range(0, crs);
expectedValues = Arrays.stream(allValues).filter(v -> v.valueGroup() == ValueGroup.GEOMETRY).filter(v -> ((PointValue) v).getCoordinateReferenceSystem() == crs).collect(Collectors.toList());
} else {
supportedQuery = PropertyIndexQuery.range(0, valueGroup);
expectedValues = Arrays.stream(allValues).filter(v -> v.valueGroup() == valueGroup).collect(Collectors.toList());
}
// when
try (var reader = accessor.newValueReader()) {
SimpleEntityValueClient client = new SimpleEntityValueClient();
reader.query(NULL_CONTEXT, client, unorderedValues(), supportedQuery);
// then
while (client.next()) {
Value foundValue = client.values[0];
assertTrue(expectedValues.remove(foundValue), "found value that was not expected " + foundValue);
}
assertThat(expectedValues.size()).as("did not find all expected values").isEqualTo(0);
}
}
use of org.neo4j.values.storable.PointValue in project neo4j by neo4j.
the class CsvInputBatchImportIT method buildUpExpectedData.
private static void buildUpExpectedData(List<InputEntity> nodeData, List<InputEntity> relationshipData, Map<String, InputEntity> expectedNodes, Map<String, String[]> expectedNodeNames, Map<String, Map<String, Consumer<Object>>> expectedNodePropertyVerifiers, Map<String, Map<String, Map<String, AtomicInteger>>> expectedRelationships, Map<String, AtomicLong> nodeCounts, Map<String, Map<String, Map<String, AtomicLong>>> relationshipCounts) {
for (InputEntity node : nodeData) {
expectedNodes.put((String) node.id(), node);
expectedNodeNames.put(nameOf(node), node.labels());
// Build default verifiers for all the properties that compares the property value using equals
Assertions.assertFalse(node.hasIntPropertyKeyIds);
Map<String, Consumer<Object>> propertyVerifiers = new TreeMap<>();
for (int i = 0; i < node.propertyCount(); i++) {
final Object expectedValue = node.propertyValue(i);
Consumer verify;
if (expectedValue instanceof TemporalAmount) {
// Since there is no straightforward comparison for TemporalAmount we add it to a reference
// point in time and compare the result
verify = actualValue -> {
LocalDateTime referenceTemporal = LocalDateTime.of(0, 1, 1, 0, 0);
LocalDateTime expected = referenceTemporal.plus((TemporalAmount) expectedValue);
LocalDateTime actual = referenceTemporal.plus((TemporalAmount) actualValue);
assertEquals(expected, actual);
};
} else if (expectedValue instanceof Temporal) {
final LocalDate expectedDate = ((Temporal) expectedValue).query(TemporalQueries.localDate());
final LocalTime expectedTime = ((Temporal) expectedValue).query(TemporalQueries.localTime());
final ZoneId expectedZoneId = ((Temporal) expectedValue).query(TemporalQueries.zone());
verify = actualValue -> {
LocalDate actualDate = ((Temporal) actualValue).query(TemporalQueries.localDate());
LocalTime actualTime = ((Temporal) actualValue).query(TemporalQueries.localTime());
ZoneId actualZoneId = ((Temporal) actualValue).query(TemporalQueries.zone());
assertEquals(expectedDate, actualDate);
assertEquals(expectedTime, actualTime);
if (expectedZoneId == null) {
if (actualZoneId != null) {
// If the actual value is zoned it should have the default zone
assertEquals(testDefaultTimeZone.get(), actualZoneId);
}
} else {
assertEquals(expectedZoneId, actualZoneId);
}
};
} else if (expectedValue instanceof float[]) {
verify = actualValue -> assertArrayEquals((float[]) expectedValue, (float[]) actualValue);
} else if (expectedValue.getClass().isArray()) {
verify = actualValue -> assertArrayEquals((Object[]) expectedValue, (Object[]) actualValue);
} else {
verify = actualValue -> assertEquals(expectedValue, actualValue);
}
propertyVerifiers.put((String) node.propertyKey(i), verify);
}
// Special verifier for pointA property
Consumer verifyPointA = actualValue -> {
// The y-coordinate should match the node number modulo 90 (so we don't break wgs boundaries)
PointValue v = (PointValue) actualValue;
double actualY = v.getCoordinates().get(0).getCoordinate().get(1);
double expectedY = indexOf(node) % 90;
String message = actualValue + " does not have y=" + expectedY;
assertEquals(expectedY, actualY, 0.1, message);
message = actualValue + " does not have crs=wgs-84";
assertEquals(CoordinateReferenceSystem.WGS84.getName(), v.getCoordinateReferenceSystem().getName(), message);
};
propertyVerifiers.put("pointA", verifyPointA);
// Special verifier for pointB property
Consumer verifyPointB = actualValue -> {
// The y-coordinate should match the node number
PointValue v = (PointValue) actualValue;
double actualY = v.getCoordinates().get(0).getCoordinate().get(1);
double expectedY = indexOf(node);
String message = actualValue + " does not have y=" + expectedY;
assertEquals(expectedY, actualY, 0.1, message);
message = actualValue + " does not have crs=cartesian";
assertEquals(CoordinateReferenceSystem.Cartesian.getName(), v.getCoordinateReferenceSystem().getName(), message);
};
propertyVerifiers.put("pointB", verifyPointB);
// Special verifier for pointArray property
Consumer verifyPointArray = actualValue -> verifyPointB.accept(((PointValue[]) actualValue)[0]);
propertyVerifiers.put("pointArray", verifyPointArray);
expectedNodePropertyVerifiers.put(nameOf(node), propertyVerifiers);
countNodeLabels(nodeCounts, node.labels());
}
for (InputEntity relationship : relationshipData) {
// Expected relationship counts per node, type and direction
InputEntity startNode = expectedNodes.get(relationship.startId());
InputEntity endNode = expectedNodes.get(relationship.endId());
{
expectedRelationships.get(nameOf(startNode)).get(nameOf(endNode)).get(relationship.stringType).incrementAndGet();
}
// Expected counts per start/end node label ids
// Let's do what CountsState#addRelationship does, roughly
relationshipCounts.get(null).get(null).get(null).incrementAndGet();
relationshipCounts.get(null).get(relationship.stringType).get(null).incrementAndGet();
for (String startNodeLabelName : asSet(startNode.labels())) {
Map<String, Map<String, AtomicLong>> startLabelCounts = relationshipCounts.get(startNodeLabelName);
startLabelCounts.get(null).get(null).incrementAndGet();
Map<String, AtomicLong> typeCounts = startLabelCounts.get(relationship.stringType);
typeCounts.get(null).incrementAndGet();
if (COMPUTE_DOUBLE_SIDED_RELATIONSHIP_COUNTS) {
for (String endNodeLabelName : asSet(endNode.labels())) {
startLabelCounts.get(null).get(endNodeLabelName).incrementAndGet();
typeCounts.get(endNodeLabelName).incrementAndGet();
}
}
}
for (String endNodeLabelName : asSet(endNode.labels())) {
relationshipCounts.get(null).get(null).get(endNodeLabelName).incrementAndGet();
relationshipCounts.get(null).get(relationship.stringType).get(endNodeLabelName).incrementAndGet();
}
}
}
Aggregations