Allow inserts to use existing feature ID - STEMLab/geotools GitHub Wiki

Description

GeoTools data stores always generate a new feature id when a new feature is being inserted. In some cases this is a requirement, such as in Shapefiles, where the id is the feature row number in the file. In other cases it's merely an implementation choice, such as the case of property data store or the JDBC data stores.

Using the user provided feature id is a requirement in a few cases:

  • WFS 1.1 insert handling mandates the support of a useExisting policy
  • some organizations mandate a primary key generation policy for database tables
  • the primary key can be a multicolumn key whose content references other tables via foreign keys
  • the key itself has a business meaning (e.g., a social security number)

This proposal adds hints to allow:

  • a feature store to advertise the ability to use user provided feature id during inserts
  • a user to ask the feature id to be used during the import
    Neither of them requires new API, thus this proposal is targeted towards both the 2.6.x and trunk branches.

To support advertisement QueryCapabilities gets a new method:

public class QueryCapabilities {

    /**
     * If true the datastore supports using the provided feature id in the data insertion
     * workflow as opposed to generating a new id. In that case it will look into the user data
     * map ({@link Feature#getUserData()}) for a {@link Hints#USE_EXISTING_FID} key associated to a
     * {@link Boolean#TRUE} value, if the key/value pair is there an attempt to use the provided
     * id will be made, and the operation will fail of the key cannot be parsed into a valid
     * storage identifier.
     * @return
     */
    public boolean isUseExisingFIDSupported() {
        return false;
    }
}

To support uses in asking the FID to be used a new well know hint has been added, that need to be specified in the Feature user map (see example below):

pubilc class Hints {
 ...

    /**
     * When this key is used in the user data section of a feature and the feature store
     * query capabilities reports being able to use provided feature ids the store will
     * try to use the provided feature id during insertion, and will fail if the FID
     * cannot be parsed into a valid storage identifier
     *
     * @since 2.7
     */
    public static final Key USE_EXISTING_FID = new Key("org.geotools.fidPolicy.UseExisting");


}

Implementation wise the JDBC data stores and the property data store will be modified to support the USE_EXISTING_FID hint.

GEOT-3120 contains a full working patch to implement the above.

Status

This proposal has been implemented

Voting has not started yet:

Usage example

AFTER

The following unit tests shows checking for USE_EXISTING_FID hint support and asking the FID to be used during insertion.

public void testAddFeaturesUseExisting() throws IOException {
    // check we advertise the ability to reuse feature ids
    assertTrue(featureStore.getQueryCapabilities().isUseExisingFIDSupported());

    SimpleFeatureBuilder b = new SimpleFeatureBuilder(featureStore.getSchema());
    DefaultFeatureCollection collection = new DefaultFeatureCollection(null,
            featureStore.getSchema());

    String typeName = b.getFeatureType().getTypeName();
    for (int i = 3; i < 6; i++) {
        b.set(aname("intProperty"), new Integer(i));
        b.set(aname("geometry"), new GeometryFactory().createPoint(new Coordinate(i, i)));
        b.featureUserData(Hints.USE_EXISTING_FID, Boolean.TRUE);
        collection.add(b.buildFeature(typeName + "." + (i * 10)));
    }
    List<FeatureId> fids = featureStore.addFeatures(collection);

    assertEquals(3, fids.size());

    // check the created ids are the ones expected
    assertTrue(fids.contains(SimpleFeatureBuilder.createDefaultFeatureIdentifier(typeName + ".30")));
    assertTrue(fids.contains(SimpleFeatureBuilder.createDefaultFeatureIdentifier(typeName + ".40")));
    aassertTrue(fids.contains(SimpleFeatureBuilder.createDefaultFeatureIdentifier(typeName + ".50")));

    SimpleFeatureCollection features = featureStore.getFeatures();
    assertEquals(6, features.size());

    FilterFactory ff = dataStore.getFilterFactory();

    // manually check features are actually there
    for (Iterator f = fids.iterator(); f.hasNext();) {
        FeatureId identifier = (FeatureId) f.next();
        String fid = identifier.getID();
        Id filter = ff.id(Collections.singleton(identifier));

        features = featureStore.getFeatures(filter);
        assertEquals(1, features.size());
    }
}

Documentation Changes

The following page already documents the above (as modified by Jody):

⚠️ **GitHub.com Fallback** ⚠️