|
| 1 | +/*********************************************************************** |
| 2 | + * Copyright (c) 2013-2025 General Atomics Integrated Intelligence, Inc. |
| 3 | + * All rights reserved. This program and the accompanying materials |
| 4 | + * are made available under the terms of the Apache License, Version 2.0 |
| 5 | + * which accompanies this distribution and is available at |
| 6 | + * https://www.apache.org/licenses/LICENSE-2.0 |
| 7 | + ***********************************************************************/ |
| 8 | + |
| 9 | + |
| 10 | +package org.locationtech.geomesa.filter.function |
| 11 | + |
| 12 | +import org.geotools.feature.simple.SimpleFeatureImpl |
| 13 | +import org.geotools.filter.identity.FeatureIdImpl |
| 14 | +import org.locationtech.geomesa.filter.FilterHelper |
| 15 | +import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypes |
| 16 | +import org.specs2.mutable.SpecificationWithJUnit |
| 17 | + |
| 18 | +class BucketHashFunctionTest extends SpecificationWithJUnit { |
| 19 | + |
| 20 | + import FilterHelper.ff |
| 21 | + |
| 22 | + import scala.collection.JavaConverters._ |
| 23 | + |
| 24 | + val sft = SimpleFeatureTypes.createType("test", |
| 25 | + "name:String,age:Int,time:Long,weight:Float,precision:Double,dtg:Date,bytes:Bytes,uuid:UUID") |
| 26 | + |
| 27 | + val nullValues = Seq.fill[AnyRef](sft.getAttributeCount)(null).asJava |
| 28 | + |
| 29 | + "BucketHashFunction" should { |
| 30 | + "hash strings" in { |
| 31 | + // string hashBytes(utf8Bytes(v)) iceberg → 1210000089 |
| 32 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 33 | + sf.setAttribute("name", "iceberg") |
| 34 | + ff.function("bucketHash", ff.property("name"), ff.literal(8)).evaluate(sf) mustEqual Int.box(1210000089 % 8) |
| 35 | + } |
| 36 | + "hash ints" in { |
| 37 | + // int hashLong(long(v)) [1] 34 → 2017239379 |
| 38 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 39 | + sf.setAttribute("age", "34") |
| 40 | + ff.function("bucketHash", ff.property("age"), ff.literal(8)).evaluate(sf) mustEqual Int.box(2017239379 % 8) |
| 41 | + } |
| 42 | + "hash longs" in { |
| 43 | + // long hashBytes(littleEndianBytes(v)) 34L → 2017239379 |
| 44 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 45 | + sf.setAttribute("time", "34") |
| 46 | + ff.function("bucketHash", ff.property("time"), ff.literal(8)).evaluate(sf) mustEqual Int.box(2017239379 % 8) |
| 47 | + } |
| 48 | + "hash floats" in { |
| 49 | + // float hashLong(doubleToLongBits(double(v)) [5] 1.0F → -142385009, 0.0F → 1669671676, -0.0F → 1669671676 |
| 50 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 51 | + sf.setAttribute("weight", "1") |
| 52 | + ff.function("bucketHash", ff.property("weight"), ff.literal(8)).evaluate(sf) mustEqual Int.box((-142385009 & Int.MaxValue) % 8) |
| 53 | + } |
| 54 | + "hash doubles" in { |
| 55 | + // double hashLong(doubleToLongBits(v)) [5] 1.0D → -142385009, 0.0D → 1669671676, -0.0D → 1669671676 |
| 56 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 57 | + sf.setAttribute("precision", "1") |
| 58 | + ff.function("bucketHash", ff.property("precision"), ff.literal(8)).evaluate(sf) mustEqual Int.box((-142385009 & Int.MaxValue) % 8) |
| 59 | + } |
| 60 | + "hash dates" in { |
| 61 | + // timestamp hashLong(microsecsFromUnixEpoch(v)) 2017-11-16T22:31:08 → -2047944441, 2017-11-16T22:31:08.000001 → -1207196810 |
| 62 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 63 | + sf.setAttribute("dtg", "2017-11-16T22:31:08") |
| 64 | + ff.function("bucketHash", ff.property("dtg"), ff.literal(8)).evaluate(sf) mustEqual Int.box((-2047944441 & Int.MaxValue) % 8) |
| 65 | + } |
| 66 | + "hash byte arrays" in { |
| 67 | + // binary hashBytes(v) 00 01 02 03 → -188683207 |
| 68 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 69 | + sf.setAttribute("bytes", Array[Byte](0, 1, 2, 3)) |
| 70 | + ff.function("bucketHash", ff.property("bytes"), ff.literal(8)).evaluate(sf) mustEqual Int.box((-188683207 & Int.MaxValue) % 8) |
| 71 | + } |
| 72 | + "hash uuids" in { |
| 73 | + // uuid hashBytes(uuidBytes(v)) [4] f79c3e09-677c-4bbd-a479-3f349cb785e7 → 1488055340 |
| 74 | + val sf = new SimpleFeatureImpl(nullValues, sft, new FeatureIdImpl("1")) |
| 75 | + sf.setAttribute("uuid", "f79c3e09-677c-4bbd-a479-3f349cb785e7") |
| 76 | + ff.function("bucketHash", ff.property("uuid"), ff.literal(8)).evaluate(sf) mustEqual Int.box(1488055340 % 8) |
| 77 | + } |
| 78 | + } |
| 79 | +} |
0 commit comments