Repository: usergrid Updated Branches: refs/heads/USERGRID-926 [created] 5cc0b3807
add distance field Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/a9da97df Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/a9da97df Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/a9da97df Branch: refs/heads/USERGRID-926 Commit: a9da97df7669caea605529e83943a9846aea3f40 Parents: a7b6a9c Author: Shawn Feldman <[email protected]> Authored: Fri Nov 6 11:10:45 2015 -0700 Committer: Shawn Feldman <[email protected]> Committed: Fri Nov 6 11:10:45 2015 -0700 ---------------------------------------------------------------------- .../read/search/CandidateEntityFilter.java | 6 +++ .../org/apache/usergrid/persistence/GeoIT.java | 42 ++++++++++++++++++++ .../model/entity/EntityToMapConverter.java | 8 +++- .../persistence/model/field/DistanceField.java | 42 ++++++++++++++++++++ .../persistence/model/field/FieldTypeName.java | 3 +- .../persistence/index/GeoCandidateResult.java | 40 +++++++++++++++++++ .../index/impl/EsEntityIndexImpl.java | 7 ++-- .../persistence/index/impl/IndexingUtils.java | 39 +++++++++++++----- 8 files changed, 172 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java ---------------------------------------------------------------------- diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java index 9a9636f..449639c 100644 --- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java +++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/pipeline/read/search/CandidateEntityFilter.java @@ -25,6 +25,8 @@ import java.util.*; import org.apache.usergrid.corepersistence.index.IndexLocationStrategyFactory; import org.apache.usergrid.persistence.index.*; import org.apache.usergrid.persistence.index.impl.IndexProducer; +import org.apache.usergrid.persistence.model.field.DistanceField; +import org.apache.usergrid.persistence.model.field.DoubleField; import org.apache.usergrid.persistence.model.field.Field; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -197,6 +199,7 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate final Candidate candidate = filterResult.getValue(); final CandidateResult candidateResult = candidate.getCandidateResult(); + final boolean isGeo = candidateResult instanceof GeoCandidateResult; final SearchEdge searchEdge = candidate.getSearchEdge(); final Id candidateId = candidateResult.getId(); final UUID candidateVersion = candidateResult.getVersion(); @@ -252,6 +255,9 @@ public class CandidateEntityFilter extends AbstractFilter<FilterResult<Candidate //they're the same add it final Entity returnEntity = entity.getEntity().get(); + if(isGeo){ + returnEntity.setField(new DistanceField(((GeoCandidateResult)candidateResult).getDistance())); + } final Optional<EdgePath> parent = filterResult.getPath(); http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/core/src/test/java/org/apache/usergrid/persistence/GeoIT.java ---------------------------------------------------------------------- diff --git a/stack/core/src/test/java/org/apache/usergrid/persistence/GeoIT.java b/stack/core/src/test/java/org/apache/usergrid/persistence/GeoIT.java index 230f9be..fcc4c32 100644 --- a/stack/core/src/test/java/org/apache/usergrid/persistence/GeoIT.java +++ b/stack/core/src/test/java/org/apache/usergrid/persistence/GeoIT.java @@ -161,6 +161,48 @@ public class GeoIT extends AbstractCoreIT { em.delete(user); } + + /** + * Validate the ability to query a moving entity + * 1. Create an entity with location + * 2. Query from a point near the entity's location + * 3. Move the entity farther away from the center point + * 4. Run the same query again to verify the entity is no longer in the area + */ + @Test + public void validateDistanceQueryExists() throws Exception { + LOG.info("GeoIT.testMovingTarget"); + //Get the EntityManager instance + EntityManager em = app.getEntityManager(); + assertNotNull(em); + + //1. Create an entity with location + Map<String, Object> properties = new LinkedHashMap<String, Object>() {{ + put("username", "edanuff"); + put("email", "[email protected]"); + put("location", new LinkedHashMap<String, Object>() {{ + put("latitude", 37.776753); + put("longitude", -122.407846); + }}); + }}; + Entity user = em.create("user", properties); + assertNotNull(user); + app.refreshIndex(); + + final double lat = 37.776753; + final double lon = -122.407846; + //2. Query from a point near the entity's location + Query query = Query.fromQL("select * where location within 100 of " + + lat + "," + lon); + Results listResults = em.searchCollection(em.getApplicationRef(), "users", query); + assertEquals(1, listResults.size()); + Entity entity = listResults.getEntity(); + assertTrue(entity.getMetadata("distance")!=null); + assertTrue(Double.parseDouble( entity.getMetadata("distance").toString())>0); + + em.delete(user); + } + /** * Validate the ability to query connections within proximity of the users * 1. Create an entity with location http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/EntityToMapConverter.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/EntityToMapConverter.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/EntityToMapConverter.java index b9f637a..dd7a916 100644 --- a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/EntityToMapConverter.java +++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/EntityToMapConverter.java @@ -57,8 +57,14 @@ public class EntityToMapConverter { } private EntityMap toMap( EntityObject entity, EntityMap entityMap ) { + for ( Field field : entity.getFields() ) { - if ( field instanceof ListField || field instanceof ArrayField || field instanceof SetField) { + if( field instanceof DistanceField){ + if(!entityMap.containsKey("metadata"))entityMap.put("metadata",new HashMap<String,Object>()); + DistanceField distanceField = (DistanceField) field; + Map<String,Object> metaMap = (Map) entityMap.get("metadata"); + metaMap.put(DistanceField.NAME, distanceField.getValue()); + }else if ( field instanceof ListField || field instanceof ArrayField || field instanceof SetField) { Collection list = ( Collection ) field.getValue(); entityMap.put( field.getName(), processCollection( list ) ); } http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DistanceField.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DistanceField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DistanceField.java new file mode 100644 index 0000000..4e9e1e1 --- /dev/null +++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DistanceField.java @@ -0,0 +1,42 @@ +/* + * + * * Licensed to the Apache Software Foundation (ASF) under one or more + * * contributor license agreements. The ASF licenses this file to You + * * 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. For additional information regarding + * * copyright in this work, please see the NOTICE file in the top level + * * directory of this distribution. + * + */ +package org.apache.usergrid.persistence.model.field; + +/** + * Classy class class. + */ +public class DistanceField extends DoubleField { + public static final String NAME = "distance"; + public DistanceField( Double value) { + super(NAME, value); + } + + public DistanceField( Double value, boolean unique) { + super(NAME, value, unique); + } + + public DistanceField() { + super(); + } + @Override + public FieldTypeName getTypeName() { + return FieldTypeName.DISTANCE; + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/FieldTypeName.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/FieldTypeName.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/FieldTypeName.java index 90f3879..8b0cdc3 100644 --- a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/FieldTypeName.java +++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/FieldTypeName.java @@ -39,5 +39,6 @@ public enum FieldTypeName { LONG, SET, STRING, - UUID + UUID, + DISTANCE } http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/GeoCandidateResult.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/GeoCandidateResult.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/GeoCandidateResult.java new file mode 100644 index 0000000..07ef41c --- /dev/null +++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/GeoCandidateResult.java @@ -0,0 +1,40 @@ +/* + * + * * Licensed to the Apache Software Foundation (ASF) under one or more + * * contributor license agreements. The ASF licenses this file to You + * * 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. For additional information regarding + * * copyright in this work, please see the NOTICE file in the top level + * * directory of this distribution. + * + */ +package org.apache.usergrid.persistence.index; + +import org.apache.usergrid.persistence.model.entity.Id; + +import java.util.UUID; + +/** + * Classy class class. + */ +public class GeoCandidateResult extends CandidateResult { + private final double distance; + + public GeoCandidateResult(Id entityId, UUID entityVersion, String docId, double distance) { + super(entityId, entityVersion, docId); + this.distance = distance; + } + + public double getDistance() { + return distance; + } +} http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java index f03a98a..b32a11c 100644 --- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java +++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/EsEntityIndexImpl.java @@ -668,11 +668,12 @@ public class EsEntityIndexImpl implements EntityIndex,VersionedData { logger.debug( " Hit count: {} Total hits: {}", hits.length, searchHits.getTotalHits() ); List<CandidateResult> candidates = new ArrayList<>( hits.length ); + final boolean isGeo = query.getOriginalQuery().contains("location") && query.getOriginalQuery().contains("within"); for ( SearchHit hit : hits ) { + CandidateResult candidateResult; - final CandidateResult candidateResult = parseIndexDocId( hit.getId() ); - + candidateResult = parseIndexDocId( hit, isGeo ); candidates.add( candidateResult ); } @@ -696,7 +697,7 @@ public class EsEntityIndexImpl implements EntityIndex,VersionedData { for ( SearchHit hit : hits ) { - final CandidateResult candidateResult = parseIndexDocId( hit.getId() ); + final CandidateResult candidateResult = parseIndexDocId( hit ); // if comparing against the latestVersion, make sure we only add the candidateResult if it's // older than or equal to the latest marked version http://git-wip-us.apache.org/repos/asf/usergrid/blob/a9da97df/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/IndexingUtils.java ---------------------------------------------------------------------- diff --git a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/IndexingUtils.java b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/IndexingUtils.java index 18cb928..88da04e 100644 --- a/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/IndexingUtils.java +++ b/stack/corepersistence/queryindex/src/main/java/org/apache/usergrid/persistence/index/impl/IndexingUtils.java @@ -24,6 +24,7 @@ import java.util.regex.Pattern; import org.apache.usergrid.persistence.core.scope.ApplicationScope; import org.apache.usergrid.persistence.index.CandidateResult; +import org.apache.usergrid.persistence.index.GeoCandidateResult; import org.apache.usergrid.persistence.index.IndexEdge; import org.apache.usergrid.persistence.index.SearchEdge; import org.apache.usergrid.persistence.model.entity.Entity; @@ -31,6 +32,7 @@ import org.apache.usergrid.persistence.model.entity.Id; import org.apache.usergrid.persistence.model.entity.SimpleId; import com.google.common.base.Preconditions; +import org.elasticsearch.search.SearchHit; public class IndexingUtils { @@ -220,28 +222,45 @@ public class IndexingUtils { builder.append( type ).append( "(" ).append( value ).append( ")" ); } - /** * Parse the document id into a candidate result */ - public static CandidateResult parseIndexDocId( final String documentId ) { + public static CandidateResult parseIndexDocId( final SearchHit hit ) { + return parseIndexDocId(hit.getId()); + } + + public static CandidateResult parseIndexDocId( final SearchHit hit, boolean isGeo ) { + final String documentId = hit.getId(); + final double distance = isGeo ? (double) hit.sortValues()[0] : -1; + return parseIndexDocId(documentId,distance); + } + + public static CandidateResult parseIndexDocId( final String documentId ) { + return parseIndexDocId(documentId,-1); + } + /** + * Parse the document id into a candidate result + */ + public static CandidateResult parseIndexDocId( final String documentId, final double distance ) { - final Matcher matcher = DOCUMENT_PATTERN.matcher( documentId ); + final Matcher matcher = DOCUMENT_PATTERN.matcher(documentId); - Preconditions.checkArgument( matcher.matches(), "Pattern for document id did not match expected format" ); - Preconditions.checkArgument( matcher.groupCount() == 9, "9 groups expected in the pattern" ); + Preconditions.checkArgument(matcher.matches(), "Pattern for document id did not match expected format"); + Preconditions.checkArgument(matcher.groupCount() == 9, "9 groups expected in the pattern"); //Other fields can be parsed using groups. The groups start at value 1, group 0 is the entire match - final String entityUUID = matcher.group( 3 ); - final String entityType = matcher.group( 4 ); + final String entityUUID = matcher.group(3); + final String entityType = matcher.group(4); - final String versionUUID = matcher.group( 5 ); + final String versionUUID = matcher.group(5); - Id entityId = new SimpleId( UUID.fromString( entityUUID ), entityType ); + Id entityId = new SimpleId(UUID.fromString(entityUUID), entityType); - return new CandidateResult( entityId, UUID.fromString( versionUUID ), documentId ); + return distance>0 + ? new GeoCandidateResult(entityId, UUID.fromString(versionUUID), documentId, distance) + : new CandidateResult(entityId, UUID.fromString(versionUUID), documentId); }
