1
votes

I am storing an entity using Objectify and would now like to add an index to one of the properties in order to run a query.

The entity arrives from an iOS client that is connected via Google Cloud Endpoints. It sets the property in the Objective-C class that was generated by ServiceGenerator.

So what I have tried is adding an @Index annotation to the property in Eclipse and uploading the project again with appcfg. However, when I inspect the datastore in Google Developers Console is still says "No indexes serving" and if I create new entities and run Objectify queries on the (now indexed) property is still get zero items back.

So what exact steps do I have to go through in order to add an index to an existing property for an existing entity that is defined with Objectify annotations in an Eclipse project?

This Q&A suggests it should be straightforward, but for some reason it is (apparently) not. Perhaps the complication arises from the involvement of Google Cloud Endpoints and an Objective-C client.

Update Here are some relevant code snippets, as requested:

The property is defined as follows:

@Entity
@Cache
public class SomeEntity {
    @Id public String someID;
    @Index String someProperty
}

The iOS client sets the the property as follows (relying on Objective-C classes produced by ServiceGenerator):

someEntity.someProperty = @"someString";

The server receives and saves the entity as follows:

@ApiMethod
public AddEntityResponse addEntity(AddEntityRequest request, HttpServletRequest httpServletRequest) throws ServletException {
    SomeEntity someEntity = request.someEntity;
    someEntity.someID = UUID.randomUUID().toString();
    ofy().save().entity(someEntity).now();
    // ...
}

When the server runs the following query it receives zero items back, although an entity with the given property value exists (is e.g. visible in the datastore viewer):

ofy().load().type(SomeEntity.class).filter("someProperty", "someString").list();
4

4 Answers

2
votes

In order to add indexes to existing entities you have to perform these simple steps:

  1. Annotate your properties with @Index
  2. Save the existing entity

The reason for point 2 is because annotating your entity is not enough by itself because in the DataStore your data are unaffected. You have to explicitly re-save the existing entity to force the Datastore to create the index(es) for newly annotated properties.

The same applies to when removing indexes. You must re-save for changes to take effect.

Edit

From your code snippet I can see that your query is not quite right. You are invoking the query method on the object returned by type(...) which implements LoadType interface but neither of the two interfaces extended by [LoadType][1] (namely LoadIds and Query define that method! I assume you haven't created your own wrapper as your code looks like standard objectify, so I'm not sure how your code is compiling or not blowing up at runtime.

Anyway to fix, since it looks like what you want to do is filter by specified predicate, change your query to look:

ofy().load().type(SomeEntity.class).filter("someProperty", "someString").list();
1
votes

Yes, I think it is should be straight-forward - you just add the @Index annotation to the appropriate attribute of your entity. Remember that this won't affect existing entities, or the kind in general - just new entities written with this updated class.

For verifying, I suggest you look at the datastore viewer in the new cloud console. It provides more information and allows you to see index info for individual entities. Make sure to inspect your newly created entities there.

1
votes

When I add a seemingly redundant assignment to the newly indexed property to the code that runs on the server I can make progress and receive the expected results from queries.

@ApiMethod
public AddEntityResponse addEntity(AddEntityRequest request, HttpServletRequest httpServletRequest) throws ServletException {
    SomeEntity someEntity = request.someEntity;
    someEntity.someID       = UUID.randomUUID().toString();
    someEntity.someProperty = someEntity.someProperty;
    ofy().save().entity(someEntity).now();
    // ...
}

After storing one new entity, I see this entity in Google Developers Console's Cloud Datastore Query (section) and I receive it from the query that applies a filter for someProperty. (For some reason the Google Developer Console's Cloud Datastore Indexes section still says "No indexes serving").

So my conclusion is that assignments to @Indexed properties only have actual effects on indices (and hence queries) if they are carried out directly with Objectify on the server (not indirectly on iOS clients and then passed to the server with Google Cloud Endpoints).

0
votes

I am unsure how this correlates to indexes, but I know modifying your model can be problematic if there is already data in that specific model. I've had issues adding properties or switching default values on some

I don't know how relevant it could be in your case, but could you "copy" your model with the new indexes and then migrate data to your new model, which should have that index properly built?

(p.s: yes I'm aware I'm asking you to "turn it off and on again" ;) )