summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/QueryEngine/Fulltext/MySQLValueMatchConditionBuilder.php
blob: 9580e2d56a43b3b97e75be98d600760de9a2727b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php

namespace SMW\SQLStore\QueryEngine\Fulltext;

use SMW\Query\Language\ValueDescription;

/**
 * @license GNU GPL v2+
 * @since 2.5
 *
 * @author mwjames
 */
class MySQLValueMatchConditionBuilder extends ValueMatchConditionBuilder {

	/**
	 * @see ValueMatchConditionBuilder::canApplyFulltextSearchMatchCondition
	 * @since 2.5
	 *
	 * @param ValueDescription $description
	 *
	 * @return boolean
	 */
	public function canApplyFulltextSearchMatchCondition( ValueDescription $description ) {

		if ( !$this->isEnabled() ) {
			return false;
		}

		if ( $description->getProperty() !== null && $this->isExemptedProperty( $description->getProperty() ) ) {
			return false;
		}

		if ( !$this->searchTable->isValidByType( $description->getDataItem()->getDiType() ) ) {
			return false;
		}

		$matchableText = $this->getMatchableTextFromDescription(
			$description
		);

		$comparator = $description->getComparator();

		if ( $matchableText && ( $comparator === SMW_CMP_LIKE || $comparator === SMW_CMP_NLKE ) ) {

			// http://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
			// innodb_ft_min_token_size and innodb_ft_max_token_size are used
			// for InnoDB search indexes. ft_min_word_len and ft_max_word_len
			// are used for MyISAM search indexes

			// Don't count any wildcard
			return $this->hasMinTokenLength( str_replace( '*', '', $matchableText ) );
		}

		return false;
	}

	/**
	 * @see ValueMatchConditionBuilder::getWhereCondition
	 * @since 2.5
	 *
	 * @param ValueDescription $description
	 * @param string $temporaryTable
	 *
	 * @return string
	 */
	public function getWhereCondition( ValueDescription $description, $temporaryTable = '' ) {

		$affix = '';
		$matchableText = $this->getMatchableTextFromDescription(
			$description
		);

		// Any query modifier? Take care of it before any tokenizer or ngrams
		// distort the marker
		if (
			( $pos = strrpos( $matchableText, '&BOL' ) ) !== false ||
			( $pos = strrpos( $matchableText, '&INL' ) ) !== false ||
			( $pos = strrpos( $matchableText, '&QEX' ) ) !== false ) {
			$affix = mb_strcut( $matchableText, $pos );
			$matchableText = str_replace( $affix, '', $matchableText );
		}

		$value = $this->textSanitizer->sanitize(
			$matchableText,
			true
		);

		$value .= $affix;

		// A leading or trailing minus sign indicates that this word must not
		// be present in any of the rows that are returned.
		// InnoDB only supports leading minus signs.
		if ( $description->getComparator() === SMW_CMP_NLKE ) {
			$value = '-' . $value;
		}

		$temporaryTable = $temporaryTable !== '' ? $temporaryTable . '.' : '';
		$column = $temporaryTable . $this->searchTable->getIndexField();

		$property = $description->getProperty();
		$propertyCondition = '';

		// Full text is collected in a single table therefore limit the match
		// process by adding the PID as an additional condition
		if ( $property !== null ) {
			$propertyCondition = 'AND ' . $temporaryTable . 'p_id=' . $this->searchTable->getIdByProperty( $property );
		}

		$querySearchModifier = $this->getQuerySearchModifier(
			$value
		);

		return "MATCH($column) AGAINST (" . $this->searchTable->addQuotes( $value ) . " $querySearchModifier) $propertyCondition";
	}

	/**
	 * @since 2.5
	 *
	 * @param  string &$value
	 *
	 * @return string
	 */
	public function getQuerySearchModifier( &$value ) {

		//  @see http://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
		// "MySQL can perform boolean full-text searches using the IN BOOLEAN
		// MODE modifier. With this modifier, certain characters have special
		// meaning at the beginning or end of words ..."
		if ( strpos( $value, '&BOL' ) !== false ) {
			$value = str_replace( '&BOL', '', $value );
			return 'IN BOOLEAN MODE';
		}

		if ( strpos( $value, '&INL' ) !== false ) {
			$value = str_replace( '&INL', '', $value );
			return 'IN NATURAL LANGUAGE MODE';
		}

		if ( strpos( $value, '&QEX' ) !== false ) {
			$value = str_replace( '&QEX', '', $value );
			return 'WITH QUERY EXPANSION';
		}

		return 'IN BOOLEAN MODE';
	}

}