I wanna create nested entity with DataImportHandler.
I use Solr 8.6, Postgress 12, openjdk-11.
My config (schema.xml) looks like this:
<schema name="products" version="1.5">
<field name="_version_" type="long" indexed="true" stored="true"/>
<field name="_root_" type="int" indexed="true" stored="false"/>
<uniqueKey>id</uniqueKey>
<field name="id" type="int" indexed="true" stored="true" required="true" multiValued="false"/>
<field name="price" type="float" indexed="true" required="true" stored="true"/>
<field name="categories" type="int" indexed="false" stored="true" required="true" multiValued="true"/>
<field name="pictures" type="string" indexed="true" stored="true" multiValued="true"/>
<field name="pid" type="int" indexed="true" stored="true" />
<field name="previewUrl " type="string" indexed="true" stored="true" />
</schema>
data-config.xml
<dataConfig>
<dataSource type="JdbcDataSource"
driver="org.postgresql.Driver"
url="jdbc:postgresql://${db.host}/myDB"
user="user"
password="myPassword"
/>
<document>
<entity name="products"
pk="id"
transformer="DateFormatTransformer"
query="SELECT * from products"
deltaQuery="SELECT id FROM products WHERE updated > '${dataimporter.last_index_time}'::timestamp"
deltaImportQuery="SELECT * FROM products WHERE id=${dataimporter.delta.id}"
/>
<field column="id" name="id"/>
<field column="price" name="price"/>
<entity name="categories"
query="SELECT category_id FROM product_category WHERE product_id='${products.id}'">
<field column="category_id" name="categories"/>
</entity>
<entity name="pictures"
child="true"
pk="pid"
query="SELECT * FROM pictures WHERE product_id='${products.id}'"
>
<field column="id" name="pid"/>
<field column="preview_url" name="previewUrl"/>
</entity>
</entity>
</document>
</dataConfig>
This is the result I expect:
[
{
"id":1,
"price": 10,
"categories": [1, 2]
"pictures": [
{
"pid":1,
"previewUrl":"/url"
},
{
"pid":2,
"previewUrl":"/url"
},
]
"_version_":1674819829308063744
}
]
But I get the following error:
org.apache.solr.common.SolrException: [doc=null] missing required field: price
What am I doing wrong?
Related
20180216-17:21:04.640 : 8=FIX.4.2;9=115;35=V;34=3;49=SNDJ;52=20180216-17:21:04.640;56=BROKER;55=EUR/USD;146=1;262=676;263=1;264=1;265=1;266=Y;267=1;269=0;10=061;
20180216-17:21:04.641 : 8=FIX.4.2;9=119;35=3;34=3;49=BROKER;52=20180216-17:21:04.641;56=SNDJ;45=3;58=Tag not defined for this message type;371=55;372=V;373=2;10=237;
I receive 'tag not defined for this message type' rejects (35=3) when attempting to send 35=V messages. I have added and removed ValidateUserDefinedFields, ValidateFieldsOutOfOrder.
I have razed the group structure, re-added it, redefined both Symbol and NoRelatedSym types (to string, symbol, int, numingroup etc.), changed the symbol being sent to EURUSD, TEST, etc. and nothing works.
Have I missed something very simple here? It seems related to the fact that the request message puts the symbol tag ahead of the group, but I do not know why.
MarketDataRequest.h:
FIELD_SET(*this, FIX::NoRelatedSym);
class NoRelatedSym: public FIX::Group
{
public:
NoRelatedSym() :
FIX::Group(146,55,FIX::message_order(55,65,48,22,167,200,205,201,2
02,206,231,223,207,106,348,349,107,350,351,336,0)) {}
FIELD_SET(*this, FIX::Symbol);
....
};
My current FIX 4.2 .xml set up for MarketDataRequest messages looks like:
<message name='MarketDataRequest' msgtype='V' msgcat='app'>
<field name='MDReqID' required='Y' />
<field name='SubscriptionRequestType' required='Y' />
<field name='MarketDepth' required='Y' />
<field name='MDUpdateType' required='N' />
<field name='AggregatedBook' required='N' />
<group name='NoMDEntryTypes' required='Y'>
<field name='MDEntryType' required='Y' />
</group>
<group name='NoRelatedSym' required='Y'>
<field name='Symbol' required='Y' />
<field name='SymbolSfx' required='N' />
<field name='SecurityID' required='N' />
<field name='IDSource' required='N' />
<field name='SecurityType' required='N' />
<field name='MaturityMonthYear' required='N' />
<field name='MaturityDay' required='N' />
<field name='PutOrCall' required='N' />
<field name='StrikePrice' required='N' />
<field name='OptAttribute' required='N' />
<field name='ContractMultiplier' required='N' />
<field name='CouponRate' required='N' />
<field name='SecurityExchange' required='N' />
<field name='Issuer' required='N' />
<field name='EncodedIssuerLen' required='N' />
<field name='EncodedIssuer' required='N' />
<field name='SecurityDesc' required='N' />
<field name='EncodedSecurityDescLen' required='N' />
<field name='EncodedSecurityDesc' required='N' />
<field name='TradingSessionID' required='N' />
</group>
</message>
Configuration Settings:
[DEFAULT]
BeginString=FIX.4.2
ReconnectInterval=60
SocketAcceptPort=7091
SenderCompID=SNDJ
TargetCompID=BROKER
SocketNodelay=Y
PersistMessage=Y
FileStorePath=cache
FileLogPath=log
[SESSION]
ConnectionType=acceptor
StartTime=00:30:00
EndTime=23:30:00
ReconnectInterval=30
HeartBtInt=15
SocketAcceptPort=7091
SocketReuseAddress=Y
DataDictionary=spec/FIX42.xml
AppDataDictionary=spec/FIX42.xml
SenderCompID=BROKER
TargetCompID=SNDJ
FileStorePath=cache
FileLogPath=log
[SESSION]
BeginString=FIX.4.2
ConnectionType=initiator
StartTime=00:30:00
EndTime=23:30:00
ReconnectInterval=15
HeartBtInt=15
SocketConnectPort=7091
SocketConnectHost=127.0.0.1
DataDictionary=spec/FIX42.xml
AppDataDictionary=spec/FIX42.xml
SenderCompID=SNDJ
TargetCompID=BROKER
FileStorePath=cache
FileLogPath=log
Thanks
The message that you are sending is invalid per your own DD.
Look at the first body fields after the header ends:
55=EUR/USD;146=1;262=676;...
That 55 field is supposed to be inside the 146 repeating group, but its placement puts it prior to the group.
I suspect your config may be to blame. If you update your question to include the config, I will probably be able to see what's wrong and update this answer.
UPDATE:
You are missing UseDataDictionary=Y from your config, though that's not the cause of your problem. (You need it to receive messages correctly, though.)
Also, you don't need AppDataDictionary -- that's only for FIX 5+.
I am seeing this exception while I am trying to index data from MongoDB collection :
Exception while processing: products document : SolrInputDocument(fields: []):org.apache.solr.handler.dataimport.DataImportHandlerException: com.mongodb.util.JSONParseException:
{idStr,name,code,description,price,brand,size,color}
^
at org.apache.solr.handler.dataimport.MongoEntityProcessor.initQuery(MongoEntityProcessor.java:46)
at org.apache.solr.handler.dataimport.MongoEntityProcessor.nextRow(MongoEntityProcessor.java:54)
at org.apache.solr.handler.dataimport.EntityProcessorWrapper.nextRow(EntityProcessorWrapper.java:244)
at org.apache.solr.handler.dataimport.DocBuilder.buildDocument(DocBuilder.java:476)
at org.apache.solr.handler.dataimport.DocBuilder.buildDocument(DocBuilder.java:415)
at org.apache.solr.handler.dataimport.DataImporter.runCmd(DataImporter.java:481)
at org.apache.solr.handler.dataimport.DataImporter$1.run(DataImporter.java:462)
Caused by: com.mongodb.util.JSONParseException:
{idStr,name,code,description,price,brand,size,color}
^
at com.mongodb.util.JSONParser.parseString(JSON.java:387)
Following is my data-source-config file in dataimport directory in conf folder of my core :
<dataConfig>
<dataSource name="mymongodb" type="MongoDataSource" database="mongodb://*.*.*.*/testdb" />
<document name="data">
<entity
name="products"
processor="MongoEntityProcessor"
query="{idStr,name,code,description,price,brand,size,color}"
collection="products"
datasource="mymongodb"
transformer="MongoMapperTransformer" >
<field column="idstr" name="idstr" mongoField="idStr"/>
<field column="name" name="name" mongoField="name"/>
<field column="code" name="code" mongoField="code"/>
<field column="description" name="description" mongoField="description"/>
<field column="price" name="price" mongoField="price"/>
<field column="brand" name="brand" mongoField="brand"/>
<field column="size" name="size" mongoField="size"/>
<field column="color" name="color" mongoField="color"/>
<entity
name="categories"
processor="MongoEntityProcessor"
query="{'idStr':'${categories.idstr}'}"
collection="categories"
datasource="mymongodb"
transformer="MongoMapperTransformer">
<field column="type" name="type" mongoField="type"/>
</entity>
</entity>
</document>
</dataConfig>
I am trying to join the field idStr of categories collection with the idStr of products collection(field name => idstr) and get the above fields ( name,description,... from products and type field from categories).
Any comments/solution on this exception would be really appreciated.Thanks!
Your SOLR field is declared as idstr but you are referencing it in the query attribute of dataConfig as idStr (camelcase difference).
I was able to resolve this ...
Following is the working configuration in the data-source-config file :
<entity
name="products"
query="select idStr,name,code,description,price,brand,size,color from products">
<field name="prodidStr" column="idStr" />
<field name="name" column="name" />
<field name="code" column="name" />
<field name="description" column="description" />
<field name="price" column="price" />
<field name="brand" column="brand" />
<field name="size" column="size" />
<field name="color" column="color" />
<entity
name="categories"
dataSource="mongod"
query="select idStr,ancestors from categories where idStr = '${products.idStr}'">
<field name="catidStr" column="idStr" />
<field name="ancestors" column="ancestors" />
</entity>
</entity>
I'm new to OpenErp.
I want to Add a new field "mother_name" in res.partner.So i have added the following code to res.partner.py
In column's i have add like this
_columns = {
'name': fields.char('Name', size=128, required=True, select=True),
'date': fields.date('Date', select=1),
'title': fields.many2one('res.partner.title', 'Title'),
'parent_id': fields.many2one('res.partner', 'Related Company'),
'child_ids': fields.one2many('res.partner', 'parent_id', 'Contacts', domain=[('active','=',True)]), # force "active_test" domain to bypass _search() override
'ref': fields.char('Reference', size=64, select=1),
'date_delivery': fields.date('Expected Delivery Date'),
'mother_ln': fields.char('Mother', size=64),
.
.
.
}
Now in res_partner_view.xml . I'm using the following code
<record id="view_partner_tree" model="ir.ui.view">
<field name="name">res.partner.tree</field>
<field name="model">res.partner</field>
<field eval="8" name="priority"/>
<field name="arch" type="xml">
<tree string="Contacts">
<field name="name"/>
<field name="function" invisible="1"/>
<field name="phone"/>
<field name="email"/>
<field name="user_id" invisible="1"/>
<field name="is_company" invisible="1"/>
<field name="country" invisible="1"/>
<field name="country_id" invisible="1"/>
<field name="date_delivery"/>
<field name="mother_ln"/>
<field name="parent_id" invisible="1"/>
</tree>
</field>
</record>
<record id="view_partner_form" model="ir.ui.view">
<field name="name">res.partner.form</field>
<field name="model">res.partner</field>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<form string="Partners" version="7.0">
<sheet>
<field name="image" widget='image' class="oe_left oe_avatar" options='{"preview_image": "image_medium", "size": [90, 90]}'/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name"/> (
<field name="is_company" on_change="onchange_type(is_company)" class="oe_inline"/> <label for="is_company" string="Is a Company?"/>)
</div>
<h1>
<field name="name" default_focus="1" placeholder="Name" />
</h1>
<field name="parent_id"
placeholder="Company"
domain="[('is_company', '=', True)]" context="{'default_is_company': True, 'default_supplier': supplier}"
attrs="{'invisible': [('is_company','=', True),('parent_id', '=', False)]}"
on_change="onchange_address(use_parent_address, parent_id)"/>
<!-- <field name="category_id" widget="many2many_tags" placeholder="Tags..."/> -->
</div>
<div class="oe_right oe_button_box" name="buttons"> </div>
<group>
<group>
<label for="type" attrs="{'invisible': [('parent_id','=', False)]}"/>
<div attrs="{'invisible': [('parent_id','=', False)]}" name="div_type">
<field class="oe_inline"
name="type"/>
</div>
<label for="street" string="Address"/>
<div>
<field name="use_parent_address" class="oe_edit_only oe_inline"
on_change="onchange_address(use_parent_address, parent_id)"
attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"/>
<label for="use_parent_address" class="oe_edit_only" attrs="{'invisible': [('parent_id','=', False),('use_parent_address','=',False)]}"/>
<button name="open_parent" type="object" string="(edit company address)" class="oe_link oe_edit_only"
attrs="{'invisible': ['|',('parent_id','=', False),('use_parent_address','=',False)]}"/>
<field name="street" placeholder="Street..." attrs="{'readonly': [('use_parent_address','=',True)]}"/>
<field name="street2" attrs="{'readonly': [('use_parent_address','=',True)]}"/>
<div class="address_format">
<field name="city" placeholder="City" style="width: 40%%" attrs="{'readonly': [('use_parent_address','=',True)]}"/>
<field name="state_id" class="oe_no_button" placeholder="State" style="width: 37%%" options='{"no_open": True}' on_change="onchange_state(state_id)" attrs="{'readonly': [('use_parent_address','=',True)]}"/>
<field name="zip" placeholder="ZIP" style="width: 20%%" attrs="{'readonly': [('use_parent_address','=',True)]}"/>
</div>
<field name="country_id" placeholder="Country" class="oe_no_button" options='{"no_open": True}' attrs="{'readonly': [('use_parent_address','=',True)]}"/>
</div>
</group>
<group>
<!-- We Don't require Job Position for Customer -->
<!-- <field name="function" placeholder="e.g. Sales Director"
attrs="{'invisible': [('is_company','=', True)]}"/> -->
<field name="phone" placeholder="e.g. +32.81.81.37.00"/>
<field name="mobile"/>
<field name="fax"/>
<field name="email" widget="email"/>
<!-- <field name="title" domain="[('domain', '=', 'contact')]"
options='{"no_open": True}' attrs="{'invisible': [('is_company','=', True)]}" /> -->
</group>
<field name="date_delivery"/>
<field name="mother_ln"/>
</group>
.
.
.
</record>
Better is you inherit that code and add your field rather doing changes to main code.
for your py file
class res_partner(osv.osv):
_inherit= 'res.partner'
_columns = {
'mother_ln': fields.char('Mother', size=64),
'father_name': fields.char('Father', size=64),
# this is comment. -> you can add as many types of fields as you want - eg. char ,
# boolean, float, integer, one2many, many2one etc.
}
res_partner()
for your xml file :-
<record id="view_res_partner_inherited" model="ir.ui.view">
<field name="name">view.res.partner.inherited</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form" />
<field name="arch" type="xml">
<field name="date_delivery" position="after">
<field name="mother_ln" />
<field name="father_name" />
<!--comment- likewise this is your view, add all your fields here in view -->
</field>
</field>
</record>
I am able to successfully integrate between MONGODB & SOLR, using MONGO-CONNECTOR. And whenever, I update or add any thing, in the sample collection i have created, it copies only two or three fields in a documents, and rest of the fields data are not copied into solr. This is some thing I am not able to do it.
This is my collection and its document details. Name of collection: testdb
document inserted as follows:
db.testdb.insert( {
... _id: "101",
... name: "test",
... description: "descr",
... mydesc: "mydescr",
... nmdsc: "nmdsc1",
... coords: "coords1"
... })
And the data sync between solr and mongo logs says successful:
2014-01-17 19:35:38,462 - INFO - Finished 'http://<hostname>:<port>/solr/update/?co
mmit=true' (post) with body '<add><doc>' in 0.210 seconds.
But when I execute a query to see the document data it says only these fields data:
{
"responseHeader": {
"status": 0,
"QTime": 0,
"params": {
"q": "*:*",
"wt": "json"
}
},
"response": {
"numFound": 1,
"start": 0,
"docs": [
{
"id": "101",
"description": "descr",
"name": "test",
"_version_": 1457486601392226300
}
]
}
}
Clearly i can see that following fields & respective data are not copied into solr:
... mydesc: "mydescr",
... nmdsc: "nmdsc1",
... coords: "coords1"
Following is my schema.xml:
<schema name="narayana" version="1.5">
<types>
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true" />
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0" />
<fieldType name="text_wslc" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
<fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" positionIncrementGap="0" />
<fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate" />
<fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="6" positionIncrementGap="0" />
</types>
<fields>
<field name="id" type="string" indexed="true" stored="true" required="true" />
<field name="name" type="text_wslc" indexed="true" stored="true" />
<field name="description" type="text_wslc" indexed="true" stored="true" />
<field name="mydesc" type="text_wslc" indexed="true" stored="true" />
<field name="nmdsc" type="text_wslc" indexed="true" stored="true" multiValued="true" />
<field name="coords" type="string" indexed="true" stored="true" multiValued="true" />
<field name="_version_" type="long" indexed="true" stored="true" />
<dynamicField name="*" type="string" indexed="true" stored="true" />
</fields>
<uniqueKey>id</uniqueKey>
<defaultSearchField>nmdsc</defaultSearchField>
<!-- we don't want too many results in this usecase -->
<solrQueryParser defaultOperator="AND" />
<copyField source="name" dest="nmdsc" />
<copyField source="description" dest="nmdsc" />
</schema>
You are defining your configuration to copy only two specific fields, if you want to index additional fields, you should be adding them to your configuration file and the same import cycle again:
<!-- we don't want too many results in this usecase -->
<solrQueryParser defaultOperator="AND" />
<copyField source="name" dest="nmdsc" />
<copyField source="description" dest="nmdsc" />
Suppose I have a test application representing some friends list. The application uses a collection where all documents are in the following format:
_id : ObjectId("someString"),
name : "George",
description : "some text",
age : 35,
friends : {
[
{
name: "Peter",
age: 30
town: {
name_town: "Paris",
country: "France"
}
},
{
name: "Thomas",
age: 25
town: {
name_town: "Berlin",
country: "Germany"
}
}, ... // more friends
]
}
... // more documents
How can I describe such collection in the schema.xml ? I need to produce facet queries like: "Give me countries, where George's friends live". Another use case may be - "Return all documents(persons), whose friend is 30 years old." etc.
My initial idea is to mark "friends" attribute as text field by this schema.xml definition:
<fieldType name="text_wslc" class="solr.TextField" positionIncrementGap="100">
....
<field name="friends" type="text_wslc" indexed="true" stored="true" />
and try to search for eg. "age" and "30" words in the text, but it is not a very reliable solution.
Please, leave aside not logically well-formed architecture of the collection. It is only an example of similar problem I am just facing.
Any help or idea will be highly appreciated.
EDIT:
Sample 'schema.xml'
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="text-schema" version="1.5">
<types>
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0" />
<fieldType name="trInt" class="solr.TrieIntField" precisionStep="0" omitNorms="true" />
<fieldType name="text_p" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.TrimFilterFactory"/>
<filter class="solr.WordDelimiterFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.TrimFilterFactory"/>
<filter class="solr.WordDelimiterFilterFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
</types>
<fields>
<field name="_id" type="string" indexed="true" stored="true" required="true" />
<field name="_version_" type="long" indexed="true" stored="true"/>
<field name="_ts" type="long" indexed="true" stored="true"/>
<field name="ns" type="string" indexed="true" stored="true"/>
<field name="description" type="text_p" indexed="true" stored="true" />
<field name="name" type="text_p" indexed="true" stored="true" />
<field name="age" type="trInt" indexed="true" stored="true" />
<field name="friends" type="text_p" indexed="true" stored="true" /> <!-- Here is the problem - when the type is text_p, all fields are considered as a text; optimal solution would be something like "collection" tag to mark name_town and town as descendant of the field 'friends' but unfortunately, this is not how the solr works-->
<field name="town" type="text_p" indexed="true" stored="true"/>
<field name="name_town" type="string" indexed="true" stored="true"/>
<field name="town" type="string" indexed="true" stored="true"/>
</fields>
<uniqueKey>_id</uniqueKey>
As Solr is document-centric you will need to flatten as much as you can down. According to the sample you have given, I would create a schema.xml like the one below.
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="friends" version="1.0">
<fields>
<field name="id"
type="int" indexed="true" stored="true" multiValued="false" />
<field name="name"
type="text" indexed="true" stored="true" multiValued="false" />
<field name="description"
type="text" indexed="true" stored="true" multiValued="false" />
<field name="age"
type="int" indexed="true" stored="true" multiValued="false" />
<field name="town"
type="text" indexed="true" stored="true" multiValued="false" />
<field name="townRaw"
type="string" indexed="true" stored="true" multiValued="false" />
<field name="country"
type="text" indexed="true" stored="true" multiValued="false" />
<field name="countryRaw"
type="string" indexed="true" stored="true" multiValued="false" />
<field name="friends"
type="int" indexed="true" stored="true" multiValued="true" />
</fields>
<copyField source="country" dest="countryRaw" />
<copyField source="town" dest="townRaw" />
<types>
<fieldType name="string" class="solr.StrField" sortMissingLast="true"/>
<fieldType name="int" class="solr.TrieIntField"
precisionStep="0" positionIncrementGap="0" />
<fieldType name="text" class="solr.TextField"
positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory" />
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
</types>
</schema>
I would go with the approach to model each person for itself. The relationship between two persons is modelled via the attribute friends, which translates into an array of IDs. So at index time you would need to fetch the IDs of all friends for a person and put them into that field.
Most of the other fields are straight forward. Interesting are the two Raw fields. Since you said that you want to facet on the country you will need the country unchanged or optimized for faceting. Usually the types of fields differ depending on their purpose (searching for them, faceting by them, autosuggesting them, etc.). In this case country and town are indexed just as they are given in.
Now to your use cases,
Give me countries, where George's friends live
This can then be done by faceting. You could query
for the ID of George
facet on countryRaw
Such a query would look like q=friends:1&rows=0&facet=true&facet.field=countryRaw&facet.mincount=1
Return all documents(persons), whose friend is 30 years old.
This one is harder. First off you will need Solr's join feature. You need to configure this in your solrconfig.xml.
<config>
<!-- loads of other stuff -->
<queryParser name="join" class="org.apache.solr.search.JoinQParserPlugin" />
<!-- loads of other stuff -->
</config>
The according join query would look like this q={!join from=id to=friends}age:[30 TO *]
This explains as follows
with age:[30 TO *] you search for all persons that are of age 30 or older
then you take their id and join it on the friends attibute of all others
this will return you all persons that have the ids matched by the initial query within their friends attribute
As I have not written this off of my mind, you may have a look on my solrsample project on github. I have added a test case there that deals about the question:
https://github.com/chriseverty/solrsample/blob/master/src/main/java/de/cheffe/solrsample/FriendJoinTest.java