目的はKind名の一覧を取得する事。Kind名さえわかれば、後は普通にDatastoreServiceFactory#getDatastoreService()を使って、Entityを読み込む事ができて、Entityが読み込めればそこから全propertyを取得できたりして、色々な事ができそう。ローカルからAppEngineにアップロードする時にも使えそうだし。Eclipse用にLocalDatastoreViewerPluginとかも作れそうだし。
……っていうエントリを書くつもりだったんだけど、SchemaやらEntityProtoやら色々要素が多すぎて、説明するのが大変に面倒になって来たので、ユーティリティクラスのコードだけ晒す事にしますw ゴメンナサイ、ゴメンナサイ。
AppEngineの起動と終了
Datastore周りの単体テストをする時とほぼおんなじ。
static final String DATASTORE_V3 = "datastore_v3";
static ApiProxy.Environment setUpDatastoreService(final String appId, final String versionId,
    String applicationFolder) {
  ApiProxy.Environment environment;
  ApiProxy.setEnvironmentForCurrentThread(environment = new ApiProxy.Environment() {
    public String getAppId() { return appId; }
    public String getVersionId() { return versionId; }
    public String getRequestNamespace() { return ""; }
    public String getAuthDomain() { return "hoge.com"; }
    public boolean isLoggedIn() { return true; }
    public String getEmail() { return "fuga@hoge.com"; }
    public boolean isAdmin() { return false; }
    public Map<String, Object> getAttributes() {
      Map<String, Object> map = new HashMap<String, Object>();
      return map;
    }
  });
  ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(applicationFolder)) {});
  return environment;
}
static void tearDownDatastoreService() {
  ((LocalDatastoreService) ((ApiProxyLocalImpl) ApiProxy.getDelegate())
      .getService(DATASTORE_V3)).stop();
  ApiProxy.setDelegate(null);
  ApiProxy.setEnvironmentForCurrentThread(null);
}Kind名の一覧を取得する
LocalDatastoreService#getSchema()ってのがあります。スキーマレスなのにgetSchame()とはなんのこっちゃってカンジですが、Datastoreのビューアが使っているのもこの情報です。
public static String[] getKinds(Environment environment) {
  LocalDatastoreService datastoreService = (LocalDatastoreService) ((ApiProxyLocalImpl) ApiProxy
      .getDelegate()).getService(DATASTORE_V3);
  Schema schema = datastoreService.getSchema(null, (new StringProto()).setValue(environment
      .getAppId()));
  List<EntityProto> entityProtoList = schema.kinds();
  List<String> kindList = new ArrayList<String>(entityProtoList.size());
  for (EntityProto entityProto : entityProtoList) {
    List<?> path = entityProto.getKey().getPath().elements();
    Element element = (Element) path.get(path.size() - 1);
    kindList.add(element.getType());
  }
  return kindList.toArray(new String[0]);
}特定のproperty名の一覧を取得する
Kind名がわれば実行時に取得できるので、あんまり必要無いかも。ちなみに、JDOを使っているとPOJOで定義した覚えが無いpropertyが混ざってくると思います。楽観的排他制御とか、ListPropertyのExtentで定義した制御の為にJDOが独自のpropertyを付加したものです。また、残念な事にインデックス対象でないpropertyは一覧から取得できません。
public static String[] getProperties(Environment environment, String kind) {
  LocalDatastoreService datastoreService = (LocalDatastoreService) ((ApiProxyLocalImpl) ApiProxy
      .getDelegate()).getService(DATASTORE_V3);
  Schema schema = datastoreService.getSchema(null, (new StringProto()).setValue(environment
      .getAppId()));
  List<EntityProto> entityProtoList = schema.kinds();
  for (EntityProto entityProto : entityProtoList) {
    List<?> path = entityProto.getKey().getPath().elements();
    Element element = (Element) path.get(path.size() - 1);
    String type = element.getType();
    if (kind.equals(type)) {
      List<Property> properties = entityProto.propertys();
      String[] propertyNames = new String[properties.size()];
      for (int i = 0; i < propertyNames.length; i++) {
        propertyNames[i] = properties.get(i).getName();
      }
      return propertyNames;
    }
  }
  throw new RuntimeException("kind \"" + kind + "\" is not found.");
}使い方
SDKのコンテナを使った環境で使用しているデータファイル(war/WEB-INF/appengine-generated/local_db.bin)を使いたい場合は、以前書いたエントリ「#appengine のテスト用初期データを作成する」を参考に、アプリケーションのversionIdを確認しておく必要があります。
@Test public void test() {
  Environment environment = setUpDatastoreService("shin1ogawa-app", "versionid.1", "war");
  try {
    String[] kinds = DatastoreUtil.getKinds(environment);
    for (String kind : kinds) {
      System.out.println(kind);
      String[] properties = DatastoreUtil.getProperties(environment, kind);
      for (String property : properties) {
        System.out.println("  " + property);
      }
    }
  } finally {
    tearDownDatastoreService();
  }
}importの宣言
import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import com.google.appengine.api.datastore.Blob; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.dev.LocalDatastoreService; import com.google.appengine.tools.development.ApiProxyLocalImpl; import com.google.apphosting.api.ApiProxy; import com.google.apphosting.api.ApiBasePb.StringProto; import com.google.apphosting.api.ApiProxy.Environment; import com.google.apphosting.api.DatastorePb.Schema; import com.google.storage.onestore.v3.OnestoreEntity.EntityProto; import com.google.storage.onestore.v3.OnestoreEntity.Property; import com.google.storage.onestore.v3.OnestoreEntity.Path.Element;
 
0 件のコメント:
コメントを投稿