/** * personium.io * Copyright 2014 FUJITSU LIMITED * * Licensed 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. */ package com.fujitsu.dc.core.model.impl.es.doc; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.odata4j.producer.QueryInfo; import com.fujitsu.dc.common.es.response.DcSearchHit; import com.fujitsu.dc.common.es.response.DcSearchResponse; import com.fujitsu.dc.core.DcCoreConfig; import com.fujitsu.dc.core.model.impl.es.QueryMapFactory; import com.fujitsu.dc.core.model.impl.es.accessor.ODataLinkAccessor; import com.fujitsu.dc.core.model.impl.es.odata.EsQueryHandler; import com.fujitsu.dc.core.model.impl.es.odata.UserDataODataProducer; /** * ESでN:Nリンクを扱う. リンクは2つのタイプの間に張られている。 EsLinkHandler elh = new EsLinkHandler(type1, type2); 双方のキーを指定して、リンクドキュメントを作成する。 * 双方のキーを指定して、リンクドキュメントのキーを作成する。 片側のTypeのキーを指定してもう片方のTypeの一覧を取得する。 */ public class LinkDocHandler implements EsDocHandler { private String id; private String cellId; private String boxId; private String nodeId; private String ent1Type; private String ent1Key; private String ent2Type; private String ent2Key; private Long published; private Long updated; private static final int DEFAULT_TOP_VALUE = DcCoreConfig.getTopQueryDefaultSize(); /** * コンストラクタ. */ public LinkDocHandler() { this.id = null; this.cellId = null; this.boxId = null; this.nodeId = null; this.ent1Type = null; this.ent1Key = null; this.ent2Type = null; this.ent2Key = null; this.published = null; this.updated = null; } /** * コンストラクタ. * @param srcHandler OEntityDocHandler * @param tgtHandler OEntityDocHandler */ public LinkDocHandler(final EntitySetDocHandler srcHandler, final EntitySetDocHandler tgtHandler) { this.cellId = srcHandler.getCellId(); this.boxId = srcHandler.getBoxId(); this.nodeId = srcHandler.getNodeId(); String srcType = srcHandler.getType(); String srcId = srcHandler.getId(); String tgtType = tgtHandler.getType(); String tgtId = tgtHandler.getId(); // ES 保存時の一意キー作成 TreeMap<String, String> tm = new TreeMap<String, String>(); tm.put(srcType, srcId); tm.put(tgtType, tgtId); this.ent1Type = tm.firstKey(); this.ent2Type = tm.lastKey(); this.ent1Key = tm.get(ent1Type); this.ent2Key = tm.get(ent2Type); this.id = this.createLinkId(); long dateTime = new Date().getTime(); this.published = dateTime; this.updated = dateTime; } /** * 検索結果からLinkDocHandlerを生成するコンストラクタ. * @param searchHit 検索結果データ */ public LinkDocHandler(final DcSearchHit searchHit) { this.id = searchHit.getId(); Map<String, Object> source = searchHit.getSource(); this.cellId = source.get(KEY_CELL_ID).toString(); if (source.containsKey(KEY_BOX_ID) && source.get(KEY_BOX_ID) != null) { this.boxId = source.get(KEY_BOX_ID).toString(); } if (source.containsKey(KEY_NODE_ID) && source.get(KEY_NODE_ID) != null) { this.nodeId = source.get(KEY_NODE_ID).toString(); } // ES 保存時の一意キー作成 String srcType = source.get(KEY_ENT1_TYPE).toString(); String srcId = source.get(KEY_ENT1_ID).toString(); String tgtType = source.get(KEY_ENT2_TYPE).toString(); String tgtId = source.get(KEY_ENT2_ID).toString(); TreeMap<String, String> tm = new TreeMap<String, String>(); tm.put(srcType, srcId); tm.put(tgtType, tgtId); this.ent1Type = tm.firstKey(); this.ent2Type = tm.lastKey(); this.ent1Key = tm.get(ent1Type); this.ent2Key = tm.get(ent2Type); this.published = Long.parseLong(source.get(KEY_PUBLISHED).toString()); this.updated = Long.parseLong(source.get(KEY_UPDATED).toString()); } /** * @return the ent1Type */ public String getEnt1Type() { return ent1Type; } /** * @param ent1Type the ent1Type to set */ public void setEnt1Type(String ent1Type) { this.ent1Type = ent1Type; } /** * @return the ent1Key */ public String getEnt1Key() { return ent1Key; } /** * @param ent1Key the ent1Key to set */ public void setEnt1Key(String ent1Key) { this.ent1Key = ent1Key; } /** * @return the ent2Type */ public String getEnt2Type() { return ent2Type; } /** * @param ent2Type the ent2Type to set */ public void setEnt2Type(String ent2Type) { this.ent2Type = ent2Type; } /** * @return the ent2Key */ public String getEnt2Key() { return ent2Key; } /** * @param ent2Key the ent2Key to set */ public void setEnt2Key(String ent2Key) { this.ent2Key = ent2Key; } @Override public String getId() { return this.id; } /** * @return cell id */ public String getCellId() { return cellId; } /** * @param cellid cell Id */ public void setCellId(String cellid) { this.cellId = cellid; } /** * @return box id */ public String getBoxId() { return boxId; } /** * @param boxid box id */ public void setBoxId(String boxid) { this.boxId = boxid; } /** * @return node id */ public String getNodeId() { return nodeId; } /** * @param nodeid node id */ public void setNodeId(String nodeid) { this.nodeId = nodeid; } @Override public Long getVersion() { return null; } @Override public Map<String, Object> getSource() { return this.createLinkDoc(); } /** * @return Json */ public Map<String, Object> createLinkDoc() { Map<String, Object> ret = new HashMap<String, Object>(); if (this.cellId != null) { ret.put(KEY_CELL_ID, this.cellId); } ret.put(KEY_BOX_ID, this.boxId); ret.put(KEY_NODE_ID, this.nodeId); ret.put(KEY_ENT1_TYPE, this.ent1Type); ret.put(KEY_ENT1_ID, this.ent1Key); ret.put(KEY_ENT2_TYPE, this.ent2Type); ret.put(KEY_ENT2_ID, this.ent2Key); ret.put(KEY_PUBLISHED, this.published); ret.put(KEY_UPDATED, this.updated); return ret; } /** * @return ID */ public String createLinkId() { return this.ent1Key + "-" + this.ent2Key; } /** * N:Nのリンクのドキュメント件数を取得する. * @param accessor link用のaccessor * @param srcHandler OEntityDocHandler * @param targetSetName targetSetName * @param targetEntityTypeId targetEntityTypeId * @return ドキュメント件数 */ public static long getNtoNCount( final ODataLinkAccessor accessor, final EntitySetDocHandler srcHandler, final String targetSetName, final String targetEntityTypeId) { NtoNQueryParameter parameter = new NtoNQueryParameter(srcHandler, targetSetName, targetEntityTypeId); return accessor.count(parameter.getSource(0, 0)); } /** * N:Nのリンクの一覧を取得する. * @param accessor link用のaccessor * @param srcHandler OEntityDocHandler * @param targetSetName targetSetName * @param targetEntityTypeId targetEntityTypeId * @param queryInfo queryInfo * @return ESQuery */ public static List<String> query(final ODataLinkAccessor accessor, final EntitySetDocHandler srcHandler, final String targetSetName, final String targetEntityTypeId, final QueryInfo queryInfo) { NtoNQueryParameter parameter = new NtoNQueryParameter(srcHandler, targetSetName, targetEntityTypeId); // IDの一覧を検索 Integer size = DEFAULT_TOP_VALUE; Integer from = 0; if (queryInfo != null) { if (queryInfo.top != null) { size = queryInfo.top; } if (queryInfo.skip != null) { from = queryInfo.skip; } } List<String> ret = new ArrayList<String>(); DcSearchResponse sr = accessor.search(parameter.getSource(size, from)); if (sr == null) { return ret; } for (DcSearchHit hit : sr.getHits().getHits()) { Map<String, Object> hs = hit.getSource(); ret.add((String) hs.get(parameter.getTargetKey())); } return ret; } /** * @return the published */ public Long getPublished() { return published; } /** * @param published the published to set */ public void setPublished(Long published) { this.published = published; } /** * @return the updated */ public Long getUpdated() { return updated; } /** * @param updated the updated to set */ public void setUpdated(Long updated) { this.updated = updated; } /** * @param id the id to set */ public void setId(String id) { this.id = id; } /** * ES上のOData Link格納において更新日時を保存するJSONキー. */ public static final String KEY_UPDATED = "u"; /** * ES上のOData Link格納において作成日時を保存するJSONキー. */ public static final String KEY_PUBLISHED = "p"; /** * ES上のOData Link格納においてCellの内部IDを保存するJSONキー. */ public static final String KEY_CELL_ID = "c"; /** * ES上のOData Link格納においてBoxの内部IDを保存するJSONキー. */ public static final String KEY_BOX_ID = "b"; /** * ES上のOData Link格納においてコレクションのnodeidを保存するJSONキー. */ public static final String KEY_NODE_ID = "n"; /** * ES上のOData Link格納において文字列比較で小さい側のタイプ名を保存するJSONキー. */ public static final String KEY_ENT1_TYPE = "t1"; /** * ES上のOData Link格納において文字列比較で小さい側のタイプのエンティティIDを保存するJSONキー. */ public static final String KEY_ENT1_ID = "k1"; /** * ES上のOData Link格納において文字列比較で大きい側のタイプ名を保存するJSONキー. */ public static final String KEY_ENT2_TYPE = "t2"; /** * ES上のOData Link格納において文字列比較で大きい側のタイプのエンティティIDを保存するJSONキー. */ public static final String KEY_ENT2_ID = "k2"; /** * 指定されたエンティティタイプと関連付いているデータのIDを返却する. * @param baseEntityType エンティティタイプ * @return ID 存在しない場合はnullを返却 */ public String getLinkedEntitytIdFromBaseEntityType(String baseEntityType) { if (this.ent1Type.equals(baseEntityType)) { return this.ent2Key; } else if (this.ent2Type.equals(baseEntityType)) { return this.ent1Key; } else { return null; } } /** * 指定されたエンティティタイプのIDを返却する. * @param entityType エンティティタイプ * @return ID 存在しない場合はnullを返却 */ public String getEntitytIdFromEntityType(String entityType) { if (this.ent1Type.equals(entityType)) { return this.ent1Key; } else if (this.ent2Type.equals(entityType)) { return this.ent2Key; } else { return null; } } /** * N:Nのリンクの一覧を取得するためのクエリ情報を生成するクラス. */ public static class NtoNQueryParameter { private String t1; private String t2; private String k1; private String k2; /** * コンストラクタ. * @param srcHandler OEntityDocHandler * @param targetSetName ターゲット側のEntitySet名 * @param targetEntityTypeId ターゲット側のEntityTypeID */ public NtoNQueryParameter( final EntitySetDocHandler srcHandler, final String targetSetName, final String targetEntityTypeId) { String srcSetName = srcHandler.getType(); String srcId = srcHandler.getId(); TreeMap<String, String> tm = new TreeMap<String, String>(); if (srcSetName.equals(UserDataODataProducer.USER_ODATA_NAMESPACE)) { tm.put(srcHandler.getEntityTypeId(), srcId); tm.put(targetEntityTypeId, ""); } else { tm.put(srcSetName, srcId); tm.put(targetSetName, ""); } this.t1 = tm.firstKey(); this.t2 = tm.lastKey(); this.k1 = tm.get(t1); this.k2 = tm.get(t2); } /** * N:Nのリンクの一覧を取得するためのクエリ情報を生成する(ソートなし). * @param size 取得する件数 * @param from フェッチ数 * @return ESQuery */ public Map<String, Object> getSource(Integer size, Integer from) { Map<String, Object> source = new HashMap<String, Object>(); Map<String, Object> filter = new HashMap<String, Object>(); Map<String, Object> and = new HashMap<String, Object>(); List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>(); if (this.k1.length() == 0) { filters.add(QueryMapFactory.termQuery(KEY_ENT2_ID, this.k2)); } else { filters.add(QueryMapFactory.termQuery(KEY_ENT1_ID, this.k1)); } List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>(); queries.add(QueryMapFactory.termQuery(KEY_ENT1_TYPE, this.t1)); queries.add(QueryMapFactory.termQuery(KEY_ENT2_TYPE, this.t2)); Map<String, Object> query = QueryMapFactory.filteredQuery(null, QueryMapFactory.mustQuery(queries)); and.put("filters", filters); filter.put("and", and); source.put("query", query); source.put("filter", filter); source.put("size", size); source.put("from", from); return source; } /** * Expand対象データ取得時のN:Nのリンクの一覧を取得するためのクエリ情報を生成する. * @param size 取得する件数 * @return ESQuery */ public Map<String, Object> getSourceForExpand(Integer size) { Map<String, Object> source = new HashMap<String, Object>(); List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>(); if (this.k1.length() == 0) { queries.add(QueryMapFactory.termQuery(KEY_ENT2_ID, this.k2)); } else { queries.add(QueryMapFactory.termQuery(KEY_ENT1_ID, this.k1)); } queries.add(QueryMapFactory.termQuery(KEY_ENT1_TYPE, this.t1)); queries.add(QueryMapFactory.termQuery(KEY_ENT2_TYPE, this.t2)); Map<String, Object> query = QueryMapFactory.filteredQuery(null, QueryMapFactory.mustQuery(queries)); source.put("query", query); source.put("sort", QueryMapFactory.sortQuery(getTargetKey(), EsQueryHandler.SORT_ASC)); source.put("size", size); return source; } /** * ターゲット側のキー名(k1, k2)を取得する. */ String getTargetKey() { if (this.k1.length() == 0) { return KEY_ENT1_ID; } else { return KEY_ENT2_ID; } } } }