/** * 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.odata; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.apache.commons.lang.StringEscapeUtils; import org.odata4j.edm.EdmEntityType; import org.odata4j.edm.EdmProperty; import org.odata4j.expression.AddExpression; import org.odata4j.expression.AggregateAllFunction; import org.odata4j.expression.AggregateAnyFunction; import org.odata4j.expression.AndExpression; import org.odata4j.expression.BinaryLiteral; import org.odata4j.expression.BoolParenExpression; import org.odata4j.expression.BooleanLiteral; import org.odata4j.expression.ByteLiteral; import org.odata4j.expression.CastExpression; import org.odata4j.expression.CeilingMethodCallExpression; import org.odata4j.expression.CommonExpression; import org.odata4j.expression.ConcatMethodCallExpression; import org.odata4j.expression.DateTimeLiteral; import org.odata4j.expression.DateTimeOffsetLiteral; import org.odata4j.expression.DayMethodCallExpression; import org.odata4j.expression.DecimalLiteral; import org.odata4j.expression.DivExpression; import org.odata4j.expression.DoubleLiteral; import org.odata4j.expression.EndsWithMethodCallExpression; import org.odata4j.expression.EntitySimpleProperty; import org.odata4j.expression.EqExpression; import org.odata4j.expression.ExpressionVisitor; import org.odata4j.expression.FloorMethodCallExpression; import org.odata4j.expression.GeExpression; import org.odata4j.expression.GtExpression; import org.odata4j.expression.GuidLiteral; import org.odata4j.expression.HourMethodCallExpression; import org.odata4j.expression.IndexOfMethodCallExpression; import org.odata4j.expression.Int64Literal; import org.odata4j.expression.IntegralLiteral; import org.odata4j.expression.IsofExpression; import org.odata4j.expression.LeExpression; import org.odata4j.expression.LengthMethodCallExpression; import org.odata4j.expression.LtExpression; import org.odata4j.expression.MinuteMethodCallExpression; import org.odata4j.expression.ModExpression; import org.odata4j.expression.MonthMethodCallExpression; import org.odata4j.expression.MulExpression; import org.odata4j.expression.NeExpression; import org.odata4j.expression.NegateExpression; import org.odata4j.expression.NotExpression; import org.odata4j.expression.NullLiteral; import org.odata4j.expression.OrExpression; import org.odata4j.expression.OrderByExpression; import org.odata4j.expression.OrderByExpression.Direction; import org.odata4j.expression.ParenExpression; import org.odata4j.expression.ReplaceMethodCallExpression; import org.odata4j.expression.RoundMethodCallExpression; import org.odata4j.expression.SByteLiteral; import org.odata4j.expression.SecondMethodCallExpression; import org.odata4j.expression.SingleLiteral; import org.odata4j.expression.StartsWithMethodCallExpression; import org.odata4j.expression.StringLiteral; import org.odata4j.expression.SubExpression; import org.odata4j.expression.SubstringMethodCallExpression; import org.odata4j.expression.SubstringOfMethodCallExpression; import org.odata4j.expression.TimeLiteral; import org.odata4j.expression.ToLowerMethodCallExpression; import org.odata4j.expression.ToUpperMethodCallExpression; import org.odata4j.expression.TrimMethodCallExpression; import org.odata4j.expression.YearMethodCallExpression; import org.odata4j.producer.QueryInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.core.DcCoreConfig; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.model.ctl.Common; import com.fujitsu.dc.core.model.impl.es.QueryMapFactory; import com.fujitsu.dc.core.model.impl.es.doc.OEntityDocHandler; /** * ODataの$filterをはじめとするクエリをESのJSONベースQueryDSLに変換する. * $filterはOata4JではBoolCommonExpressionに変換される。 * OData4JではExpressionの評価にはVisitorパターンを採用しており、 * 本クラスはここでVisitorとして振る舞うべくExpressionVisitorを実装している。 * ひと通りVisitを終えたのち、本オブジェクトにgetSource()すると、 * ESのSearchRequestに渡すべきJSONが取得できる。 * Personium.ioでサポートしていないクエリに関しては、例外をスローする。 */ public class EsQueryHandler implements ExpressionVisitor, ODataQueryHandler { private static final int DEFAULT_TOP_VALUE = DcCoreConfig.getTopQueryDefaultSize(); EdmEntityType entityType; Map<String, Object> source; Map<String, Object> current; Stack<Map<String, Object>> stack = new Stack<Map<String, Object>>(); Map<String, Object> orderBy; /** * SORT_ASC 昇順. */ public static final String SORT_ASC = "asc"; /** * SORT_DESC 降順. */ public static final String SORT_DESC = "desc"; /** * ログ. */ static Logger log = LoggerFactory.getLogger(EsQueryHandler.class); /** * コンストラクタ. */ public EsQueryHandler() { this.source = new HashMap<String, Object>(); this.stack.push(this.source); this.current = new HashMap<String, Object>(); this.source.put("filter", this.current); } /** * コンストラクタ2. * $filter, $skip, $top, $orderby, $select を処理する。 * $expand は対応していない。 * @param entityType エンティティタイプ */ public EsQueryHandler(EdmEntityType entityType) { this.source = new HashMap<String, Object>(); this.entityType = entityType; } /** * 初期化. * @param queryInfo OData4jのQueryInfo. * @param implicitConds 暗黙検索条件. */ public void initialize(QueryInfo queryInfo, List<Map<String, Object>> implicitConds) { List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>(); if (queryInfo != null) { if (queryInfo.filter != null) { this.stack.push(this.source); this.current = new HashMap<String, Object>(); filters.add(this.current); queryInfo.filter.visit(this); } if (queryInfo.customOptions != null && !queryInfo.customOptions.isEmpty()) { String keywords = queryInfo.customOptions.get("q"); if (keywords != null && !keywords.equals("")) { // 半角空白が指定された場合は、AND検索とする for (String keyword : keywords.split(" ")) { if (keyword.isEmpty()) { continue; } Map<String, Object> map = new HashMap<String, Object>(); map.put("query", keyword); map.put("operator", "and"); map.put("type", "phrase"); Map<String, Object> all = new HashMap<String, Object>(); all.put("_all", map); Map<String, Object> match = new HashMap<String, Object>(); match.put("match", all); Map<String, Object> query = new HashMap<String, Object>(); query.put("query", match); filters.add(query); } } } this.setTop(queryInfo.top); this.setSkip(queryInfo.skip); this.setOrderBy(queryInfo.orderBy); this.setSelect(queryInfo.select); } Map<String, Object> filter = new HashMap<String, Object>(); if (!filters.isEmpty()) { Map<String, Object> and = new HashMap<String, Object>(); and.put("filters", filters); filter.put("and", and); } this.source.put("filter", filter); // 暗黙条件の設定があるとき if (implicitConds != null && implicitConds.size() != 0) { Map<String, Object> query = QueryMapFactory.filteredQuery(null, QueryMapFactory.mustQuery(implicitConds)); this.source.put("query", query); } // _versionを返却する this.source.put("version", true); } /** * @param top $topの値 */ public void setTop(Integer top) { if (top != null) { this.source.put("size", top); } else { this.source.put("size", DEFAULT_TOP_VALUE); } } /** * @param skip $skipの値 */ public void setSkip(Integer skip) { if (skip != null) { this.source.put("from", skip); } } /** * @param orderBy $orderByの値 */ public void setOrderBy(List<OrderByExpression> orderBy) { if (orderBy != null) { List<Map<String, Object>> sort = new ArrayList<Map<String, Object>>(); for (OrderByExpression order : orderBy) { this.orderBy = new HashMap<String, Object>(); order.visit(this); if (!this.orderBy.isEmpty()) { sort.add(this.orderBy); } } this.source.put("sort", sort); } } /** * @param selects $selectの値 */ public void setSelect(List<EntitySimpleProperty> selects) { getSelectQuery(this.source, selects); } /** * $selectの値からES検索用のクエリを組立てる. * @param baseSource 入力値を格納したMap * @param selects $select */ public void getSelectQuery(Map<String, Object> baseSource, List<EntitySimpleProperty> selects) { if (selects != null && selects.size() > 0) { // fieldsクエリの組立 List<String> fields = new ArrayList<String>(); fields.add(OEntityDocHandler.KEY_STATIC_FIELDS + "." + Common.P_ID.getName()); fields.add(OEntityDocHandler.KEY_PUBLISHED); fields.add(OEntityDocHandler.KEY_UPDATED); fields.add(OEntityDocHandler.KEY_CELL_ID); fields.add(OEntityDocHandler.KEY_BOX_ID); fields.add(OEntityDocHandler.KEY_NODE_ID); fields.add(OEntityDocHandler.KEY_ENTITY_ID); for (EntitySimpleProperty select : selects) { if (select == null) { // $selectで指定された値がプロパティ名でなかった場合 throw DcCoreException.OData.SELECT_PARSE_ERROR; } String prop = select.getPropertyName(); if (!Common.P_ID.getName().equals(prop) && !Common.P_PUBLISHED.getName().equals(prop) && !Common.P_UPDATED.getName().equals(prop) && !"__metadata".equals(prop)) { String fieldName = getFieldName(prop); fields.add(fieldName); } } // selectのfield指定方法がEs0.19とEs1.X系とで異なる // この部分の差異をHelperが対応する EsQueryHandlerHelper.composeSourceFilter(baseSource, fields); } } /** * フィールド名を取得する. * @param prop プロパティ名 * @return フィールド名 */ protected String getFieldName(String prop) { String fieldName = OEntityDocHandler.KEY_STATIC_FIELDS + "." + prop; return fieldName; } /** * 検索クエリを取得する. * @return 検索クエリ. */ public Map<String, Object> getSource() { log.debug(this.source.toString()); return this.source; } /** * 左辺処理前の共通処理. */ @Override public void beforeDescend() { } /** * 左辺、右辺の処理後の共通処理. */ @Override public void afterDescend() { } /** * 左辺処理後、右辺処理前の共通処理. */ @Override public void betweenDescend() { } @Override public void visit(String type) { log.debug("visit(String type)"); } @Override public void visit(OrderByExpression expr) { log.debug("visit(OrderByExpression expr)"); if (!(expr.getExpression() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } // ソートクエリを設定する String key = getSearchKey(expr.getExpression(), true); Map<String, Object> sortOption = new HashMap<String, Object>(); sortOption.put("order", getOrderOption(expr.getDirection())); sortOption.put("ignore_unmapped", true); this.orderBy.put(key, sortOption); } /** * $orderbyのオプションを取得する. * @param option odata4jのオプション * @return optionValue 取得したオプション */ public String getOrderOption(Direction option) { String optionValue; // デフォルト値は昇順(ASCENDING) if (option == null || option.equals(Direction.ASCENDING)) { optionValue = SORT_ASC; } else { optionValue = SORT_DESC; } return optionValue; } @Override public void visit(Direction direction) { log.debug("visit(Direction direction)"); } @Override public void visit(AddExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(AndExpression expr) { log.debug("visit(AndExpression expr)"); List<Object> andList = new ArrayList<Object>(); Map<String, Object> lhs = new HashMap<String, Object>(); Map<String, Object> rhs = new HashMap<String, Object>(); andList.add(lhs); andList.add(rhs); this.current.put("and", andList); this.current = lhs; stack.push(rhs); } @Override public void visit(OrExpression expr) { log.debug("visit(OrExpression expr)"); List<Object> orList = new ArrayList<Object>(); Map<String, Object> lhs = new HashMap<String, Object>(); Map<String, Object> rhs = new HashMap<String, Object>(); orList.add(lhs); orList.add(rhs); this.current.put("or", orList); this.current = lhs; stack.push(rhs); } /** * 完全一致検索時のvisit. * @param expr EqExpression */ @Override public void visit(EqExpression expr) { log.debug("visit(EqExpression expr)"); // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterEqCondition(edmProperty, searchValue); // 検索クエリを設定する // 検索対象がnullの場合、{"missing":{"field":"xxx"}}を作成する if (expr.getRHS() instanceof NullLiteral) { Map<String, Object> missing = new HashMap<String, Object>(); missing.put("field", getSearchKey(expr.getLHS(), true)); this.current.put("missing", missing); this.current = stack.pop(); } else { // 検索対象がnull以外の場合、termクエリを作成する Map<String, Object> term = new HashMap<String, Object>(); term.put(getSearchKey(expr.getLHS(), true), getSearchValue(expr.getRHS())); this.current.put("term", term); this.current = stack.pop(); } } /** * 検索条件に指定されたキーのスキーマ定義を取得する. * 以下の場合はエラーとなる * <ul> * <li>__metadataが指定された場合</li> * <li>未定義のPropertyが指定された場合</li> * <li>Propretyの命名規約に従っていない名前が指定された場合 <br /> * (上記はPropertyとして登録できないため、スキーマ定義が取得できないことで書式エラーとみなす)</li> * </ul> * @param searchKey * @return EdmProperty */ private EdmProperty getEdmProprety(EntitySimpleProperty searchKey) { String propertyName = searchKey.getPropertyName(); EdmProperty edmProperty = this.entityType.findProperty(propertyName); if (null == edmProperty) { throw DcCoreException.OData.UNKNOWN_QUERY_KEY.params(propertyName); } return edmProperty; } /** * elasticsearchの検索文字列を返却する. * @param expr CommonExpression * @return elasticsearchの検索文字列 */ private Object getSearchValue(CommonExpression expr) { if (expr instanceof IntegralLiteral) { return ((IntegralLiteral) expr).getValue(); } else if (expr instanceof Int64Literal) { return ((Int64Literal) expr).getValue(); } else if (expr instanceof DoubleLiteral) { return ((DoubleLiteral) expr).getValue(); } else if (expr instanceof BooleanLiteral) { return ((BooleanLiteral) expr).getValue(); } else { String value; try { value = StringEscapeUtils.unescapeJavaScript(((StringLiteral) expr).getValue()); } catch (Exception e) { log.info("Failed to unescape searchValue.", e); throw DcCoreException.OData.OPERATOR_AND_OPERAND_UNABLE_TO_UNESCAPE.params(((StringLiteral) expr) .getValue()); } return value; } } /** * elasticsearchの検索キーを返却する. * @param expr CommonExpression * @return elasticsearchの検索キー */ private String getSearchKey(CommonExpression expr) { return getSearchKey(expr, false); } /** * elasticsearchの検索キーを返却する. * @param expr CommonExpression * @param isUntouched isUntouched * @return elasticsearchの検索キー */ protected String getSearchKey(CommonExpression expr, Boolean isUntouched) { // 検索キーとして設定を行う String keyName = ((EntitySimpleProperty) expr).getPropertyName(); // published, updated if (Common.P_PUBLISHED.getName().equals(keyName)) { return OEntityDocHandler.KEY_PUBLISHED; } else if (Common.P_UPDATED.getName().equals(keyName)) { return OEntityDocHandler.KEY_UPDATED; } // スキーマ定義項目であればs.フィールド、定義外項目であればd.フィールドを検索する String fieldPrefix = OEntityDocHandler.KEY_STATIC_FIELDS + "."; // untouchedフィールドの指定であれば、untouchedを返却する if (isUntouched) { return fieldPrefix + keyName + ".untouched"; } else { return fieldPrefix + keyName; } } @Override public void visit(BooleanLiteral expr) { } @Override public void visit(CastExpression expr) { } @Override public void visit(ConcatMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(DateTimeLiteral expr) { } @Override public void visit(DateTimeOffsetLiteral expr) { } @Override public void visit(DecimalLiteral expr) { } @Override public void visit(DivExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(EndsWithMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } /** * EntitySimplePropertyのvisit. * @param expr EntitySimpleProperty */ @Override public void visit(EntitySimpleProperty expr) { } @Override public void visit(GeExpression expr) { log.debug("visit(GeExpression expr)"); // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterOpCondition(edmProperty, searchValue); // ESの Range filterを設定する Map<String, Object> ge = new HashMap<String, Object>(); Map<String, Object> property = new HashMap<String, Object>(); ge.put("gte", getSearchValue(expr.getRHS())); property.put(getSearchKey(expr.getLHS(), true), ge); this.current.put("range", property); this.current = stack.pop(); } @Override public void visit(GtExpression expr) { log.debug("visit(GtExpression expr)"); // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterOpCondition(edmProperty, searchValue); Map<String, Object> gt = new HashMap<String, Object>(); Map<String, Object> property = new HashMap<String, Object>(); gt.put("gt", getSearchValue(expr.getRHS())); property.put(getSearchKey(expr.getLHS(), true), gt); this.current.put("range", property); this.current = stack.pop(); } @Override public void visit(GuidLiteral expr) { } @Override public void visit(BinaryLiteral expr) { } @Override public void visit(ByteLiteral expr) { } @Override public void visit(SByteLiteral expr) { } @Override public void visit(IndexOfMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(SingleLiteral expr) { log.debug("visit(SingleLiteral expr)"); } @Override public void visit(DoubleLiteral expr) { } @Override public void visit(IntegralLiteral expr) { } @Override public void visit(Int64Literal expr) { } @Override public void visit(IsofExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(LeExpression expr) { log.debug("visit(LeExpression expr)"); // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterOpCondition(edmProperty, searchValue); // ESの Range filterを設定する Map<String, Object> le = new HashMap<String, Object>(); Map<String, Object> property = new HashMap<String, Object>(); le.put("lte", getSearchValue(expr.getRHS())); property.put(getSearchKey(expr.getLHS(), true), le); this.current.put("range", property); this.current = stack.pop(); } @Override public void visit(LengthMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(LtExpression expr) { log.debug("visit(LtExpression expr)"); // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterOpCondition(edmProperty, searchValue); // ESの Range filterを設定する Map<String, Object> lt = new HashMap<String, Object>(); Map<String, Object> property = new HashMap<String, Object>(); lt.put("lt", getSearchValue(expr.getRHS())); property.put(getSearchKey(expr.getLHS(), true), lt); this.current.put("range", property); this.current = stack.pop(); } @Override public void visit(ModExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(MulExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(NeExpression expr) { // $filterに指定された検索条件のプロパティが単純型ではない場合は、パースエラーとする。 if (!(expr.getLHS() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getLHS()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 CommonExpression searchValue = expr.getRHS(); FilterConditionValidator.validateFilterEqCondition(edmProperty, searchValue); // 検索クエリ(not filter)を設定する // 検索対象がnullの場合、{"missing":{"field":"xxx"}}を作成する if (expr.getRHS() instanceof NullLiteral) { Map<String, Object> field = new HashMap<String, Object>(); Map<String, Object> missing = new HashMap<String, Object>(); Map<String, Object> filter = new HashMap<String, Object>(); field.put("field", getSearchKey(expr.getLHS(), true)); missing.put("missing", field); filter.put("filter", missing); this.current.put("not", filter); this.current = stack.pop(); } else { // 検索対象がnull以外の場合、termクエリを作成する Map<String, Object> field = new HashMap<String, Object>(); Map<String, Object> term = new HashMap<String, Object>(); Map<String, Object> filter = new HashMap<String, Object>(); field.put(getSearchKey(expr.getLHS(), true), getSearchValue(expr.getRHS())); term.put("term", field); filter.put("filter", term); this.current.put("not", filter); this.current = stack.pop(); } } @Override public void visit(NegateExpression expr) { } @Override public void visit(NotExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(NullLiteral expr) { } @Override public void visit(ParenExpression expr) { log.debug("visit(ParenExpression expr)"); } @Override public void visit(BoolParenExpression expr) { } @Override public void visit(ReplaceMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(StartsWithMethodCallExpression expr) { log.debug("visit(StartsWithMethodCallExpression expr)"); // 左辺辺がプロパティ、右辺が文字列でない場合はパースエラーとする if (!(expr.getTarget() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getTarget()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 FilterConditionValidator.validateFilterFuncCondition(edmProperty, expr.getValue()); // 検索クエリを設定する Map<String, Object> prefix = new HashMap<String, Object>(); prefix.put(getSearchKey(expr.getTarget(), true), getSearchValue(expr.getValue())); this.current.put("prefix", prefix); this.current = stack.pop(); } /** * 文字列のvisit. * @param expr StringLiteral */ @Override public void visit(StringLiteral expr) { } @Override public void visit(SubExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_OPERATOR; } @Override public void visit(SubstringMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(SubstringOfMethodCallExpression expr) { log.debug("visit(SubstringOfMethodCallExpression expr)"); // 左辺が文字列、右辺がプロパティでない場合はパースエラーとする if (!(expr.getTarget() instanceof EntitySimpleProperty)) { throw DcCoreException.OData.FILTER_PARSE_ERROR; } EdmProperty edmProperty = getEdmProprety((EntitySimpleProperty) expr.getTarget()); // $filterに指定されたプロパティの型と検索条件の値として指定されたデータ型の検証 FilterConditionValidator.validateFilterFuncCondition(edmProperty, expr.getValue()); // 検索クエリを設定する Map<String, Object> searchKey = new HashMap<String, Object>(); Map<String, Object> query = new HashMap<String, Object>(); Map<String, Object> text = new HashMap<String, Object>(); searchKey.put("query", getSearchValue(expr.getValue())); searchKey.put("type", "phrase"); text.put(getSearchKey(expr.getTarget()), searchKey); query.put("match", text); this.current.put("query", query); this.current = stack.pop(); } @Override public void visit(TimeLiteral expr) { } @Override public void visit(ToLowerMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(ToUpperMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(TrimMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(YearMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(MonthMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(DayMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(HourMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(MinuteMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(SecondMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(RoundMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(FloorMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(CeilingMethodCallExpression expr) { throw DcCoreException.OData.UNSUPPORTED_QUERY_FUNCTION; } @Override public void visit(AggregateAnyFunction expr) { } @Override public void visit(AggregateAllFunction expr) { } }