メインコンテンツまでスキップ
バージョン: 4.x

SEARCH関数

SEARCH関数は、Apache Dorisがバージョン4.0から提供する統合全文検索クエリエントリポイントです。簡潔なDSL(ドメイン固有言語)を通じてクエリ条件を記述し、転置インデックスに基づいて効率的に実行します。

SEARCHはboolean値を返す述語関数です。WHERE句のフィルタ条件として使用できます。テキストマッチングルールを記述するSEARCH DSL文字列を受け取り、マッチ可能な条件を転置インデックスにプッシュダウンして実行します。

典型的な使用例:

  • テキストフィールドに対するterm、phrase、およびboolean組み合わせ検索を実行
  • 複数カラムにわたる組み合わせ検索を実行
  • パターンマッチングにワイルドカードや正規表現を使用
  • VARIANTサブカラムに対する構造化テキスト検索を実行
  • Lucene/Elasticsearchのquery_string構文スタイルとの互換性を維持

構文

基本呼び出し形式

SEARCH('<search_expression>')
SEARCH('<search_expression>', '<default_field>')
SEARCH('<search_expression>', '<default_field>', '<default_operator>')

パラメータの説明:

パラメータ必須説明
<search_expression>必須SEARCH DSLクエリ式(文字列リテラル)
<default_field>オプションDSL内の項目が明示的にフィールドを指定しない場合に自動的に適用されるカラム名
<default_operator>オプション複数項目式のデフォルトのboolean演算子。andまたはorのみ受け入れられます(大文字小文字を区別しない)。デフォルトはorです

使用上の注意:

  • 位置: 行フィルタリングに参加する述語としてWHERE句で使用
  • 戻り値の型: BOOLEAN(マッチした場合はTRUE)

default_fieldが提供されると、Dorisは単体の項目や関数を自動的にそのフィールドに展開します。例えば:

  • SEARCH('foo bar', 'tags', 'and')SEARCH('tags:ALL(foo bar)')と等価です
  • SEARCH('foo bar', 'tags')tags:ANY(foo bar)に展開されます

DSL内で明示的に表示されるboolean演算は最も高い優先順位を持ち、デフォルト演算子をオーバーライドします。

optionsパラメータ(JSON形式)

2番目のパラメータは、高度な設定のためのJSON文字列にすることもできます:

SEARCH('<search_expression>', '<options_json>')

サポートされているオプション:

OptionTypeDescription
default_fieldstringフィールドが明示的に指定されていない項目に使用されるデフォルトの列名
default_operatorstring複数項目式のデフォルトオペレーター(andまたはor
modestringstandard(デフォルト)またはlucene
minimum_should_matchintegerSHOULD句の最小マッチ数(Luceneモードのみ)

例:

SELECT * FROM docs
WHERE SEARCH('apple banana',
'{"default_field":"title","default_operator":"and","mode":"lucene"}');

NULL と三値論理

SEARCH() は SQL の三値論理に従います:

  • マッチに参加するすべての列の値が NULL の場合、結果は UNKNOWN となります(WHERE でフィルタされます)
  • 他の部分式と組み合わせる場合、結果は boolean の短絡評価ルールに従います。例:
    • TRUE OR NULL = TRUE
    • FALSE OR NULL = NULL
    • NOT NULL = NULL

この動作はテキスト検索演算子と一致しています。

シナリオ別の SEARCH の使用

シナリオ 1: 単一用語マッチ

使用例: フィールド内の特定の用語を含むドキュメントを検索します。

  • 構文: column:term
  • セマンティクス: 列のトークン化された結果に対して用語をマッチします。マッチが大文字小文字を区別するかどうかは、インデックスプロパティ lower_case に依存します
  • インデックス推奨事項: 適切な parser/analyzer を使用して列に転置インデックスを作成します
SELECT id, title FROM search_test_basic WHERE SEARCH('title:Machine');
SELECT id, title FROM search_test_basic WHERE SEARCH('title:Python');
SELECT id, title FROM search_test_basic WHERE SEARCH('category:Technology');

シナリオ 2: 複数語句のOR一致(ANY)

使用例: 候補リストの任意の語句がヒットした場合に一致が発生する場合、例えばタグマッチングや結合キーワード検索。

  • 構文: column:ANY(term1 term2 ...)
  • セマンティクス: カラムのトークン化された結果がリスト内の任意の語句を含む(OR セマンティクス)。順序は関係なく、重複した語句は無視される
  • インデックス推奨: カラムにトークン化された転置インデックスを作成する(english/chinese/unicode など)
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ANY(python javascript)');
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ANY(machine learning tutorial)');

-- Edge case: a single-value ANY is equivalent to a term query
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ANY(python)');

シナリオ3: 複数語AND検索(ALL)

使用例: 複数の語句がすべてヒットする必要がある場合、例えば厳密なトピックフィルタリング。

  • 構文: column:ALL(term1 term2 ...)
  • セマンティクス: カラムのトークン化された結果が、リスト内のすべての語句を同時に含む(AND セマンティクス)。順序は重要ではなく、重複する語句は無視される
  • インデックス推奨: カラムにトークン化された転置インデックスを作成する(english/chinese/unicodeなど)
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ALL(machine learning)');
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ALL(programming tutorial)');

-- Edge case: a single-value ALL is equivalent to a term query
SELECT id, title FROM search_test_basic WHERE SEARCH('tags:ALL(python)');

シナリオ 4: Boolean組み合わせクエリ

使用例: 複数の条件をAND/OR/NOTで組み合わせる必要がある場合、例えば「Aを含み、かつBを含まない」など。

  • 構文: (expr) AND/OR/NOT (expr)
  • セマンティクス: ANDOR、およびNOTを使用してSEARCH内のサブ式を組み合わせる
  • インデックス推奨事項: インデックスプッシュダウンを取得するために、可能な限りマッチ可能な条件をSEARCH内に配置する。追加のフィルタリングには他のWHERE条件を使用する
SELECT id, title FROM search_test_basic
WHERE SEARCH('title:Machine AND category:Technology');

SELECT id, title FROM search_test_basic
WHERE SEARCH('title:Python OR title:Data');

SELECT id, title FROM search_test_basic
WHERE SEARCH('category:Technology AND NOT title:Machine');

シナリオ 5: 複雑なネストされた式

ユースケース: 括弧を使用してboolean演算の優先順位を制御し、複数レベルのネストされたフィルター条件を構築する。

  • 構文: 括弧を使用して式をグループ化する。例:(expr1 OR expr2) AND expr3
  • セマンティクス: 括弧でboolean演算の優先順位を制御する。複数レベルのネストがサポートされている
  • インデックス推奨事項: boolean組み合わせクエリと同様
SELECT id, title FROM search_test_basic
WHERE SEARCH('(title:Machine OR title:Python) AND category:Technology');

SELECT id, title FROM search_test_basic
WHERE SEARCH('tags:ANY(python javascript) AND (category:Technology OR category:Programming)');

シナリオ 6: Lucene/Elasticsearch 構文互換性

用途: Elasticsearch からの移行、または Lucene query_string セマンティクスに従った表現の記述を希望する場合。

Lucene モードは Elasticsearch/Lucene の query_string 動作をエミュレートします。Boolean 演算子は従来のブール代数ではなく、左から右への修飾子として機能します。

標準モードとの主な違い:

  • AND/OR/NOT は隣接する項に影響する修飾子です
  • 演算子の優先順位は左から右です
  • MUST/SHOULD/MUST_NOT が内部的に使用されます(Lucene の Occur enum と類似)
  • 純粋な NOT クエリは空の結果を返します(正の条件句が必要)

Lucene モードの有効化:

-- Basic Lucene mode
SELECT * FROM docs
WHERE SEARCH('apple AND banana',
'{"default_field":"title","mode":"lucene"}');

-- Use minimum_should_match
SELECT * FROM docs
WHERE SEARCH('apple AND banana OR cherry',
'{"default_field":"title","mode":"lucene","minimum_should_match":1}');

動作の比較:

クエリStandard モードLucene モード
a AND ba ∩ b+a +b (両方が MUST)
a OR ba ∪ ba b (両方が SHOULD、min=1)
NOT a¬a空の結果 (正の句なし)
a AND NOT ba ∩ ¬b+a -b (a が MUST、b が MUST_NOT)
a AND b OR c(a ∩ b) ∪ c+a b c (a のみが MUST)

注意: Lucene モードでは、a AND b OR c は左から右に解析されます:OR 演算子により b が MUST から SHOULD に変更されます。SHOULD 句のマッチを要求するには minimum_should_match を使用してください。

シナリオ 7: フレーズクエリ

ユースケース: 連続した順序のあるフレーズを検索します。例えば「machine learning」が順序通りに現れることを要求します。

  • 構文: column:"quoted phrase"
  • セマンティクス: カラムのアナライザーに従って連続した順序のある語句にマッチします。フレーズ全体は二重引用符で囲む必要があります
  • インデックスの推奨事項: 対象カラムは位置情報を持つトークン化された転置インデックスを使用する必要があります(parser で設定)
SELECT id, title FROM search_test_basic
WHERE SEARCH('content:"machine learning"');

シナリオ 8: 複数カラムにわたる組み合わせ検索

使用例: 単一のクエリが複数のフィールドをカバーする場合、例えばタイトル、タグ、または作成者に一致する行を返す。

  • 構文: column1:term OR column2:ANY(...) OR ...
  • セマンティクス: 単一の式内で複数のカラムにわたってマッチする。各カラムは独自のインデックス/トークナイザー設定を適用する
  • インデックス推奨: 関与する各カラムに適切な転置インデックスを構築する
SELECT id, title FROM search_test_basic
WHERE SEARCH('title:Python OR tags:ANY(database mysql) OR author:Alice');

SELECT id, title FROM search_test_basic
WHERE SEARCH('tags:ALL(tutorial) AND category:Technology');

シナリオ 9: Wildcard Query

使用例: プレフィックス、サフィックス、または部分一致。例えば、"Chris"で始まるすべての名前を検索する場合。

  • 構文: column:prefix*, column:*mid*, column:?ingle
  • セマンティクス: 任意の長さの文字列にマッチする * と、単一文字にマッチする ? を使用
  • インデックス推奨: 非tokenizedインデックスに適している。大文字小文字を区別しないマッチングを得るために lower_case を有効にしたtokenizedインデックスでも使用可能
SELECT id, title FROM search_test_basic
WHERE SEARCH('firstname:Chris*');

-- Combine with the default field parameter
SELECT id, firstname FROM people
WHERE SEARCH('Chris*', 'firstname');

シナリオ 10: 正規表現クエリ

使用例: 複雑なパターンマッチングにLuceneスタイルの正規表現を使用します。

  • 構文: column:/regex/
  • セマンティクス: スラッシュで囲まれたパターンを使用してLuceneスタイルの正規表現でマッチングします
  • インデックス推奨事項: トークン化されていない転置インデックスのみがサポートされています
SELECT id, title FROM corpus
WHERE SEARCH('title:/data.+science/');

シナリオ 11: 厳密等価マッチ (EXACT)

使用例: カラムの元の値全体を厳密にマッチします。大文字小文字を区別し、トークン化は行いません。

  • 構文: column:EXACT(text)
  • セマンティクス: カラム値全体に対して厳密マッチングを実行します。大文字小文字を区別します。部分的な用語はマッチしません
  • インデックス推奨: EXACTを高速化するため、カラムに非トークン化転置インデックスも構築してください(parserを設定せずに)
SELECT id
FROM t
WHERE SEARCH('content:EXACT(machine learning)');

シナリオ12:VARIANTサブカラムクエリ

ユースケース: 半構造化VARIANTフィールド内の特定のサブパスを検索します。

  • 構文: variant_col.sub.path:term
  • セマンティクス: ドット区切りのパスを通じてVARIANTサブカラムにアクセスしてマッチングを行います。マッチング動作はVARIANTカラムのindex/analyzerの設定に従います
  • サポートされる機能: Boolean結合、ANY/ALL、ネストされたパス。存在しないサブカラムはマッチを返しません
SELECT id
FROM test_variant_search_subcolumn
WHERE SEARCH('properties.message:alpha');

シナリオ13: VARIANT入れ子グループ同一要素クエリ

ユースケース: VARIANT列でvariant_enable_nested_group = trueを有効にした後、array<object>値または最上位レベルのオブジェクト配列の同一要素内で複数のフィールドを検索する。

  • 構文: NESTED(variant_col.array_path, inner_expression); 最上位レベル配列の場合はNESTED(variant_col, inner_expression)を使用
  • セマンティクス: inner_expression内の複数フィールドは同一の入れ子配列要素内でマッチする必要があり、異なる要素間での偶発的なマッチを防ぐ
  • 使用上の注意: 入れ子式内のフィールドは入れ子パスに対して相対的である必要があります。例えば、NESTED(properties.items, properties.items.msg:hello)ではなくNESTED(properties.items, msg:hello)を使用してください
CREATE TABLE test_variant_nested_group (
id BIGINT,
properties VARIANT<PROPERTIES("variant_enable_nested_group" = "true")>,
INDEX idx_properties (properties) USING INVERTED PROPERTIES("parser" = "english")
);

INSERT INTO test_variant_nested_group VALUES
(1, '{"items":[{"msg":"hello","title":"news"},{"msg":"foo","title":"bar"}]}'),
(2, '{"items":[{"msg":"hello"},{"title":"news"}]}'),
(3, '[{"msg":"hello","title":"news"},{"msg":"hello","title":"sports"}]'),
(4, '[{"msg":"hello"},{"title":"news"}]');

-- Same-element match under object field items. This matches id=1.
SELECT id
FROM test_variant_nested_group
WHERE SEARCH('NESTED(properties.items, msg:hello AND title:news)')
ORDER BY id;

-- Same-element match on a top-level array. The path is the VARIANT column name. This matches id=3.
SELECT id
FROM test_variant_nested_group
WHERE SEARCH('NESTED(properties, msg:hello AND title:news)')
ORDER BY id;

-- Single-field match on a top-level array. This matches id=3 and id=4.
SELECT id
FROM test_variant_nested_group
WHERE SEARCH('NESTED(properties, msg:hello)')
ORDER BY id;

完全な例

以下の包括的な例では、同じカラムでトークン化されたインデックスとトークン化されていないインデックスの両方を構築する方法、およびEXACT、ANY/ALL、フレーズ、ワイルドカードクエリを組み合わせる方法を示しています。

テーブルとベースインデックスの作成

-- Build both tokenized and non-tokenized inverted indexes
CREATE TABLE t (
id INT,
content STRING,
INDEX idx_untokenized(content) USING INVERTED,
INDEX idx_tokenized(content) USING INVERTED PROPERTIES("parser" = "standard")
);

EXACTクエリとトークン化クエリの比較

-- Strict equality match (uses the non-tokenized index)
SELECT id, content
FROM t
WHERE SEARCH('content:EXACT(machine learning)')
ORDER BY id;

-- EXACT does not match partial terms
SELECT id, content
FROM t
WHERE SEARCH('content:EXACT(machine)')
ORDER BY id;

-- ANY/ALL use the tokenized index
SELECT id, content FROM t WHERE SEARCH('content:ANY(machine learning)') ORDER BY id;
SELECT id, content FROM t WHERE SEARCH('content:ALL(machine learning)') ORDER BY id;

-- Compare EXACT and ANY
SELECT id, content FROM t WHERE SEARCH('content:EXACT(deep learning)') ORDER BY id;
SELECT id, content FROM t WHERE SEARCH('content:ANY(deep learning)') ORDER BY id;

組み合わせ条件と簡略形

-- Combined conditions
SELECT id, content
FROM t
WHERE SEARCH('content:EXACT(machine learning) OR content:ANY(intelligence)')
ORDER BY id;

-- Simplified form using a default field and default operator
SELECT id, tags
FROM tag_dataset
WHERE SEARCH('deep learning', 'tags', 'and'); -- Automatically expands to tags:ALL(deep learning)

-- Use a phrase and a wildcard together
SELECT id, content FROM t
WHERE SEARCH('content:"deep learning" OR content:AI*')
ORDER BY id;

VARIANT列検索の例

-- A VARIANT column with an inverted index
CREATE TABLE test_variant_search_subcolumn (
id BIGINT,
properties VARIANT<PROPERTIES("variant_max_subcolumns_count"="0")>,
INDEX idx_properties (properties) USING INVERTED PROPERTIES (
"parser" = "unicode",
"lower_case" = "true",
"support_phrase" = "true"
)
);

-- Single-term query
SELECT id
FROM test_variant_search_subcolumn
WHERE SEARCH('properties.message:alpha')
ORDER BY id;

-- AND / ALL queries
SELECT id
FROM test_variant_search_subcolumn
WHERE SEARCH('properties.message:alpha AND properties.message:beta')
ORDER BY id;

SELECT id
FROM test_variant_search_subcolumn
WHERE SEARCH('properties.message:ALL(alpha beta)')
ORDER BY id;

-- OR query across different subcolumns
SELECT id
FROM test_variant_search_subcolumn
WHERE SEARCH('properties.message:hello OR properties.category:beta')
ORDER BY id;

エスケープ文字

DSLで特殊文字をエスケープするにはバックスラッシュ(\)を使用します:

エスケープ説明
\ リテラルスペース(用語を結合)title:First\ Valueは"First Value"にマッチ
\( \)リテラル括弧title:hello\(world\)は"hello(world)"にマッチ
\:リテラルコロンtitle:key\:valueは"key:value"にマッチ
\\リテラルバックスラッシュtitle:path\\to\\fileは"path\to\file"にマッチ

例:

-- Search for a value containing a space as a single term
SELECT * FROM docs WHERE SEARCH('title:First\\ Value');

-- Search for a value containing parentheses
SELECT * FROM docs WHERE SEARCH('title:hello\\(world\\)');

-- Search for a value containing a colon
SELECT * FROM docs WHERE SEARCH('title:key\\:value');

注意: SQL文字列では、バックスラッシュは二重エスケープする必要があります。SQLで \\ を使用すると、DSLでは単一の \ になります。

現在の制限事項

  • 範囲およびリスト句(field:[a TO b]field:IN(...) など)は、通常の用語マッチングにフォールバックします。代わりに通常のSQL範囲や IN フィルターを使用してください。

代替として標準演算子またはテキスト検索演算子を使用できます:

-- Filter by range using SQL
SELECT * FROM t WHERE created_at >= '2024-01-01';