14 ファセット・ナビゲーションの使用

ファセット・ナビゲーション機能について理解します。

この章のトピックは、次のとおりです:

14.1 ファセット・ナビゲーションについて

この機能は、eコマースまたはカタログ・アプリケーションでよく使用される、ファセットとも呼ばれるグループ集計を実装します。多くのアプリケーションでは、問合せによって返されたヒットのリストを表示するだけでなく、その結果がカテゴリ化されていることが望まれます。

たとえば、eコマース・アプリケーションで、マネジメントという語句に対する問合せに一致するすべての製品をそのファセット情報とともに表示するとします。ファセットには、「製品のタイプ」(本やDVD)、「著者」および「日付」があります。各ファセットに対して、アプリケーションでは一意の値(本やDVD)とその件数が表示されます。「本」カテゴリに分類される、関心がある製品のほとんどをすばやくアセスメントできます。「製品のタイプ」で「本」を選択して、検索をさらに絞り込むことができます。

グループ集計は特定の値を持つドキュメントの数として定義されます。値が同じドキュメント内で繰り返されている場合、そのドキュメントによりその値の合計グループ集計に加えられる件数は1です。グループ件数つまりファセットは、optimized_for search SDATAを使用するSDATAセクションでサポートされています。問合せのファセットの計算をリクエストするには、結果セット・インタフェースを使用します。

14.2 ファセットとしてのセクションの定義

SDATAは構造化データを参照します。グループ件数つまりファセットは、optimized_for属性を'search'または'sort and search'に設定して作成するSDATAセクションでサポートされています。MULTI_COLUMN_DATASTOREプリファレンスで、データがoptimized_for search SDATAとして指定されたタグや列の間にあると、そのデータは自動的にファセット・データとして索引付けされます。宣言されたタイプと一致しないデータは、特定の行の索引付けエラーをそのときに処理しているのと同じフレームワークに基づいて処理されます。

次の文では、いくつかのタグ付けされたデータが表のVARCHAR2列に挿入されます。後から、ここで使用されたタグに基づいてデータを収集するためのSDATAセクションを定義できます。

  • バイナリfloatやバイナリdoubleにはタグprice:

    insert into mytab values (1, 'red marble' <price>1.23</price>');
  • タイム・スタンプにはタグT:

    insert into mytab values (1,'blue marbles <T>2012-12-05T05:20:00</T>');

次の文では、セクション・グループが作成され、様々なSDATAセクション・グループが追加されます。セクションの定義には、そのセクションが属するセクション・グループ、セクションの名前、検索するタグ、およびデータ型が含まれます。

exec ctx_ddl.create_section_group('sg','BASIC_SECTION_GROUP')
exec ctx_ddl.add_SDATA_section('sg','sec01','name', 'varchar2')
exec ctx_ddl.add_SDATA_section('sg','sec02','count', 'number')
exec ctx_ddl.add_SDATA_section('sg','sec03','date', 'date')
exec ctx_ddl.add_SDATA_section('sg','sec04','timestamp', 'timestamp')
exec ctx_ddl.add_SDATA_section('sg','sec05','new price', 'binary_double')
exec ctx_ddl.add_SDATA_section('sg','sec06','old price','binary_float')
exec ctx_ddl.add_SDATA_section('sg','sec07','timestamp','timestamp with time zone')

ファセットに付けた名前が'sec01'で、'name'タグは、索引付けされたドキュメント内に出現する実際のタグ名です。'date''timestamp'および'timestamp with time zone'データ型では、入力データは標準ISO形式である必要があります。

関連項目:

標準ISOフォーマットの詳細は、『Oracle Databaseグローバリゼーション・サポート・ガイド』を参照してください。

例14-1 ファセット・ナビゲーションの使用

次の文では、productsという名前の表が作成されます。

drop table products;

create table products(name varchar2(60), vendor varchar2(60), rating number, price number, mydate date);

次の文は、値をproductsに挿入します。

insert all
 into products values ('cherry red shoes', 'first vendor', 5, 129, sysdate)
 into products values ('bright red shoes', 'first vendor', 4, 109, sysdate)
 into products values ('more red shoes', 'second vendor', 5, 129, sysdate)
 into products values ('shoes', 'third vendor', 5, 109, sysdate)
select * from dual;

次の文は、dsという名前のMULTI_COLUMN_DATASTOREプリファレンスを作成して、ファセットとして使用するよう他の各種列を索引(name)に取り込んでいます。

/*A MULTI_COLUMN_DATASTORE automatically adds tags by default so that the text to be indexed looks like 
'<name>cherry red shoes</name><vendor>first vendor</vendor><rating> .... '*/

exec ctx_ddl.drop_preference  ('ds')
exec ctx_ddl.create_preference('ds', 'MULTI_COLUMN_DATASTORE')
exec ctx_ddl.set_attribute    ('ds', 'COLUMNS', 'name, vendor, rating, price, mydate')

ノート:

Oracleでは、binary_float、binary_double、timestampおよびtimestamp with timezoneのデータ型を持つ表の列は許可されていません。そのため、このようなデータ型をMULTI_COLUMN_DATASTOREで使用することは困難です。それでも、これらのデータ型のタグ付きデータがドキュメントに含まれている場合はファセットを作成できます。あるいは、'timestamp'列は'date'に変換し、binary_floatbinary_doubleは'number'として格納できます。

次の文では、sgという名前のセクション・グループを作成し、各列のoptimized_for search属性がファセットとして処理されるようにします。

/* A Section Group is created to specify the data type of each column (varchar2 is the default) and 
how each column that is brought into the index should be used.*/


exec ctx_ddl.drop_section_group   ('sg')
exec ctx_ddl.create_section_group ('sg', 'BASIC_SECTION_GROUP')

exec ctx_ddl.add_sdata_section    ('sg', 'vendor', 'vendor', 'VARCHAR2')
exec ctx_ddl.add_sdata_section    ('sg', 'rating', 'rating', 'NUMBER')
exec ctx_ddl.add_sdata_section    ('sg', 'price', 'price', 'NUMBER')
exec ctx_ddl.add_sdata_section    ('sg', 'mydate', 'mydate', 'DATE')

exec ctx_ddl.set_section_attribute('sg', 'vendor', 'optimized_for', 'SEARCH')
exec ctx_ddl.set_section_attribute('sg', 'rating', 'optimized_for', 'SEARCH')
exec ctx_ddl.set_section_attribute('sg', 'price',  'optimized_for', 'SEARCH')
exec ctx_ddl.set_section_attribute('sg', 'mydate', 'optimized_for', 'SEARCH')

次の文では、nameに索引が作成され、PARAMETERS句を使用してプリファレンスを指定します。

CREATE INDEX product_index ON products (name)
INDEXTYPE IS ctxsys.context
PARAMETERS ('datastore ds section group sg');

次の文では製品名'red shoes'を問い合せ、計算のファセットを指定できます。count属性は製品の問合せに一致する品目の合計数を示します。結果セット・インタフェースでは、一致するアイテムが最も多い上位ベンダー、最安値、および最新の着荷など、様々な要件を指定します。

set long 500000
set pagesize 0

variable displayrs clob;

declare
  rs clob;
begin
   ctx_query.result_set('product_index', 'red shoes', '<ctx_result_set_descriptor>
	 	<count/>
	 	<group sdata="vendor" topn="5" sortby="count" order="desc">
	 	<count exact="true"/>
	 	</group>
	 	<group sdata="price" topn="3" sortby="value" order="asc">
	 	<count exact="true"/>
	 	</group>
	 	<group sdata="mydate" topn="3" sortby="value" order="desc">
	 	<count exact="true"/>
	 	</group>
	 	</ctx_result_set_descriptor>',
	 	rs);

/* Pretty-print the result set (rs) for display purposes. 
It is not required if you are going to manipulate it in XML.*/

   select xmlserialize(Document XMLType(rs) as clob indent size=2) into :displayrs from dual;
   dbms_lob.freetemporary(rs);
end;
/
select :displayrs from dual;

出力は次のようになります。

<ctx_result_set>                                                                
  <count>3</count>                                                              

  <groups sdata="VENDOR">                                                       
    <group value="first vendor">                                                  
      <count>2</count>                                                          
    </group>                                                                    
    <group value="second vendor">                                                    
      <count>1</count>                                                          
    </group>                                                                    
  </groups>                                                                     

  <groups sdata="PRICE">                                                        
    <group value="109">                                                         
      <count>1</count>                                                          
    </group>                                                                    
    <group value="129">                                                         
      <count>2</count>                                                          
    </group>                                                                    
  </groups>                                                                     

  <groups sdata="MYDATE">                                                       
    <group value="2017-12-06 05:44:54">                                         
      <count>3</count>                                                          
    </group>                                                                    
  </groups>                                                                     
</ctx_result_set>

14.3 結果セット・インタフェースを使用したファセットの問合せ

Oracle Databaseリリース18から、指定されたファセットのリストのグループ・カウント操作が用意されています。bucketby属性の値をsingleに設定して使用することで、単一の値ごとにグループ・カウントを取得できます。topnsortbyおよびorder属性もサポートされています。

bucketby属性
  • このリリースでは、singleが有効な唯一の属性です。

  • 'single'モードでは、ファセットのすべての一意の値のリスト、および各値のドキュメント・カウントが生成されます。

topn属性
  • 有効な属性値は、0より大きい正の数です。

  • この属性は、上位n件のファセット値とそのカウントのみを返すよう指定します。

  • グループ・カウントにより、sortby属性がvalueに設定されている場合の除き、返される上位n件の値が決定されます。その場合、値はデータ型に従ってソートされ、ソートの上位n件の結果が返されます。ソートにおいては順序属性が考慮されます。

  • デフォルトでは、結果はグループ・カウントを基準として降順でソートされます。

  • カウントに同順位がある場合、この同順位内のファセット値の順序は保証されません。

単一件数

次の文は、表mytabにいくつかの行を挿入します。一部の行にはファセット<B>の値が2つあり、一部の行には単一の値があります。

begin
 	insert into mytab values (1, '<B>1.234</B><B>5</B>');
 	insert into mytab values (2, '<B>1.432</B>');
 	insert into mytab values (3, '<B>2.432</B><B>6</B>');
 	insert into mytab values (4, '<B>2.432</B>');
end;

単一件数では、一意のそれぞれの値と、その値を持つドキュメントの数を表示します。

<ctx_result_set>
	<groups sdata="SEC01">
		<group value="2.432"><count>2</count></group>
		<group value="1.234"><count>1</count></group>
		<group value="5"><count>1</count></group>
		<group value="6"><count>1</count></group>
		<group value="1.432"><count>1</count></group>
	</groups>
</ctx_result_set>

ドキュメント1が削除されると、次のような結果になります。

<ctx_result_set>
	<groups sdata="SEC01">
		<group value="2.432"><count>2</count></group>
		<group value="6"><count>1</count></group>
		<group value="1.432"><count>1</count></group>
	</groups>
</ctx_result_set>
sortbyおよびorder属性
  • sortbyは属性値"count"および"value"をサポートしています。

    • countはグループ件数(数値)でソートします。これはデフォルトです。

    • valueは、データ型に応じて、値でソートします。

  • orderでは、ASC(昇順)およびDESC(降順) (こちらがデフォルト)がサポートされています。

  • 選択されていない場合、デフォルトはcount DESCです。

この例は、bucketbysingleに設定されている場合の数値ファセットのグループ化を示しています。ここで、mytab_idxは索引の名前、textが問合せであり、グループSDATAがファセットを要求しています。

begin
	 ctx_query.result_set('mytab_idx', 'text',
                        '<ctx_result_set_descriptor>
	 <group sdata="sec01" topn = "4" sortby = "value" order="asc" bucketby="single">
	 <count/>    
	 </group>
	 </ctx_result_set_descriptor>'
	 :rs);
end;

次に、sortby属性がcountではなくvalueに設定されているために、値がアルファベット順にリストされているサンプル出力を示します。order属性がascに設定されているため、値は昇順(ABCからXYZ)で表示されます。topn属性が4に設定されているため、4つの値のみが表示されます。

<ctx_result_set>
	 <group SDATA="SEC01"> 
	 	 <group value="ABC"><count>2</count>
	 </group>
	 	 <group value="DEF"><count>1</count>
	 </group>
	 	 <group value="GHI"><count>10</count>
	 </group>
	 	 <group value="XYZ"><count>1</count>
	 </group>
</ctx_result_set>

14.4 フィルタとしてファセットを使用した問合せの絞込み

ファセットの実装で、SDATAに対して使用可能なデータベース比較演算子の標準セットが用意されているCONTAINS問合せがサポートされるようになりました。次の例は、'name' varchar2セクションに基づいています。数字とともに使用する場合、検索する数字を引用符で囲まないでください。
contains (text, 'SDATA(sec01 = "run")', 1) > 0
contains (text, 'SDATA(sec01 > "run")', 1) > 0
contains (text, 'SDATA(sec01 >= "run")', 1) > 0
contains (text, 'SDATA(sec01 < "run")', 1) > 0
contains (text, 'SDATA(sec01 <= "run")', 1) > 0
contains (text, 'SDATA(sec01 <> "run")', 1) > 0
contains (text, 'SDATA(sec01 != "run")', 1) > 0
contains (text, 'SDATA(sec01 between "run1" and "run2")', 1) > 0
contains (text, 'SDATA(sec01 not between "run1" and "run2")', 1) > 0
contains (text, 'SDATA(sec01 is null)', 1) > 0
contains (text, 'SDATA(sec01 is not null)', 1) > 0
contains (text, 'SDATA(sec01 like "%run")', 1) > 0
contains (text, 'SDATA(sec01 like "run%")', 1) > 0
contains (text, 'SDATA(sec01 like "%run%")', 1) > 0
contains (text, 'SDATA(sec01 not like "%run")', 1) > 0
contains (text, 'SDATA(sec01 not like "run%")', 1) > 0
contains (text, 'SDATA(sec01 not like "%run%")', 1) > 0

contains (text, 'SDATA(sec02 = 9)', 1) > 0
contains (text, 'SDATA(sec02 < 10)', 1) > 0
contains (text, 'SDATA(sec02 between 2 and  20)', 1) > 0

比較演算子は、各種のデータ型に対する現在のoptimized_for search SDATAの動作に基づいて動作します。

14.5 複数値ファセット

複数の値が同じ文書のoptimized for search SDATAセクションにある場合、値がSDATAセクションに対応するそれぞれのタグで囲まれていれば、各値が索引付けされます。値が個別にセクション・タグで囲まれておらず、同じセクション・タグ内に一緒にある場合、単一の値として処理されます。

たとえば、ドキュメント内にある<car>First Car, Second Car</car>は、'First Car, Second Car'という値の単一文字列として処理されます。しかし、<car>First Car</car><car>Second Car</car>はドキュメントにおいて別個の2つの値として処理されます。