Highlighting in SolrNet - c#

I have a problem to get highlighting working. I use a ASP.Net MVC Application with a WCF Service. The WCF Service return the results to the view. My code is:
public IEnumerable<Dictionary<string, string>> SolrQuery(string searchString)
{
SolrInitialSetup();
var solr = ServiceLocator.Current.GetInstance<ISolrOperations<SolrPDFDocument>>();
var results = solr.Query(new SolrQueryByField("author", searchString),
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "search_snippet" },
}
});
var returnedResults = new List<Dictionary<string, string>>();
if (results.Count > 0)
{
foreach (var pdfDoc in results)
{
var docBuffer = new Dictionary<string, string>();
docBuffer.Add("Id", ""+pdfDoc.Id.GetHashCode());
docBuffer.Add("Content", "" + pdfDoc.Content.ElementAt(0));
docBuffer.Add("Version", "" + pdfDoc.Version);
foreach (var h in results.Highlights[results[0].Id])
{
docBuffer.Add("search_snippet", String.Format("{0}", string.Join(", ", h.Value.ToArray())));
Debug.WriteLine("search_snippet", String.Format("{0}", string.Join(", ", h.Value.ToArray())));
}
returnedResults.Add(docBuffer);
}
}
return returnedResults;
}
# Line foreach (var h in results.Highlights[results[0].Id]) I get an error in the browser The given key was not present in the dictionary. I have no idea whats wrong.
My schema.xml:
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="content" type="text_general" indexed="true" stored="true" multiValued="true" termVectors="true" termPositions="true" termOffsets="true"/>
<field name="author" type="text_general" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true"/>
<field name="_version_" type="long" indexed="true" stored="true"/>
<field name="search_snippet" type="text_general" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true" />
My SolrPDFDocument.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SolrNet.Attributes;
namespace WcfSearchService
{
class SolrPDFDocument
{
[SolrUniqueKey("id")]
public string Id { get; set; }
[SolrField("author")]
public string Author { get; set; }
[SolrField("content")]
public ICollection<string> Content { get; set; }
[SolrField("search_snippet")]
public ICollection<string> SearchSnippet { get; set; }
[SolrField("_version_")]
public long Version { get; set; }
}
}
My solrconfig.xml
<requestHandler name="/select" class="solr.SearchHandler">
<!-- default values for query parameters can be specified, these
will be overridden by parameters in the request
-->
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
<str name="df">text</str>
<!-- Highlighting defaults -->
<str name="hl">on</str>
<str name="hl.fl">search_snippet author content</str>
</lst>
</requestHandler>

OK i have solved my problem.
Instead of using the sample code like that:
var results = solr.Query(new SolrQueryByField("author", searchString),
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "author" },
}
});
I set the Parameter of the new Fields like this:
var results = solr.Query(new SolrQueryByField("text", searchString),
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "*" },
}
});
So I'm getting all the snippets to all results. Thanks for your help!!!!

You might not be getting any highlight results at all. Can you debug your code and validate that results.Highlights is populated. Also check your highlighting defaults settings as I believe you need to set hl=true not on.
<str name="hl">true</str>

This is interesting. For me, this wasn't working (I wasn't getting any results):
var results = solr.Query(new SolrQueryByField("text", searchString),
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "*" },
}
});
But, this works for me:
var results = solr.Query(string.Format("{0}:{1}", "text", searchString),
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "*" },
}
});
I am using solr 4.6.0 and SolrNet build #173 (16 Oct 13 00:32)

Related

How to read multiple attributes from an xml file

I have the following xml:
<?xml version="1.0" encoding="UTF-8"?>
<TestRun id="2fc10ef6-b97f-49e5-a58d-863dfb599cb3" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
<Times creation="2019-08-26T11:27:34.3642040+00:00" queuing="2019-08-26T11:27:34.3642190+00:00" start="2019-08-26T11:27:29.1640690+00:00" finish="2019-08-26T11:29:28.0320260+00:00" />
<TestSettings name="default" id="3c3c8ad0-9076-4c83-a283-03f5490f906b">
<Deployment runDeploymentRoot="_9e3d0007c2b9 2019-08-26 11:27:34" />
</TestSettings>
<Results>
<UnitTestResult testName="FirstName" outcome="Passed" testListId="1">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="SecondName" outcome="Passed" testListId="2">
<Output>
</Output>
</UnitTestResult>
<UnitTestResult testName="Thirdname" outcome="Passed" testListId="3">
<Output>
</Output>
</UnitTestResult>
</Results>
</TestRun>
And i have the following classes:
{
public string testName { get; set; }
public string outcome { get; set; }
}
public DtoHeader ReadXmlFile()
{
var binDirectory = Path.GetDirectoryName(GetType().GetTypeInfo().Assembly.Location);
var file = Path.Combine(binDirectory, "myfile.xml");
var xDocument = XDocument.Load(file);
XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(new NameTable());
nameSpaceManager.AddNamespace("ns", "http://microsoft.com/schemas/VisualStudio/TeamTest/2010");
var items = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager).ToArray();
if (!items.Any())
{
}
return new DtoHeader
{
testName = items.Descendants().Attributes("testName").First().Value,
};
}
I will like to extract the value of the attributes testName and outcome and put these values in a list.However, I have not been able to do this after going through multiple examples.
items.Descendants().Attributes("testName") returns IEnumerable of XAttribute.
You need to select the Value member of each XAttibute and return a list.
var list = items.Descendants().Attributes("testName").Select(t => t.Value).ToList();
Edit:
Return as DtoHeader List:
var list = items.Descendants().Attributes("testName").Select(t =>
new DtoHeader
{
testName = t.Value
}
).ToList();
To get a list of DtoHeaders using Linq2Xml you could use the following approach:
var headers = xDocument.Root.XPathSelectElements("./ns:Results", nameSpaceManager)
.Elements()
.Select(x => new DtoHeader
{
testName = x.Attribute("testName").Value,
outcome = x.Attribute("outcome").Value
})
.ToList();
I suggest to use xmltocsharp to convert the xml to classes and use XmlSerializerto do the rest of the job. Here is the simplified code to get the desired result.
string requestBody = ReadFile("XMLFile1.xml");
XmlSerializer serializer = new XmlSerializer(typeof(TestRun));
using (TextReader reader = new StringReader(requestBody))
{
//convert the xml to object
TestRun testRun = (TestRun)serializer.Deserialize(reader);
foreach (var result in testRun.Results.UnitTestResult)
{
Console.WriteLine($"{result.TestName} : {result.Outcome}");
}
}
var doc = XDocument.Load(file);
var list = new System.Collections.Generic.List<DtoHeader>();
foreach (var n in doc.Descendants().First(node => node.Name.LocalName == "Results").Elements().Where(e => e.Name.LocalName == "UnitTestResult"))
{
list.Add(new DtoHeader
{
outcome = n.Attribute("outcome").Value,
testName = n.Attribute("testName").Value,
});
}

Get all the attribute values by attribute name from XML file using Linq to XML

I have an XML file and I have to extract all the attribute values from XML. I have tried the below one but I need it in Linq. Can anyone guide me how to do this.
Sample XML
<MapFile>
<Import>
<field name1="BorrowId" name2="EMPLID" />
<field name1="Firstname" name2="COMPLETENAME" />
<field name1="Address" name2="Address" Reference="Location" />
</Import>
<Location>
<Lookup Key="CC" Replace="1" />
<Lookup Key="CE" Replace="2" />
</Location>
</MapFile>
Expected Result
[0]:
CurrentVal = "BorrowId"
NewVal = "EMPLID"
Reference = null
ReferenceList = null
[1]:
CurrentVal = "Firstname"
NewVal = "COMPLETENAME"
Reference = null
ReferenceList = null
[2]:
CurrentVal = "Address"
NewVal = "Address"
Reference = "Location"
ReferenceList = [0]:
Key = "CC"
Value = "1"
[1]:
Key = "CE"
Value = "2"
Code
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#sPath);
var attrValues = xmlDoc.GetElementsByTagName("field");
List<MapFileModel> MapFileMod = new List<MapFileModel>();
foreach (XmlNode x in attrValues)
{
MapFileModel _objMapFile = new MapFileModel();
if (x.Attributes["name1"] != null)
{
_objMapFile.CurrentVal = x.Attributes["name1"] != null ? x.Attributes["name2"].Value : null;
_objMapFile.NewVal = x.Attributes["name2"] != null ? x.Attributes["name2"].Value : null;
_objMapFile.Reference = x.Attributes["Reference"] != null ? x.Attributes["Reference"].Value : null;
}
MapFileMod.Add(_objMapFile);
}
Okay, so it looks like you want something like this, which loads all the field elements in the Import just-below-root element, then loads the reference lists by finding every element which isn't Import.
var doc = XDocument.Load("foo.xml");
var replacements = doc
.Root
.Element("Import")
.Elements("field")
.Select(x => new Replacement {
CurrentValue = (string) x.Attribute("name1"),
NewValue = (string) x.Attribute("name2"),
Reference = (string) x.Attribute("reference")
})
.ToList();
var referenceLists = doc
.Root
.Elements()
.Where(f => f.Name.LocalName != "Import")
.ToDictionary(
x => x.Name.LocalName,
x => x.Elements("Lookup")
.Select(l => new KeyValuePair<string, string>(
(string) l.Attribute("Key"),
(string) l.Attribute("Replace"))
.ToList()
);
You'd then look up the Replacement.Reference in ReferenceLists to get the key/value pair list.
Something like this?
https://forums.asp.net/t/1964585.aspx?how+to+read+xml+elements+using+linq+in+c+net+recursively+
Must be improved but :
string strFilename = "/Message.xml";
strFilename = Server.MapPath(strFilename);
XmlDocument xmlDoc = new XmlDocument();
if (File.Exists(strFilename))
{
XmlTextReader rdrXml = new XmlTextReader(strFilename);
do
{
switch (rdrXml.NodeType)
{
case XmlNodeType.Text:
//Console.WriteLine("{0}", rdrXml.Value);
Response.Write(rdrXml.Value + "<br/>");
break;
}
} while (rdrXml.Read());
}
See below is a generic program that parses the xml string and recursively prints attribute name and value. I hope you can check if the name is a reference value per your requirement and go from over there..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
string xmlstring = #"<MapFile>
<Import>
<field name1=""BorrowId"" name2=""EMPLID"" />
<field name1=""Firstname"" name2=""COMPLETENAME"" />
<field name1=""Address"" name2=""Address"" Reference=""Location"" />
</Import>
<Location>
<Lookup Key=""CC"" Replace=""1"" />
<Lookup Key=""CE"" Replace=""2"" />
</Location>
</MapFile>";
XElement xmlTree = XElement.Parse(xmlstring);
ParseChildElement(xmlTree);
Console.ReadLine();
}
static void ParseChildElement(XElement xmlTree)
{
PrintAttributes(xmlTree);
foreach(XElement element in xmlTree.Elements())
{
ParseChildElement(element);
}
}
static void PrintAttributes(XElement xmlTree)
{
foreach (XAttribute attr in xmlTree.Attributes())
{
string[] attribArray = attr.ToString().Split('=');
if (attribArray.Length > 1)
Console.WriteLine(string.Format(#" {0} = {1}", attr.Name, attr.Value));
}
}
}
}

XML to String List

I have some code that I need to put into a string list in C# and I am reading this code from an XML files and the layout of it is something like below...
<?xml version="1.0"?>
<accountlist>
<main>
<account id="1" special_id="4923959">
<username>Adam</username>
<motto>Hello Everyone>
<money>1004</money>
<friends>394</friends>
<rareid>9</rareid>
<mission>10</mission>
</account>
</main>
</accountlist>
How can I put each account tag into a string list? from the first < account > to the < / account > tag?
Please do NOT tell me to go to the link below as it does NOT work!!
How to read a XML file and write into List<>?
So far I have tried the below code, and the string list just stays empty
XDocument doc = XDocument.Parse(this._accountsFile);
List<string> list = doc.Root.Elements("account")
.Select(element => element.Value)
.ToList();
this._accounts = list;
You'll have to use Descendants instead of Elements:
List<string> list = doc.Root.Descendants("account").Descendants()
.Select(element => element.Value)
.ToList();
Elements only returns child elements of the element (in case of the root element this means <main>).
Descendants returns the entire tree inside the element.
Also: You'll have to fix the tag <motto>Hello Everyone> to <motto>Hello Everyone</motto>
This will work on your example (but you need to close this tag <motto>Hello Everyone>
public List<string> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<string> result = new List<string>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(currentNode.InnerXml);
}
return result;
}
EDIT as an answer to your question:
Is there a way I can get the id and specal_id in a seperate string?
you can use currentNode.Attributes["YourAttributeName"].Value, to get the values.
assume you have class Account :
class Account
{
public string accountXml { get; set; }
public string Id { get; set; }
public string Special_id { get; set; }
}
Then :
public List<Account> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<Account> result = new List<Account>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(new Account
{
accountXml = currentNode.InnerXml,
Id = currentNode.Attributes["id"].Value,
Special_id = currentNode.Attributes["special_id"].Value,
});
}
return result;
}
Use XPath to get the account element first:
using System.Xml.XPath;
XDocument doc = XDocument.Parse(xml);
foreach(var account in doc.XPathSelectElements("accountlist/main/account")){
List<string> list = account.Descendants()
.Select(element => element.Value)
.ToList();
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication37
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\"?>" +
"<accountlist>" +
"<main>" +
"<account id=\"1\" special_id=\"4923959\">" +
"<username>Adam</username>" +
"<motto>" +
"Hello Everyone>" +
"<money>1004</money>" +
"<friends>394</friends>" +
"<rareid>9</rareid>" +
"<mission>10</mission>" +
"</motto>" +
"</account>" +
"</main>" +
"</accountlist>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("accountlist").Select(x => new {
id = x.Element("main").Element("account").Attribute("id").Value,
special_id = x.Element("main").Element("account").Attribute("special_id").Value,
username = x.Element("main").Element("account").Element("username").Value,
motto = x.Element("main").Element("account").Element("motto").FirstNode.ToString(),
money = x.Element("main").Element("account").Element("motto").Element("money").Value,
friends = x.Element("main").Element("account").Element("motto").Element("friends").Value,
rareid = x.Element("main").Element("account").Element("motto").Element("rareid").Value,
mission = x.Element("main").Element("account").Element("motto").Element("mission").Value,
}).ToList();
}
}
}

Process MusicBrainz Web Service

I know MusicBrainz has a version 2 available but v1 is the only way I got this far.
(This is 1 return, it is usually hundreds...):
XML:
<metadata xmlns="http://musicbrainz.org/ns/mmd-1.0#" xmlns:ext="http://musicbrainz.org/ns/ext-1.0#">
<release-list count="928" offset="0">
<release id="bea5602c-53bc-4416-af49-238aae51e8ea" type="Live Bootleg" ext:score="100">
<title>Seattle 1988</title>
<text-representation language="ENG" script="Latn" />
<artist id="5b11f4ce-a62d-471e-81fc-a69a8278c7da">
<name>Nirvana</name>
<sort-name>Nirvana</sort-name>
</artist>
<release-event-list>
<event format="CD" />
</release-event-list>
<disc-list count="0" />
<track-list count="10" />
</release>
</release-list>
</metadata>
I am able to get all albums returned with this :
client = new WebClient();
client.OpenReadCompleted += client_OpenReadCompleted;
// Call public web service.
string requestUri =
"http://musicbrainz.org/ws/1/release/?limit=100&type=xml&artist={0}";
client.OpenReadAsync(
new Uri(String.Format(
requestUri, "Jimi Hendrix")));
private void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
// Process returned data.
XElement results;
albums = new List<string>();
if (e.Error != null)
{ return; }
else
{
XNamespace ns =
#"http://musicbrainz.org/ns/mmd-1.0#";
results = XElement.Load(e.Result);
var q = from r in results.Descendants(ns + "release")
select new { Title = r.Element(ns + "title").Value };
foreach (var i in q)
{
albums.Add(i.Title);
Console.WriteLine(i.Title);
}
}
How can I also get the release id for each album?
As you already selected the release-node, you just need to get that id-attribute:
var q = from r in results.Descendants(ns + "release")
select new
{
Title = r.Element(ns + "title").Value,
Release = r.Attribute("id").Value
};
Here the class you can use to store your data
class Release
{
public Guid Id {get; set;}
public string Title {get; set;}
}
This is how you can retrieve the collection of Release from your respond.
XNamespace ns = "http://musicbrainz.org/ns/mmd-1.0#";
var doc = XElement.Load(e.Result);
IEnumerable<Release> res = doc.Descendants(ns + "release")
.Select(r => new Release{
Id = (Guid)r.Attribute("id"),
Title = (string)r.Element(ns + "title")
});
var q = from r in results.Descendants(ns + "release")
select new
{
Title = r.Element(ns + "title").Value,
Release = r.Attribute("id").Value
};
Works great! Thanks

XDocument to List of object

The content of an XDocument is the XML below.
I'd like to get a List(), see at the end of this message.
<myXml>
<myDatas code="01">
<myVar name="myvar" value="A" />
<myData name="A" value="A1" />
<myData name="B" value="B1" />
</myDatas>
<myDatas code="02">
<myVar name="myvar" value="B" />
<myData name="A" value="A2" />
<myData name="D" value="D2" />
</myDatas>
</myXml>
public class MyData
{
public string MainCode { get; set; }
public string Code { get; set; }
public string Value { get; set; }
}
I'd like a List() this content should be like this :
new MyData { MainCode = "01"; Code = "A"; Value = "A1" };
new MyData { MainCode = "01"; Code = "B"; Value = "B1" };
new MyData { MainCode = "02"; Code = "A"; Value = "A2" };
new MyData { MainCode = "02"; Code = "D"; Value = "D2" };
Sure - so you need something like this:
var query = from datas in doc.Root.Elements("myDatas")
let code = (string) datas.Attribute("code")
from data in datas.Elements("myData")
select new MyData {
MainCode = code,
Code = (string) data.Attribute("name"),
Value = (string) data.Attribute("value"),
};
var list = query.ToList();
Note the multiple from clauses to flatten the results.
Another alternative would have been to just find all the "leaf" elements and fetch the code part from the parent:
var query = from data in doc.Descendants("myData")
select new MyData {
MainCode = (string) data.Parent.Attribute("code"),
Code = (string) data.Attribute("name"),
Value = (string) data.Attribute("value"),
};
var list = query.ToList();
EDIT: If your document uses namespaces, that's easy too:
XNamespace ns = "http://the-uri-of-the-namespace";
var query = from data in doc.Descendants(ns + "myData")
...
This uses the XName operator +(XNamespace, string) overloaded operator.

Categories

Resources