com.gigaspaces.persistency.datasource.MongoQueryFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.gigaspaces.persistency.datasource.MongoQueryFactory.java

Source

/*
 * Copyright (c) 2008-2016, GigaSpaces Technologies, Inc. All Rights Reserved.
 *
 * 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.gigaspaces.persistency.datasource;

import com.gigaspaces.datasource.DataSourceQuery;
import com.gigaspaces.metadata.SpaceTypeDescriptor;
import com.gigaspaces.persistency.Constants;
import com.gigaspaces.persistency.metadata.DefaultSpaceDocumentMapper;
import com.gigaspaces.persistency.metadata.SpaceDocumentMapper;
import com.gigaspaces.persistency.parser.SQL2MongoBaseVisitor;
import com.gigaspaces.persistency.parser.SQL2MongoLexer;
import com.gigaspaces.persistency.parser.SQL2MongoParser;
import com.gigaspaces.persistency.parser.SQL2MongoParser.ParseContext;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStream;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * @author Shadi Massalha
 */
public class MongoQueryFactory {

    private static final String LIKE = "like()";
    private static final String RLIKE = "rlike()";
    private static final String PARAM_PLACEHOLDER = "'%{}'";
    private static final Map<SpaceTypeDescriptor, Map<String, String>> cachedQuery = new ConcurrentHashMap<SpaceTypeDescriptor, Map<String, String>>();

    public static BasicDBObjectBuilder create(DataSourceQuery sql) {
        Map<String, String> cache = cachedQuery.get(sql.getTypeDescriptor());

        if (cache == null) {
            cache = new HashMap<String, String>();

            cachedQuery.put(sql.getTypeDescriptor(), cache);
        }

        String query = sql.getAsSQLQuery().getQuery();

        String parsedQuery = cache.get(query);

        if (parsedQuery == null) {
            parsedQuery = parse(query);

            cache.put(query, parsedQuery);
        }
        BasicDBObjectBuilder queryResult = bind(parsedQuery, sql.getAsSQLQuery().getQueryParameters(),
                sql.getTypeDescriptor());

        replaceIdProperty(queryResult, sql.getTypeDescriptor());

        return queryResult;
    }

    @SuppressWarnings("static-access")
    private static void replaceIdProperty(BasicDBObjectBuilder qResult, SpaceTypeDescriptor typeDescriptor) {

        DBObject q = qResult.get();

        if (q.containsField(typeDescriptor.getIdPropertyName())) {

            Object value = q.get(typeDescriptor.getIdPropertyName());

            q.removeField(typeDescriptor.getIdPropertyName());

            q.put(Constants.ID_PROPERTY, value);

            qResult.start(q.toMap());
        }
    }

    private static String parse(String sql) {
        ANTLRInputStream charstream = new ANTLRInputStream(sql);

        SQL2MongoLexer lexer = new SQL2MongoLexer(charstream);

        TokenStream tokenStream = new CommonTokenStream(lexer);

        SQL2MongoParser parser = new SQL2MongoParser(tokenStream);

        SQL2MongoBaseVisitor<ParseContext> visitor = new SQL2MongoBaseVisitor<ParseContext>();

        parser.parse().accept(visitor);

        return visitor.getQuery().toString();
    }

    public static BasicDBObjectBuilder bind(String parsedQuery, Object[] parameters,
            SpaceTypeDescriptor spaceTypeDescriptor) {

        SpaceDocumentMapper<DBObject> mapper = new DefaultSpaceDocumentMapper(spaceTypeDescriptor);

        DBObject obj = (DBObject) JSON.parse(parsedQuery);

        BasicDBObjectBuilder query = BasicDBObjectBuilder.start(obj.toMap());

        if (parameters != null) {
            query = replaceParameters(parameters, mapper, query, 0);
        }
        return query;
    }

    private static BasicDBObjectBuilder replaceParameters(Object[] parameters, SpaceDocumentMapper<DBObject> mapper,
            BasicDBObjectBuilder builder, Integer index) {

        BasicDBObjectBuilder newBuilder = BasicDBObjectBuilder.start();

        DBObject document = builder.get();

        Iterator<String> iterator = document.keySet().iterator();

        while (iterator.hasNext()) {
            String field = iterator.next();
            Object ph = document.get(field);

            if (index >= parameters.length)
                return builder;

            if (ph instanceof String) {
                if (PARAM_PLACEHOLDER.equals(ph)) {
                    Object p = mapper.toObject(parameters[index++]);

                    if (p instanceof DBObject
                            && Constants.CUSTOM_BINARY.equals(((DBObject) p).get(Constants.TYPE))) {
                        newBuilder.add(field + "." + Constants.HASH, ((DBObject) p).get(Constants.HASH));
                    } else {
                        newBuilder.add(field, p);
                    }
                }
            } else if (ph instanceof Pattern) {
                Pattern p = (Pattern) ph;

                if (LIKE.equalsIgnoreCase(p.pattern())) {
                    newBuilder.add(field, convertLikeExpression((String) parameters[index++]));
                } else if (RLIKE.equalsIgnoreCase(p.pattern())) {
                    newBuilder.add(field, Pattern.compile((String) parameters[index++], Pattern.CASE_INSENSITIVE));
                }
            } else {
                DBObject element = (DBObject) ph;

                Object p = mapper.toObject(parameters[index]);

                if (p instanceof DBObject) {
                    String t = (String) ((DBObject) p).get(Constants.TYPE);
                    String op = element.keySet().iterator().next();

                    if (Constants.CUSTOM_BINARY.equals(t) && !op.equals("$ne"))
                        return newBuilder;
                }

                BasicDBObjectBuilder doc = replaceParameters(parameters, mapper,
                        BasicDBObjectBuilder.start(element.toMap()), index);

                newBuilder.add(field, doc.get());
            }
        }
        return newBuilder;
    }

    private static Pattern convertLikeExpression(String val) {
        if (val != null && !val.isEmpty()) {
            if (val.charAt(0) == '\'')
                val = val.substring(1);

            if (val.charAt(val.length() - 1) == '\'')
                val = val.substring(0, val.length() - 1);

            if (val.charAt(0) != '%')
                val = "^" + val;
            else
                val = val.substring(1);

            if (val.charAt(val.length() - 1) != '%')
                val = val + "$";
            else
                val = val.substring(0, val.length() - 1);

            val = val.replaceAll("%", ".*");
            val = val.replace('_', '.');

            return Pattern.compile(val);
        }

        return Pattern.compile("");
    }

}