Subqueries in SOLR
Met behulp van SOLR kan je snel gegevens ophalen en vertonen op je website of in je applicatie. Informatie in SOLR wordt opgeslagen in objecten die documents worden genoemd. Een document is niet genest: elk eigenschap van een document (een field) is een type veld, maar nooit een ander document.
Als je gegevens ophaalt uit SOLR, dan krijg je deze kortom niet-genest terug, ook al zijn de gegevens in je business model wel genest. Als voorbeeld zou je pagina’s kunnen hebben met daarin afbeeldingen. Dit model XML-achtig beschreven als:
<page>
<images>
<image />
<image />
</images>
</page>
In je database zouden dit waarschijnlijk (maar niet noodzakelijk) twee verschillende tabellen zijn, zeg pages en images, en de images-tabel heeft een verwijzing naar pages via page_id. Je applicatielaag koppelt deze object, bijvoorbeeld in C#:
List<Image> Images;
Page.Image.FirstOrDefault();
In SOLR is deze manier van relaties niet beschikbaar. Daar zie je iets als:
{
"page_id":1,
"item_type":"page",
"id":"1308eb79-8336-4dbf-aa47-b9234504ab46",
"_version_":1637117683543572480},
{
"page_id":2,
"item_type":"page",
"id":"78ca2798-8d15-4980-9e30-f5414c068846",
"_version_":1637117712127754240},
{
"page_id":2,
"item_type":"image",
"id":"acbb7243-11c4-42f3-8021-6b4267600c76",
"_version_":1637117720442961920}]
}
Wil je alle pagina’s ophalen? Dan geven we als filter query op “item_type:page”:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"*:*",
"fq":"item_type:page",
"_":"1561278187397"}},
"response":{"numFound":2,"start":0,"docs":[
{
"page_id":1,
"item_type":"page",
"id":"1308eb79-8336-4dbf-aa47-b9234504ab46",
"_version_":1637117683543572480},
{
"page_id":2,
"item_type":"page",
"id":"78ca2798-8d15-4980-9e30-f5414c068846",
"_version_":1637117712127754240}]
}}
Maar nu wil je uiteraard ook de gekoppelde afbeeldingen hebben per pagina. Je zou in applicatielaag door pagina’s kunnen loopen en dat per pagina een nieuwe query kunnen doen:
{
"responseHeader":{
"status":0,
"QTime":4,
"params":{
"q":"*:*",
"fq":["item_type:image",
"page_id:2"],
"_":"1561278988417"}},
"response":{"numFound":1,"start":0,"docs":[
{
"page_id":2,
"item_type":"image",
"id":"acbb7243-11c4-42f3-8021-6b4267600c76",
"_version_":1637117720442961920}]
}}
Dit is natuurlijk niet erg efficiënt. Het zou mooi zijn als je de data wel al genest terugkrijgt, zodat je maar één query hoeft uit te voeren. SOLR heeft hier een oplossing voor: subqueries.
Met de parameter fl geef je aan welke velden je terug wilt geven: fl=page_id,item_type. Wil je een subquery definiëren, dan geef je die op als veld: fl=page_id,item_type,images:[subquery]:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"*:*",
"fl":"page_id,item_type,images:[subquery]",
"fq":"item_type:page",
"_":"1561278988417"}},
"response":{"numFound":2,"start":0,"docs":[
{
"page_id":1,
"item_type":"page",
"images":{"numFound":0,"start":0,"docs":[]
}},
{
"page_id":2,
"item_type":"page",
"images":{"numFound":0,"start":0,"docs":[]
}}]
}}
Afbeeldingen zijn nu een subquery per teruggegeven document, met als alias images. Alleen weet SOLR nog niet wat dat betekent, images, en dus zijn ook hier queries en filter queries nodig. Hiervoor gebruik je images.q en images.fq:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"*:*",
"images.fq":"item_type:image",
"fl":"page_id,item_type,images:[subquery]",
"images.q":"*:*",
"fq":"item_type:page",
"_":"1561278988417"}},
"response":{"numFound":2,"start":0,"docs":[
{
"page_id":1,
"item_type":"page",
"images":{"numFound":1,"start":0,"docs":[
{
"page_id":2,
"item_type":"image",
"id":"acbb7243-11c4-42f3-8021-6b4267600c76",
"_version_":1637117720442961920}]
}},
{
"page_id":2,
"item_type":"page",
"images":{"numFound":1,"start":0,"docs":[
{
"page_id":2,
"item_type":"image",
"id":"acbb7243-11c4-42f3-8021-6b4267600c76",
"_version_":1637117720442961920}]
}}]
}}
Nu heb je echter aan alle pagina’s alle afbeeldingen gekoppeld, je moet dus op de een of andere manier een afbeelding koppelen aan een specifieke pagina. In het model hierboven heeft een image een page_id, waarmee wordt aangegeven aan welke pagina deze is gekoppeld. Om documenten aan elkaar te koppelen in een subquery, gebruik je het volgende als query van de subquery: {!terms f=page_id v=$row.page_id}". Daarnaast worden nu alle velden teruggegeven, dat kan ook anders met images.fl:
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"*:*",
"images.fq":"item_type:image",
"fl":"page_id,images:[subquery]",
"images.q":"{!terms f=page_id v=$row.page_id}",
"images.fl":"item_type,page_id",
"fq":"item_type:page",
"_":"1561276798777"}},
"response":{"numFound":2,"start":0,"docs":[
{
"page_id":1,
"images":{"numFound":0,"start":0,"docs":[]
}},
{
"page_id":2,
"images":{"numFound":1,"start":0,"docs":[
{
"page_id":2,
"item_type":"image"}]
}}]
}}
Nu heb je met één query alle pagina’s en gekoppelde afbeeldingen. In bovenstaande voorbeelden ontbreken uiteraard nuttige velden als url en image_id; deze kan je eenvoudig toevoegen via de fields parameter (<subquery>.fl).