アセット共有の Query Builderの機能は、Java API と REST API を通して公開されます。ここでは、これらの API について説明します。
サーバー側 Query Builder(QueryBuilder
)はクエリの記述を受け入れ、XPath クエリを作成して実行します。オプションで結果セットのフィルタリング、必要に応じてファセットの抽出もおこないます。
クエリの記述は、単に述語(Predicate
)のセットです。例としては、XPathのjcr:contains()
関数に対応するフルテキスト述語が含まれます。
各述語タイプに、1 つのエバリュエーターコンポーネント(PredicateEvaluator
)があります。これらのコンポーネントは、XPath、フィルタリングおよびファセットの抽出に対してその特定の述語を処理する方法を理解しています。OSGi コンポーネントのランタイムによってプラグインされる、カスタムのエバリュエーターを作成するのは簡単です。
REST API を使用すると、JSON で送信される応答を使用した HTTP によって、まったく同じ機能にアクセスできます。
QueryBuilder API は JCR API を使用して構築されています。また、OSGi バンドル内から JCR API を使用して、Adobe Experience Manager JCR をクエリすることもできます。詳しくは、JCR API を使用した Adobe Experience Manager データのクエリを参照してください。
AEM Gems は、アドビの専門家が提供する、Adobe Experience Manager を技術的に深く掘り下げた一連のセッションです。Query Builder 専用のこのセッションは、ツールを大まかに把握したり使用したりするのに非常に便利です。
クエリビルダーの詳細な概要については、AEM GemセッションAEM querybuilderで簡単に実行できる検索フォームを参照してください。
以下のサンプルは、Java プロパティのスタイル表記法で示されています。これらのサンプルを Java API で使用するには、この後の API サンプルのように Java HashMap
を使用します。
QueryBuilder
JSON サーブレットについては、各例にローカルの CQ インストールへのリンク(デフォルトは http://localhost:4502
)が含まれています。これらのリンクを使用する前に、CQ インスタンスにログインする必要があります。
デフォルトでは、この Query Builder JSON サーブレットは最大 10 件のヒットを表示します。
次のパラメーターを追加すると、サーブレットですべてのクエリ結果を表示できます。
p.limit=-1
返された JSON データをブラウザーで表示するのに、Firefox 用 JSONView などのプラグインを使用できます。
以下のクエリは 10 件の結果を返します(正確には最大 10 件)が、通知されるのは実際に表示可能なヒット数です。
http://localhost:4502/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=foundation/components/text&1_property.operation=like&orderby=path
path=/content
1_property=sling:resourceType
1_property.value=foundation/components/text
1_property.operation=like
orderby=path
同じクエリで、パラメーター p.limit=-1
を使用すると、すべての結果が返されます(インスタンスによっては非常に多くなることがあります)。
http://localhost:4502/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=foundation/components/text&1_property.operation=like&p.limit=-1&orderby=path
path=/content
1_property=sling:resourceType
1_property.value=foundation/components/text
1_property.operation=like
p.limit=-1
orderby=path
p.guessTotal
パラメーターの目的は、実用最小限の p.offset 値と p.limit 値を組み合わせることによって、表示できる適切な結果数を返すことです。このパラメーターを使用するメリットは、結果セットが大きい場合にパフォーマンスが向上することです。これにより、総合計の計算(例:result.getSize() の呼び出し)と結果セット全体の読み込みが回避され、OAK エンジンおよびインデックスに至るまですべてが最適化されます。結果が 10 万件ある場合、実行時間とメモリ使用量の両方で大きな変化が見られる可能性があります。
このパラメーターのデメリットは、ユーザーには正確な合計が表示されないことです。ただし、p.guessTotal=1000 のように最小値を設定できるので、1000 件までは必ず読み込まれます。その結果、それより小さな結果セットについては正確な合計を取得できますが、それを超えた場合は「それ以上」ということしかわかりません。
以下のクエリに p.guessTotal=true
を追加して、どのように機能するかを見てみましょう。
http://localhost:4502/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=foundation/components/text&1_property.operation=like&p.guessTotal=true&orderby=path
path=/content
1_property=sling:resourceType
1_property.value=foundation/components/text
1_property.operation=like
p.guessTotal=true
orderby=path
このクエリは、p.limit
のデフォルトである 10
件の結果をオフセット 0
で返します。
"success": true,
"results": 10,
"total": 10,
"more": true,
"offset": 0,
AEM 6.0 SP2 の時点では、数値を使用してカスタムの最大結果数までカウントアップすることもできます。上述と同じクエリを使用して、p.guessTotal
の値を 50
に変更してみます。
http://localhost:4502/bin/querybuilder.json?path=/content&1_property=sling:resourceType&1_property.value=foundation/components/text&1_property.operation=like&p.guessTotal=50&orderby=path
0オフセットを持つ結果10件と同じデフォルトの制限値を返しますが、表示される結果は最大50件までとなります。
"success": true,
"results": 10,
"total": 50,
"more": true,
"offset": 0,
デフォルトでは、Query Builder はヒット数も通知します。正確な数を決定するために、アクセス制御を結果ごとに確認するので、結果のサイズによっては長い時間がかかることがあります。合計は、主としてエンドユーザー向け UI のページネーションの実装に使用されます。正確な数の決定には時間がかかることがあるので、guessTotal 機能を使用してページネーションを実装することをお勧めします。
例えば、この UI は以下の手法に適応できます。
合計ヒット数(SearchResult.getTotalMatches()またはquerybuilder.json応答の合計)が100以下の正確な数を取得して表示します。
guessTotal
を 100 に設定して、Query Builder への呼び出しを作成します。
応答は、以下のような結果になる可能性があります。
total=43
, more=false
— ヒットの総数が43であることを示します。UI には先頭ページの一部として 10 件の結果が表示され、続く 3 ページのページネーションが提供されます。この実装を使用して、「43 件の結果が見つかりました」のような説明テキストを表示することもできます。total=100
, more=true
— ヒットの総数が100を超え、正確な数が不明であることを示します。UI には先頭ページの一部として 10 件の結果が表示され、続く 10 ページのページネーションが提供されます。この実装を使用して、「100 件を超える結果が見つかりました」のようなテキストを表示することもできます。ユーザーが次のページに移動すると、Query Builder への呼び出しによって guessTotal
の制限と、offset
パラメーターおよび limit
パラメーターの制限が増やされます。UI が無限スクロールを使用する必要がある場合は、Query Builder によって正確なヒット数が決定されないように、guessTotal
も使用する必要があります。
http://localhost:4502/bin/querybuilder.json?type=nt:file&nodename=*.jar&orderby=@jcr:content/jcr:lastModified&orderby.sort=desc
type=nt:file
nodename=*.jar
orderby=@jcr:content/jcr:lastModified
orderby.sort=desc
http://localhost:4502/bin/querybuilder.json?type=cq:Page&orderby=@jcr:content/cq:lastModified
type=cq:Page
orderby=@jcr:content/cq:lastModified
http://localhost:4502/bin/querybuilder.json?type=cq:Page&orderby=@jcr:content/cq:lastModified&orderby.sort=desc]
type=cq:Page
orderby=@jcr:content/cq:lastModified
orderby.sort=desc
http://localhost:4502/bin/querybuilder.json?fulltext=Management&orderby=@jcr:score&orderby.sort=desc
fulltext=Management
orderby=@jcr:score
orderby.sort=desc
http://localhost:4502/bin/querybuilder.json?type=cq:Page&tagid=marketing:interest/product&tagid.property=jcr:content/cq:tags
`
type=cq:Page
tagid=marketing:interest/product
tagid.property=jcr:content/cq:tags
明示的なタグIDがわかっている場合は、例のようにtagid
述語を使用します。
タグタイトルのパス(スペースなし)には、tag
述語を使用します。
前の例ではページ(cq:Page
ノード)を検索しているので、jcr:content/cq:tags
というtagid.property
述語には、そのノードからの相対パスを使用する必要があります。 デフォルトでは、tagid.property
はcq:tags
になります。
http://localhost:4502/bin/querybuilder.json?fulltext=Management&group.1_path=/content/geometrixx/en/company/management&group.2_path=/content/geometrixx/en/company/bod&group.p.or=true
fulltext=Management
group.p.or=true
group.1_path=/content/geometrixx/en/company/management
group.2_path=/content/geometrixx/en/company/bod
このクエリでは、クエリ内のサブ式を区切る役目を果たす「グループ**」(group
)を使用しているので、標準の表記法よりも多くの括弧が含まれています。例えば、前の例は、次のように、よりわかりやすいスタイルで表現することができます。
"Management" and ("/content/geometrixx/en/company/management?lang=ja" or "/content/geometrixx/en/company/bod?lang=ja")
例にあるグループの内部では、path
述語が複数回使用されています。述語の2つのインスタンスを区別して並べ替えるには(述語の順序が必要)、述語の先頭にN _ where
Nを並べ替えインデックスにする必要があります。 前の例では、結果の述部は1_path
と2_path
です。
p.or
のp
は、1_path
のようなグループのサブ述語に対して、次の内容(この場合or
)がグループのパラメータであることを示す特殊な区切り文字です。
p.or
を指定しない場合、すべての述部がAND結合され、つまり、各結果がすべての述部を満たす必要があります。
異なる述語に対してであっても、単一のクエリ内で同じ数値のプレフィックスを使用することはできません。
cq:template
プロパティを使用して、特定のテンプレートのすべてのページを検索できます。
http://localhost:4502/bin/querybuilder.json?property=cq%3atemplate&property.value=%2fapps%2fgeometrixx%2ftemplates%2fhomepage&type=cq%3aPageContent
type=cq:PageContent
property=cq:template
property.value=/apps/geometrixx/templates/homepage
これには、ページ自身ではなく、ページの jcr:content
ノードが返されるという欠点があります。この問題を解決するには、相対パスで検索します。
http://localhost:4502/bin/querybuilder.json?property=jcr%3acontent%2fcq%3atemplate&property.value=%2fapps%2fgeometrixx%2ftemplates%2fhomepage&type=cq%3aPage
type=cq:Page
property=jcr:content/cq:template
property.value=/apps/geometrixx/templates/homepage
property 述語を複数回使用する場合、ここでも、数字のプレフィックスを付加する必要があります。
http://localhost:4502/bin/querybuilder.json?1_property=jcr%3acontent%2fcq%3atemplate&1_property.value=%2fapps%2fgeometrixx%2ftemplates%2fhomepage&2_property=jcr%3acontent%2fjcr%3atitle&2_property.value=English&type=cq%3aPage
type=cq:Page
1_property=jcr:content/cq:template
1_property.value=/apps/geometrixx/templates/homepage
2_property=jcr:content/jcr:title
2_property.value=English
プロパティの複数の値("A" or "B" or "C"
)を検索する場合に大きなグループを避けるには、property
述語に複数の値を指定します。
http://localhost:4502/bin/querybuilder.json?property=jcr%3atitle&property.1_value=Products&property.2_value=Square&property.3_value=Events
property=jcr:title
property.1_value=Products
property.2_value=Square
property.3_value=Events
複数の値を持つプロパティの場合は、複数の値が一致する("A" and "B" and "C"
)ことを必須にすることもできます。
http://localhost:4502/bin/querybuilder.json?property=jcr%3atitle&property.and=true&property.1_value=test&property.2_value=foo&property.3_value=bar
property=jcr:title
property.and=true
property.1_value=test
property.2_value=foo
property.3_value=bar
デフォルトでは、次の JSON サーブレットは検索結果内の各ノードに関するデフォルトのプロパティのセット(path、name、title など)を返します。QueryBuilder返されるプロパティを制御するために、次のいずれかの操作を実行できます。
指定
p.hits=full
この場合、各ノードにすべてのプロパティが含まれます。
http://localhost:4502/bin/querybuilder.json?p.hits=full&property=jcr%3atitle&property.value=Triangle
property=jcr:title
property.value=Triangle
p.hits=full
使用方法
p.hits=selective
をクリックし、取り込むプロパティを指定します
p.properties
スペースで区切る:
http://localhost:4502/bin/querybuilder.json?p.hits=selective&property=jcr%3atitle&property.value=Triangle
http://localhost:4502/bin/querybuilder.json?
p.hits=selective&p.properties=sling%3aresourceType%20jcr%3aprimaryType&property=jcr%3atitle&property.value=Triangle
property=jcr:title
property.value=Triangle
p.hits=selective
p.properties=sling:resourceType jcr:primaryType
他に実行可能な方法として、QueryBuilder 応答に子ノードを含めることができます。これを行うには、
p.nodedepth=n
n
は、クエリが返すレベルの数です。 子ノードを返すには、そのノードをpropertiesセレクターで指定する必要があります
p.hits=full
例:
http://localhost:4502/bin/querybuilder.json?p.hits=full&p.nodedepth=5&property=jcr%3atitle&property.value=Triangle
property=jcr:title
property.value=Triangle
p.hits=full
p.nodedepth=5
述語の詳細については、Query Builder の述語リファレンスのページを参照してください。
JavadocのPredicateEvaluator
クラスを調べることもできます。 これらのクラスの Javadoc ドキュメントには、使用できるプロパティのリストが含まれています。
クラス名のプレフィックス(例えば、SimilarityPredicateEvaluator
の「similar
」)は、クラスのprincipalプロパティです。 このプロパティは、クエリ内で使用する述語の名前(小文字で使用)でもあります。
このようなプリンシパルプロパティでは、クエリを短縮し、完全修飾バリアント" similar.similar=/content/en
"の代わりに" similar=/content/en
"を使用できます。 完全修飾形式は、クラスのプリンシパルプロパティではないすべてのプロパティに対して使用する必要があります。
String fulltextSearchTerm = "Geometrixx";
// create query description as hash map (simplest way, same as form post)
Map<String, String> map = new HashMap<String, String>();
// create query description as hash map (simplest way, same as form post)
map.put("path", "/content");
map.put("type", "cq:Page");
map.put("group.p.or", "true"); // combine this group with OR
map.put("group.1_fulltext", fulltextSearchTerm);
map.put("group.1_fulltext.relPath", "jcr:content");
map.put("group.2_fulltext", fulltextSearchTerm);
map.put("group.2_fulltext.relPath", "jcr:content/@cq:tags");
// can be done in map or with Query methods
map.put("p.offset", "0"); // same as query.setStart(0) below
map.put("p.limit", "20"); // same as query.setHitsPerPage(20) below
Query query = builder.createQuery(PredicateGroup.create(map), session);
query.setStart(0);
query.setHitsPerPage(20);
SearchResult result = query.getResult();
// paging metadata
int hitsPerPage = result.getHits().size(); // 20 (set above) or lower
long totalMatches = result.getTotalMatches();
long offset = result.getStartIndex();
long numberOfPages = totalMatches / 20;
//Place the results in XML to return to client
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
//Start building the XML to pass back to the AEM client
Element root = doc.createElement( "results" );
doc.appendChild( root );
// iterating over the results
for (Hit hit : result.getHits()) {
String path = hit.getPath();
//Create a result element
Element resultel = doc.createElement( "result" );
root.appendChild( resultel );
Element pathel = doc.createElement( "path" );
pathel.appendChild( doc.createTextNode(path ) );
resultel.appendChild( pathel );
}
QueryBuilder API を使用する OSGi バンドルを作成し、その OSGi バンドルを Adobe Experience Manager アプリケーション内で使用する方法については、Query Builder API を使用する Adobe CQ OSGi バンドルの作成を参照してください。
同じクエリが、Query Builder(JSON)サーブレットを使用して HTTP を介して実行されます。
http://localhost:4502/bin/querybuilder.json?path=/content&type=cq:Page&group.p.or=true&group.1_fulltext=Geometrixx&group.1_fulltext.relPath=jcr:content&group.2_fulltext=Geometrixx&group.2_fulltext.relPath=jcr:content/@cq:tags&p.offset=0&p.limit=20
クエリはリポジトリに保存して後で使用することができます。QueryBuilder
は、次のシグネチャを持つ「storeQuery
」メソッドを提供します。
void storeQuery(Query query, String path, boolean createFile, Session session) throws RepositoryException, IOException;
QueryBuilder#storeQuery
メソッドを使用すると、指定した Query
が、createFile
引数の値に応じてファイルまたはプロパティとしてリポジトリに保存されます。次の例は、Query
をパス/mypath/getfiles
にファイルとして保存する方法を示しています。
builder.storeQuery(query, "/mypath/getfiles", true, session);
以前に保存したクエリはすべて、QueryBuilder#loadQuery
メソッドを使用してリポジトリから読み込むことができます。
Query loadQuery(String path, Session session) throws RepositoryException, IOException
例えば、パス/mypath/getfiles
に保存されたQuery
は、次のスニペットで読み込むことができます。
Query loadedQuery = builder.loadQuery("/mypath/getfiles", session);
Query Builder のクエリを試してみたり、デバッグしたりする場合は、次の URL にアクセスして、QueryBuilder のデバッガーコンソールを使用できます。
http://localhost:4502/libs/cq/search/content/querydebug.html
または、次の URL にアクセスして、QueryBuilder JSON サーブレットを使用することもできます。
http://localhost:4502/bin/querybuilder.json?path=/tmp
(path=/tmp
は一例にすぎません)。
開発サイクルでは、設定されたターゲットインデックスに対して、すべてのクエリの説明を実行します。
QueryBuilder の DEBUG ログを有効にして、基になる説明可能な XPath クエリを取得します。
com.day.cq.search.impl.builder.QueryImpl
の新しいロガーをDEBUGに作成します。上述のクラスで DEBUG を有効にすると、Query Builder で生成された XPath がログに表示されます。
関連する QueryBuilder クエリのログエントリから XPath クエリをコピーします。以下に例を示します。
com.day.cq.search.impl.builder.QueryImpl XPath query: /jcr:root/content//element(*, cq:Page)[(jcr:contains(jcr:content, "Geometrixx") or jcr:contains(jcr:content/@cq:tags, "Geometrixx"))]
XPathクエリをExplainクエリにXPathとして貼り付けて、クエリ計画を完了します
開発サイクルでは、設定されたターゲットインデックスに対して、すべてのクエリの説明を実行します。
ログから説明可能な XPath を取得する
QueryBuilder の DEBUG ログを有効にして、基になる説明可能な XPath クエリを取得します。
com.day.cq.search.impl.builder.QueryImpl
の新しいロガーをDEBUGに作成します。上述のクラスで DEBUG を有効にすると、Query Builder で生成された XPath がログに表示されます。
関連する QueryBuilder クエリのログエントリから XPath クエリをコピーします。以下に例を示します。
com.day.cq.search.impl.builder.QueryImpl XPath query: /jcr:root/content//element(*, cq:Page)[(jcr:contains(jcr:content, "Geometrixx") or jcr:contains(jcr:content/@cq:tags, "Geometrixx"))]
XPathクエリをExplainクエリにXPathとして貼り付けて、クエリ計画を完了します
Query Builder Debugger を使用して説明可能な XPath を取得する
Query Builder 以外のクエリ(XPath、JCR-SQL2)は、クエリの説明を実行に直接指定できます。
QueryBuilder でクエリをデバッグする方法の概要については、以下のビデオを参照してください。
ロガーの設定は、独自のロガーとライターの作成で説明します。
テストおよびデバッグの際に、記述されたクエリを実行したときの Query Builder 実装のログ出力(情報レベル):
com.day.cq.search.impl.builder.QueryImpl executing query (predicate tree):
null=group: limit=20, offset=0[
{group=group: or=true[
{1_fulltext=fulltext: fulltext=Geometrixx, relPath=jcr:content}
{2_fulltext=fulltext: fulltext=Geometrixx, relPath=jcr:content/@cq:tags}
]}
{path=path: path=/content}
{type=type: type=cq:Page}
]
com.day.cq.search.impl.builder.QueryImpl XPath query: /jcr:root/content//element(*, cq:Page)[(jcr:contains(jcr:content, "Geometrixx") or jcr:contains(jcr:content/@cq:tags, "Geometrixx"))]
com.day.cq.search.impl.builder.QueryImpl no filtering predicates
com.day.cq.search.impl.builder.QueryImpl query execution took 69 ms
フィルターをおこなう述語エバリュエーターや、コンパレーターでカスタム順序を使用する述語エバリュエーターを使用するクエリがある場合は、クエリ内にそのことも記述されます。
com.day.cq.search.impl.builder.QueryImpl executing query (predicate tree):
null=group: [
{nodename=nodename: nodename=*.jar}
{orderby=orderby: orderby=@jcr:content/jcr:lastModified}
{type=type: type=nt:file}
]
com.day.cq.search.impl.builder.QueryImpl custom order by comparator: jcr:content/jcr:lastModified
com.day.cq.search.impl.builder.QueryImpl XPath query: //element(*, nt:file)
com.day.cq.search.impl.builder.QueryImpl filtering predicates: {nodename=nodename: nodename=*.jar}
com.day.cq.search.impl.builder.QueryImpl query execution took 272 ms
Javadoc | 説明 |
---|---|
com.day.cq.search | 基本の QueryBuilder と Query API |
com.day.cq.search.result | Result API |
com.day.cq.search.facets | ファセット |
com.day.cq.search.facets.buckets | バケット(ファセット内に含まれる) |
com.day.cq.search.eval | 述語エバリュエーター |
com.day.cq.search.facets.extractors | ファセット抽出(エバリュエーター用) |
com.day.cq.search.writer | QueryBuilder サーブレット(/bin/querybuilder.json)用の JSON 結果ヒットライター |