How do I get Highlighted Snippets using SolrJ? - solr

I am migrating my application from Lucene to Solr. Solr handles highlighting a lot better, however if for instance I search the key word "city", I would expect a response like:
{
"id":"fdc3833a-0e4f-4314-ba8c",
"title": "Paris is a beautiful <b>city</b>",
"description": "The <b>city</b> is a great example of......",
}
while I am getting the following response instead:
{
"id":"fdc3833a-0e4f-4314-ba8c",
"title": "Paris is a beautiful city",
"description": "The city is a great example of......",
}
"highlighting": {
"fdc3833a-0e4f-4314-ba8c": {
"title": [
"Paris is a beautiful <b>city</b>"
],
"description": [
"The <b>city</b> is a great example of......"
]
}
}
So as you can see, instead of getting the highlighted term within the result, I am getting an extra section called highlighting and that means that my Java code has to change.
My question is: how will I get the highlight snippets in SolrJ?

In SolrJ is possible to get the highlighted snippets using the following code:
public String getHighlightedText(final QueryResponse queryResponse, final String fieldName, final String docId) {
String highlightedText = "";
Map<String, Map<String, List<String>>> highlights = queryResponse.getHighlighting();
if (highlights!=null && MapUtils.isNotEmpty(highlights.get(docId))) {
List<String> snippets = highlights.get(docId).get(fieldName);
if (CollectionUtils.isNotEmpty(snippets)) {
highlightedText = getFragments(snippets);
}
}
return highlightedText;
}
private static final String getFragments(List<String> snippets){
StringBuilder fragments = new StringBuilder();
for (int i = 0; i < snippets.size(); i++) {
if (i > 0) {
fragments.append("............");
}
fragments.append(snippets.get(i));
}
return fragments.toString();
}
Please notice that this code will get you the best snippets for single-value fields while you will need some variations for multi-value fields.

Related

display content-type(Strategy) fields value in the form of array in groovy

I have a snippets of groovy code (inside strategy.groovy file) as shown below in which I want to display the output in the form of desired array.
The content-type Strategy (in crafter) has the following fields:
Title English (title_en_t)
Title French (title_fr_t)
Description English (description_en_t)
Description French (description_fr_t)
Players (players_o)
groovy code (inside strategy.groovy file):
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
strategy.addElement("players_o.first_name_s").text = rootStrategy.players_o.item.component.first_name_s != null ? rootStrategy.players_o.item.component.first_name_s : "" //Line A
strategy.addElement("players_o.last_name_s").text = rootStrategy.players_o.item.component.last_name_s != null ? rootStrategy.players_o.item.component.last_name_s : "" //Line B
return strategy
}
Line A and Line B display the o/p in the following fashion:
current o/p:
players_o.first_name_s: "[John, The]"
players_o.last_name_s: "[Cena, Undertaker]"
PROBLEM STATEMENT:
I am wondering what changes I need to do in the groovy code above at Line A and Line B so that it displays the o/p in this fashion:
DESIRED O/P:
players: [{
"item": [
{
"first_name_s": "John",
"last_name_s": "Cena"
},
{
"first_name_s": "The",
"last_name_s": "Undertaker"
}
]
}]
don't know what is the crafter-cms. and you have not provided the input for your code.
i found DefaultElement and addElement in dom4j library - so i assume it's the one used in crafter-cms
https://dom4j.github.io/javadoc/2.0.1/org/dom4j/tree/DefaultElement.html
you are expecting json in output but dom4j used to work with xml...
however. just a try
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
def players = strategy.addElement('players')
for(i in rootStrategy.players_o.item.component){
def item = players.addElement('item')
item.addElement('first_name_s').text = i.first_name_s
item.addElement('last_name_s').text = i.last_name_s
}
return strategy
}
the following code i used to debug it in standard groovy console:
#Grab(group='org.dom4j', module='dom4j', version='2.1.3')
import org.dom4j.tree.DefaultElement
import org.dom4j.Element
import org.dom4j.io.XMLWriter
import org.dom4j.io.OutputFormat
def rootStrategy=[
players_o:[
item:[
component:[
[
first_name_s:'John',
last_name_s: 'Cena',
],
[
first_name_s:'The',
last_name_s: 'Undertaker'
],
]
]
]
]
private createStrategyElement(rootStrategy, ctx) {
Element strategy = new DefaultElement("strategy")
def players = strategy.addElement('players')
for(i in rootStrategy.players_o.item.component){
def item = players.addElement('item')
item.addElement('first_name_s').text = i.first_name_s
item.addElement('last_name_s').text = i.last_name_s
}
return strategy
}
println new StringWriter().with{w->
new XMLWriter(w, OutputFormat.createPrettyPrint()).write( createStrategyElement(rootStrategy,null) )
w.toString()
}

Parse a JSON file using class in Flutter

I have a following problem. I just cannot understand what can be the easiest way to parse an information from JSON to an array of objects in flutter.
I have a following class:
class Organization {
String subject;
String organization;
String shortDescription;
String address;
String phoneNumber;
String contactInfo;
List<String> tags;
Organization({
this.subject,
this.organization,
this.shortDescription,
this.address,
this.phoneNumber,
this.contactInfo,
this.tags,
});
Organization.fromJson(Map<String, dynamic> json) {
subject = json['Subject'];
organization = json['Organization'];
shortDescription = json['Short description'];
address = json['Address'];
phoneNumber = json['Phone number'];
contactInfo = json['Contact info'];
tags = json['Tags'].cast<String>();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Subject'] = this.subject;
data['Organization'] = this.organization;
data['Short description'] = this.shortDescription;
data['Address'] = this.address;
data['Phone number'] = this.phoneNumber;
data['Contact info'] = this.contactInfo;
data['Tags'] = this.tags;
return data;
}
}
This is the small sample of Json file that I have in assets folder.
[
{
"Subject": "Cultuur - Culture ",
"Organization": "Museum aan de stroom ",
"Short description": "The MAS is a museum that provides you with permanent and temporary exhibitions. These offer a refreshing look at the city and it's history. At the top, on the tenth floor, you can enjoy a 360-degree panorama of the city, the port and the river.",
"Address": "Hanzestedenplaats 1, 2000 Antwerpen",
"Phone number": "+32 3 338 44 00",
"Contact info": "www.mas.be",
"Tags": ["Meeting place", "Museum", "Antwerp", "Art"]
},
{
"Subject": "Cultuur - Culture ",
"Organization": "Red star line museum ",
"Short description": "Through personal story's of immigrants the Red star line museum paints a picture of the immigration history in Antwerp. ",
"Address": "Montevideostraat 3, 2000 Antwerpen",
"Phone number": "+32 3 298 27 70",
"Contact info": "www.redstarline.be",
"Tags": ["Museum", "Antwerp", "History", "Immigration"]
}
]
I am just trying to parse information from this file to a list of Organisations as presented below:
List<Organisation> organisations = [];
Future<void> readJson() async {
final String response =
await rootBundle.loadString('assets/organisations.json');
final data = await json.decode(response);
organisations = data;
}
However I get Unhandled Exception: type List dynamic is not a subtype of type List Organisation error.
In my opinion data should be an array of my objects. What am I doing wrong? Or maybe is there simpler method to achieve this result?
At the final of readJson funciĆ³n, you can call Organization.fromJson like this:
Future<void> _readJson() async {
final String response =
await rootBundle.loadString('assets/data/organizations.json');
final List<dynamic> data = await json.decode(response);
for (dynamic it in data) {
final Organization organization = Organization.fromJson(it); // Parse data
organizations.add(organization); // and organization to List
}
}

Retrieve documents from cloudant db if they contain a value in an array property

This is how I currently do it, is there a better (more performant or simpler) way?
Documents in Cloudant db:
1 ..
{
"_id": "someId",
"_rev": "someRev",
"title": "someTitle",
"parentDirectories": [
'/', '/base_dir'
]
}
2 ..
{
"_id": "someId2",
"_rev": "someRev",
"title": "someTitle2",
"parentDirectories": [
'/', '/base_dir'
]
}
Currently,I have this view defined in Cloudant and set the include_docs parameter to true so the entire object is returned (this is required) ..
function (doc) {
if (doc.parentDirectories) {
for (i = 0; i < doc.parentDirectories.length; i++) {
emit(doc.parentDirectories[i], {_id: doc._id});
}
}
}
And would call the view using ..
/<database>/_design/<design-doc>/_view/my_view?include_docs=true
Using keys parameter set to: ['/'], so view returns the 2 documents above.
Your solution is fine. Creating a index whose keys are the keys from your array is a perfectly good way to retrieve documents that contain a known value. One small iteration is emit null as the value, as you are retrieving the whole document at query-time anyway (with include_docs=true):
function (doc) {
if (doc.parentDirectories) {
for (i = 0; i < doc.parentDirectories.length; i++) {
emit(doc.parentDirectories[i], null);
}
}
}
You could also emit a subset of your document:
function (doc) {
if (doc.parentDirectories) {
for (i = 0; i < doc.parentDirectories.length; i++) {
emit(doc.parentDirectories[i], doc.title);
}
}
}
meaning that you could avoid using include_docs=true, for a faster response.

Parsing SolrJ Facet Pivot Response

I have a Solr Schema and I am trying to do Facet Pivoting on it by passing multiple fields in facet.pivot parameter while making a call through SolrJ. My Solr response looks something like below:
"facet_pivot": {
"boolean_value,int_value": [
{
"field": "boolean_value",
"value": false,
"count": 1,
"pivot": [
{
"field": "int_value",
"value": 364,
"count": 1
}
]
},
{
"field": "boolean_value",
"value": true,
"count": 2,
"pivot": [
{
"field": "int_value",
"value": 406,
"count": 1
},
{
"field": "int_value",
"value": 409,
"count": 1
}
]
}
]
}
How, can I parse the above response using SolrJ in form of Nested objects i.e. PivotField having relation of which int_value falls under which boolean_value.
SolrJ Version tried: 4.10.4
Update:
When you make a call through SolrJ check the SolrQuery that gets generated. In my above case the SolrQuery was:
facet.pivot=boolean_value&facet.pivot=int_value
Solr considers above pivots as two different ones and you won't get nested pivoting. For Nested Pivoting your SolrQuery should have
facet.pivot=boolean_value,int_value
I had the same problem and wrote a simple tester application. It queries Solr and writes the pivot values to StdOut as hierarchical Strings. If there are any suggestions to implement that more nicely, please let me know. Here's the code:
public class Tester {
public static final String HIERARCHICAL_FACET_SEPARATOR = "/";
public static void main(String[] args) throws SolrServerException, IOException {
CloudSolrClient solr = ... ;
SolrQuery query = new SolrQuery(...);
query.setFacet(true);
query.addFacetPivotField(new String[]{"field1,field2,field3"});
QueryResponse result = solr.query(query);
NamedList<List<PivotField>> facetPivot = result.getFacetPivot();
List<String> parsedPivotResult = parsePivotResult(facetPivot);
parsedPivotResult.forEach((s) -> {
System.out.println(s);
});
}
private static List<String> parsePivotResult(final NamedList<List<PivotField>> pivotEntryList) {
final Set<String> outputItems = new HashSet<>();
for (final Entry<String, List<PivotField>> pivotEntry : pivotEntryList) {
System.out.println("Key: " + pivotEntry.getKey());
pivotEntry.getValue().forEach((pivotField) -> {
renderOutput(new StringBuilder(), pivotField, outputItems);
});
}
final List<String> output = new ArrayList<>(outputItems);
Collections.sort(output);
return output;
}
private static void renderOutput(final StringBuilder sb, final PivotField field, final Set<String> outputItems) {
final String fieldValue = field.getValue() != null ? ((String) field.getValue()).trim() : null;
final StringBuilder outputBuilder = new StringBuilder(sb);
if (field.getPivot() != null) {
if (outputBuilder.length() > 0) {
outputBuilder.append(HIERARCHICAL_FACET_SEPARATOR);
}
outputBuilder.append(fieldValue);
outputItems.add(new StringBuilder(outputBuilder).append(" (").append(field.getCount()).append(")").toString());
field.getPivot().forEach((subField) -> {
renderOutput(outputBuilder, subField, outputItems);
});
} else {
if (outputBuilder.length() > 0) {
outputBuilder.append(HIERARCHICAL_FACET_SEPARATOR);
}
outputBuilder.append(fieldValue);
outputItems.add(outputBuilder.append(" (").append(field.getCount()).append(")").toString());
}
}
}

how to add a complextype object dynamically to an array

We have created an array of complextype(Carrier field) objects. See below metadata
{ shortName : 'Person',
namespace : 'Demo',
autoGeneratedKeyType : breeze.AutoGeneratedKeyType.Identity,
"dataProperties": [
{
"name": "carriers",
"complexTypeName":"Carrier:#Test",
"isScalar":false
}]
}
The Carrier entity is defined as below:
{
"shortName": "Carrier",
"namespace": "Test",
"isComplexType": true,
"dataProperties": [
{
"name": "Testing",
"isScalar":true,
"dataType": "String"
}
]
}
We have the following matching data for the above entities:
{
carriers: [
{
Testing : 'InputBox1'
},
{
Testing : 'InputBox2'
}
]
}
We are trying to dynamically add the complextype object(Carrier) to the above carriers array by using the following approach:
var test = {
"Testing" : "Test"
};
var result = manager.createEntity('Carrier', test);
The above code throws an exception(undefined is not a function) inside breeze.debug.js at line number 12457(see below code)
entity = entityType.createEntity(initialValues);
The exception is thrown since the complextype entity does not have 'createEntity' function in it.
What are we missing here?
Excellent question - Sorry I didn't have a chance to address this earlier.
When adding a complexType object you need to use the createInstance() method instead of the createEntity.
var thisEntityType = manager.metadataStore.getEntityType('Carrier');
var thisEntity = thisEntityType.createInstance(initialValues);
Basically you get the complexType and then create an instance of it using the values you want assigned. Keep in mind the initial values should be a hash object of course. Often I will include a helper function to do this for me like this -
function createComplexType(entityType, constructorProperties) {
var thisEntityType = manager.metadataStore.getEntityType(entityType);
var thisEntity = thisEntityType.createInstance(constructorProperties);
return thisEntity;
}

Resources