<commit_before>/**	
 * QueryManagerNuxeoImpl.java
 *
 * {Purpose of This Class}
 *
 * {Other Notes Relating to This Class (Optional)}
 *
 * $LastChangedBy: $
 * $LastChangedRevision: $
 * $LastChangedDate: $
 *
 * This document is a part of the source code and related artifacts
 * for CollectionSpace, an open source collections management system
 * for museums and related institutions:
 *
 * http://www.collectionspace.org
 * http://wiki.collectionspace.org
 *
 * Copyright © 2009 {Contributing Institution}
 *
 * Licensed under the Educational Community License (ECL), Version 2.0.
 * You may not use this file except in compliance with this License.
 *
 * You may obtain a copy of the ECL 2.0 License at
 * https://source.collectionspace.org/collection-space/LICENSE.txt
 */
package org.collectionspace.services.common.query.nuxeo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
//import org.nuxeo.ecm.core.client.NuxeoClient;

import org.collectionspace.services.jaxb.InvocableJAXBSchema;
//import org.collectionspace.services.nuxeo.client.java.NuxeoConnector;
//import org.collectionspace.services.nuxeo.client.java.NxConnect;
import org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded;
import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded;

import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.common.invocable.InvocableUtils;
import org.collectionspace.services.common.storage.DatabaseProductType;
import org.collectionspace.services.common.storage.JDBCTools;

public class QueryManagerNuxeoImpl implements IQueryManager {

	private static String ECM_FULLTEXT_LIKE = "ecm:fulltext"
			+ SEARCH_TERM_SEPARATOR + IQueryManager.SEARCH_LIKE;
	private static String SEARCH_LIKE_FORM = null;

	private final Logger logger = LoggerFactory
			.getLogger(QueryManagerNuxeoImpl.class);

	// Consider that letters, letter-markers, numbers, '_' and apostrophe are
	// words
	private static Pattern nonWordChars = Pattern
			.compile("[^\\p{L}\\p{M}\\p{N}_']");
	private static Pattern kwdTokenizer = Pattern.compile("(?:(['\"])(.*?)(?<!\\\\)(?>\\\\\\\\)*\\1|([^ ]+))");
	private static Pattern unescapedDblQuotes = Pattern.compile("(?<!\\\\)\"");
	private static Pattern unescapedSingleQuote = Pattern.compile("(?<!\\\\)'");
	//private static Pattern kwdSearchProblemChars = Pattern.compile("[\\:\\(\\)\\*\\%]");
	// HACK to work around Nuxeo regression that tokenizes on '.'. 
	private static Pattern kwdSearchProblemChars = Pattern.compile("[\\:\\(\\)\\*\\%\\.]");
	private static Pattern kwdSearchHyphen = Pattern.compile(" - ");

	private static String getLikeForm(String dataSourceName, String repositoryName) {
		if (SEARCH_LIKE_FORM == null) {
			try {
				DatabaseProductType type = JDBCTools.getDatabaseProductType(dataSourceName, repositoryName);
				if (type == DatabaseProductType.MYSQL) {
					SEARCH_LIKE_FORM = IQueryManager.SEARCH_LIKE;
				} else if (type == DatabaseProductType.POSTGRESQL) {
					SEARCH_LIKE_FORM = IQueryManager.SEARCH_ILIKE;
				}
			} catch (Exception e) {
				SEARCH_LIKE_FORM = IQueryManager.SEARCH_LIKE;
			}
		}
		return SEARCH_LIKE_FORM;
	}

	@Override
	public String getDatasourceName() {
		return JDBCTools.NUXEO_DATASOURCE_NAME;
	}
	
	// TODO: This is currently just an example fixed query. This should
	// eventually be
	// removed or replaced with a more generic method.
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.collectionspace.services.common.query.IQueryManager#execQuery(java
	 * .lang.String)
	 */
	@Override
	@Deprecated
	public void execQuery(String queryString) {
		NuxeoClientEmbedded client = null;
		try {
			client = NuxeoConnectorEmbedded.getInstance().getClient();
			RepositoryInstance repoSession = client.openRepository();

			DocumentModelList docModelList = repoSession
					.query("SELECT * FROM Relation WHERE relations_common:subjectCsid='updated-Subject-1'");
			// DocumentModelList docModelList =
			// repoSession.query("SELECT * FROM Relation");
			// DocumentModelList docModelList =
			// repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'");
			for (DocumentModel docModel : docModelList) {
				System.out
						.println("--------------------------------------------");
				System.out.println(docModel.getPathAsString());
				System.out.println(docModel.getName());
				System.out.println(docModel.getPropertyValue("dc:title"));
				// System.out.println("subjectCsid=" +
				// docModel.getProperty("relations_common",
				// "subjectCsid").toString());
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public String createWhereClauseFromAdvancedSearch(String advancedSearch) {
		String result = null;
		//
		// Process search term.  FIXME: REM - Do we need to perform and string filtering here?
		//
		if (advancedSearch != null && !advancedSearch.isEmpty()) {
			StringBuffer advancedSearchWhereClause = new StringBuffer(
					advancedSearch);
			result = advancedSearchWhereClause.toString();
		}
		
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.collectionspace.services.common.query.IQueryManager#
	 * createWhereClauseFromKeywords(java.lang.String)
	 */
	// TODO handle keywords containing escaped punctuation chars, then we need
	// to qualify the
	// search by matching on the fulltext.simpletext field.
	// TODO handle keywords containing unescaped double quotes by matching the
	// phrase
	// against the fulltext.simpletext field.
	// Both these require using JDBC, since we cannot get to the fulltext table
	// in NXQL
	@Override
	public String createWhereClauseFromKeywords(String keywords) {
		String result = null;
		StringBuffer fullTextWhereClause = new StringBuffer();
		// Split on unescaped double quotes to handle phrases
		Matcher regexMatcher = kwdTokenizer.matcher(keywords.trim());
		boolean addNOT = false;<commit_msg>CSPACE-5828: Filter out advanced search queries consisting of only a single SQL wildcard character.<commit_after>/**	
 * QueryManagerNuxeoImpl.java
 *
 * {Purpose of This Class}
 *
 * {Other Notes Relating to This Class (Optional)}
 *
 * $LastChangedBy: $
 * $LastChangedRevision: $
 * $LastChangedDate: $
 *
 * This document is a part of the source code and related artifacts
 * for CollectionSpace, an open source collections management system
 * for museums and related institutions:
 *
 * http://www.collectionspace.org
 * http://wiki.collectionspace.org
 *
 * Copyright © 2009 {Contributing Institution}
 *
 * Licensed under the Educational Community License (ECL), Version 2.0.
 * You may not use this file except in compliance with this License.
 *
 * You may obtain a copy of the ECL 2.0 License at
 * https://source.collectionspace.org/collection-space/LICENSE.txt
 */
package org.collectionspace.services.common.query.nuxeo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
//import org.nuxeo.ecm.core.client.NuxeoClient;

import org.collectionspace.services.jaxb.InvocableJAXBSchema;
//import org.collectionspace.services.nuxeo.client.java.NuxeoConnector;
//import org.collectionspace.services.nuxeo.client.java.NxConnect;
import org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded;
import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded;

import org.collectionspace.services.client.IQueryManager;
import org.collectionspace.services.common.invocable.InvocableUtils;
import org.collectionspace.services.common.storage.DatabaseProductType;
import org.collectionspace.services.common.storage.JDBCTools;

public class QueryManagerNuxeoImpl implements IQueryManager {

	private static String ECM_FULLTEXT_LIKE = "ecm:fulltext"
			+ SEARCH_TERM_SEPARATOR + IQueryManager.SEARCH_LIKE;
	private static String SEARCH_LIKE_FORM = null;

	private final Logger logger = LoggerFactory
			.getLogger(QueryManagerNuxeoImpl.class);

	// Consider that letters, letter-markers, numbers, '_' and apostrophe are
	// words
	private static Pattern nonWordChars = Pattern
			.compile("[^\\p{L}\\p{M}\\p{N}_']");
	private static Pattern kwdTokenizer = Pattern.compile("(?:(['\"])(.*?)(?<!\\\\)(?>\\\\\\\\)*\\1|([^ ]+))");
	private static Pattern unescapedDblQuotes = Pattern.compile("(?<!\\\\)\"");
	private static Pattern unescapedSingleQuote = Pattern.compile("(?<!\\\\)'");
	//private static Pattern kwdSearchProblemChars = Pattern.compile("[\\:\\(\\)\\*\\%]");
	// HACK to work around Nuxeo regression that tokenizes on '.'. 
	private static Pattern kwdSearchProblemChars = Pattern.compile("[\\:\\(\\)\\*\\%\\.]");
	private static Pattern kwdSearchHyphen = Pattern.compile(" - ");
	private static Pattern advSearchSqlWildcard = Pattern.compile(".*?[I]*LIKE\\s*\\\"\\%\\\".*?");


	private static String getLikeForm(String dataSourceName, String repositoryName) {
		if (SEARCH_LIKE_FORM == null) {
			try {
				DatabaseProductType type = JDBCTools.getDatabaseProductType(dataSourceName, repositoryName);
				if (type == DatabaseProductType.MYSQL) {
					SEARCH_LIKE_FORM = IQueryManager.SEARCH_LIKE;
				} else if (type == DatabaseProductType.POSTGRESQL) {
					SEARCH_LIKE_FORM = IQueryManager.SEARCH_ILIKE;
				}
			} catch (Exception e) {
				SEARCH_LIKE_FORM = IQueryManager.SEARCH_LIKE;
			}
		}
		return SEARCH_LIKE_FORM;
	}
    private String SQL_WILDCARD_CHAR = "%";

	@Override
	public String getDatasourceName() {
		return JDBCTools.NUXEO_DATASOURCE_NAME;
	}
	
	// TODO: This is currently just an example fixed query. This should
	// eventually be
	// removed or replaced with a more generic method.
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.collectionspace.services.common.query.IQueryManager#execQuery(java
	 * .lang.String)
	 */
	@Override
	@Deprecated
	public void execQuery(String queryString) {
		NuxeoClientEmbedded client = null;
		try {
			client = NuxeoConnectorEmbedded.getInstance().getClient();
			RepositoryInstance repoSession = client.openRepository();

			DocumentModelList docModelList = repoSession
					.query("SELECT * FROM Relation WHERE relations_common:subjectCsid='updated-Subject-1'");
			// DocumentModelList docModelList =
			// repoSession.query("SELECT * FROM Relation");
			// DocumentModelList docModelList =
			// repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'");
			for (DocumentModel docModel : docModelList) {
				System.out
						.println("--------------------------------------------");
				System.out.println(docModel.getPathAsString());
				System.out.println(docModel.getName());
				System.out.println(docModel.getPropertyValue("dc:title"));
				// System.out.println("subjectCsid=" +
				// docModel.getProperty("relations_common",
				// "subjectCsid").toString());
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public String createWhereClauseFromAdvancedSearch(String advancedSearch) {
		String result = null;
		//
		// Process search term.  FIXME: REM - Do we need to perform any string filtering here?
		//
		if (advancedSearch != null && !advancedSearch.isEmpty()) {
                        // Filtering of advanced searches on a single '%' char, per CSPACE-5828
                    	Matcher regexMatcher = advSearchSqlWildcard.matcher(advancedSearch.trim());
                        if (regexMatcher.matches()) {
                            return "";
                        }
			StringBuffer advancedSearchWhereClause = new StringBuffer(
					advancedSearch);
			result = advancedSearchWhereClause.toString();
		}
		
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.collectionspace.services.common.query.IQueryManager#
	 * createWhereClauseFromKeywords(java.lang.String)
	 */
	// TODO handle keywords containing escaped punctuation chars, then we need
	// to qualify the
	// search by matching on the fulltext.simpletext field.
	// TODO handle keywords containing unescaped double quotes by matching the
	// phrase
	// against the fulltext.simpletext field.
	// Both these require using JDBC, since we cannot get to the fulltext table
	// in NXQL
	@Override
	public String createWhereClauseFromKeywords(String keywords) {
		String result = null;
		StringBuffer fullTextWhereClause = new StringBuffer();
		// Split on unescaped double quotes to handle phrases
		Matcher regexMatcher = kwdTokenizer.matcher(keywords.trim());
		boolean addNOT = false;
{'fraction_non_alphanumeric': 0.12416627888940593, 'fraction_numerical': 0.004498216224600589, 'mean_word_length': 3.9961255327392484, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6278064', 'n_tokens_mistral': 4207, 'n_tokens_neox': 3813, 'n_words': 894}