I have nested documents such as;
public sealed class CampaignIndexModel : ElasticEntity<Guid>
{
public Guid StoreId { get; set; }
public string Slug { get; set; }
public string SlugKey { get; set; }
public string Title { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public string Condition { get; set; }
public string PreviewImageUrl { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public bool IsPublished { get; set; }
public DateTime CreatedOnUtc { get; set; }
[Nested]
public List<BadgeIndexModel> Badges { get; set; }
}
public class BadgeIndexModel
{
public string Code { get; set; }
public string Name { get; set; }
}
I'd like to query in nested object with multiple values. For example, I need to query which included Code property which are "AD", "NEW". All documents must have badge and their code properties must be "AD" and "NEW". The code properties can be dynamically. Actually I'd like to search list of string in the nested object's code property.
Note that the classes are auto-mapped while creating indexes.
I hope the question is clear, understandable.
Thank you.
UPDATE
As far as I researched Elasticsearch documentations, as below, the query result returns match exactly given badges codes.
q.Bool(b=>b
.Must(x=>x.
Nested(n=>n
.Path(p=>p.Badges)
.Query(qq=>qq
.Term(t=>t
.Field(f=>f.Badges.First().Code.Suffix("keyword"))
.Value(badge))))))
Then, the answer which is marked correct, returns documents which contains badge codes
I know it has been a little while you posted the question. But here you go -- You could do this by creating a Nested Query within which you could filter upon your list and pass this to your search method. Below method shows how this can be done. This takes the list of strings that you want to use as values for codes.
private static QueryContainer BuildNestedQuery(List<string> badgeCodes)
{
// badgeCodes is your list of strings that you want to filter on
return new QueryContainerDescriptor<CampaignIndexModel>()
.Nested(n =>
n.Path(c => c.Badges)
.Query(q => q
.Terms(t => t
.Field(f => f.Badges.FirstOrDefault().Code)
.Terms(badgeCodes.ToArray())
)
)
)
}
This QueryContainer can further be passed to the Search method of the NEST client like something shown below. However, please bear in mind that there could be slight changes in the way you trigger the client's search method depending on how you're doing it, but hooking it to the search method remains more or less the same as shown below.
// replace T with type of your choice
// client is a reference to NEST client
var result = client.Search<T>(
.From(0)
.Size(20)
.Query(q => BuildNestedQuery(badgeCodesList))
// other methods that you want to chain go here
)
This question already has answers here:
How to auto-generate a C# class file from a JSON string [closed]
(3 answers)
Closed 3 years ago.
I am attempting to deserialize a Json object that was returned from a web API with the structure as follows.
Here is my class structure in which the object is to be Deserialized into...
public class CandidateJson
{
public string response { get; set; }
//public string result { get; set; }
public Result result { get; set; }
public string Candidates { get; set; }
public List<row> rows { get; set; }
}
public class Result
{
public string result { get; set; }
public string uri { get; set; }
}
public class row
{
public string no { get; set; }
public List<FL> FL { get; set; }
}
public class FL
{
public string val { get; set; }
public string content { get; set; }
}
I am using the following line of code to Deserialized with no success....
var json = JsonConvert.DeserializeObject<CandidateJson>(JsonResult);
Upon execution of this line of code, I am prompted with the following error...
Unexpected character encountered while parsing value: {. Path 'response', line 1, position 13.
I would appreciate any assistance with this issue.
Please let me know if any additional information is needed.
Here is the raw JSON string:
{"response":{"result":{"Candidates":{"row":[
{"no":"1","FL":[{"val":"CANDIDATEID","content":"508304000012555617"},{"val":"Candidate ID","content":"ZR_129661_CAND"},{"val":"First Name","content":"PRODUCTION"},{"val":"Last Name","content":"TEST"},{"val":"Email","content":"patricia.conley#ampcorporate.com"},{"val":"Phone","content":"815-543-2109"},{"val":"Mobile","content":"815-555-5555"},{"val":"Street","content":"555 Test Ave"},{"val":"City","content":"DeKalb"},{"val":"State","content":"IL"},{"val":"Zip Code","content":"60115"},{"val":"SMCREATORID","content":"508304000000490014"},{"val":"Created By","content":"AMP Support IT Team"},{"val":"MODIFIEDBY","content":"508304000000227003"},{"val":"Modified By","content":"Nikki Bowman"},{"val":"Created Time","content":"2019-12-17 08:38:25"},{"val":"Updated On","content":"2019-12-20 15:23:10"},{"val":"Last Activity Time","content":"2019-12-20 15:23:10"},{"val":"SMOWNERID","content":"508304000000490014"},{"val":"Candidate Owner","content":"AMP Support IT Team"},{"val":"Source","content":"Non-Employee Referral"},{"val":"Email Opt Out","content":"false"},{"val":"Is Locked","content":"false"},{"val":"Is Unqualified","content":"false"},{"val":"Is Attachment Present","content":"false"},{"val":"Candidate Status","content":"Sales Training Scheduled"},{"val":"Career Page Invite Status","content":"0"},{"val":"Extension","content":"5555"},{"val":"Sales Training Date_ID","content":"508304000011808848"},{"val":"Sales Training Date","content":"2019-12-11 Digital Sales Training"},{"val":"Start Date","content":"2019-12-17"},{"val":"Candidate Job Category","content":"Print + Digital Outside"},{"val":"District Sales Manager","content":"Luke Wasowski"},{"val":"College Graduate","content":"false"},{"val":"Recruiter Initials","content":"NKB"},{"val":"Unit/Apt/Ste","content":"Apt 5"},{"val":"Hourly Rate","content":"5.00"},{"val":"Work State","content":"Illinois"},{"val":"Full Time/Part Time","content":"FTR"},{"val":"Work Email Address","content":"Nikki.Bowman#ampcorporate.com"},{"val":"EEO Class","content":"1.1"}]},
{"no":"2","FL":[{"val":"CANDIDATEID","content":"508304000011834365"},{"val":"Candidate ID","content":"ZR_125018_CAND"},{"val":"First Name","content":"Jennifer"},{"val":"Last Name","content":"Pedersen"},{"val":"Email","content":"jennyped248_hwo#indeedemail.com"},{"val":"Mobile","content":"+18157517187"},{"val":"City","content":"Genoa"},{"val":"State","content":"IL"},{"val":"Zip Code","content":"60135"},{"val":"Country","content":"United States"},{"val":"Experience in Years","content":"8"},{"val":"Current Employer","content":"WALMART"},{"val":"Current Job Title","content":"MOD TEAM MEMBER"},{"val":"Skill Set","content":"quick and exceptional customer experience, Helping and Advising Customers, Basic Word Processing, Communication Skills, Customer Service, Data Entry, Hard-Working, Intermediate Word Processing, Organisational Skills, Teamwork, Time Management, outstanding communication skills, Microsoft Word, Microsoft Excel, Microsoft Excel 2000, Microsoft Office, Microsoft Outlook, Microsoft PowerPoint, basic scheduling"},{"val":"SMCREATORID","content":"508304000000562001"},{"val":"Created By","content":"Matt Chenoweth"},{"val":"MODIFIEDBY","content":"508304000008810064"},{"val":"Modified By","content":"HR Department"},{"val":"Created Time","content":"2019-12-02 12:25:53"},{"val":"Updated On","content":"2019-12-12 09:04:51"},{"val":"Last Activity Time","content":"2019-12-12 09:04:51"},{"val":"SMOWNERID","content":"508304000000562001"},{"val":"Candidate Owner","content":"Matt Chenoweth"},{"val":"Source","content":"Indeed Resume"},{"val":"Email Opt Out","content":"false"},{"val":"Is Locked","content":"false"},{"val":"Is Unqualified","content":"false"},{"val":"Is Attachment Present","content":"true"},{"val":"Candidate Status","content":"Hired - AMP Office"},{"val":"Career Page Invite Status","content":"0"},{"val":"Source By","content":"Applied by Candidate"},{"val":"EMPID","content":"JFP147"},{"val":"Candidate Job Category","content":"Office - Digital Verification"},{"val":"College Graduate","content":"false"}]
}]}}
,"uri":"/recruit/private/json/Candidates/searchRecords"}}
I haven't tested it, but by the looks of it, your code should look like:
public class CandidateJson
{
public Response response { get; set; }
}
public class Response
{
public Result result { get; set; }
public string uri { get; set; }
}
public class Result
{
public Candidate Candidates { get; set; }
}
public class Candidate
{
public List<Row> row { get; set; }
}
public class Row
{
public string no { get; set; }
public List<FL> FL { get; set; }
}
public class FL
{
public string val { get; set; }
public string content { get; set; }
}
Note: You might want to use int or decimal instead of string for val and no, but there is not enough information for me to assert that.
I followed the steps described on this tutorial. My case is a little bit different:
Instead of indexing Hotels and Rooms, I am indexing Candidates and Resumes.
Instead of using CosmosDB I am using an Azure SQL Database.
Following the tutorial, I am able to create the Index, the 2 Indexers (one for the SQL DB and one for the Blobs storage), and the 2 data sources.
The SQL DB contains all my candidates, and the storage contains all their resumes (files with PDF/DOC/DOCX formats). Each blob has a metadata "ResumeCandidateId" that contains the same value as the "CandidateId" for the Candidate.
I have the following fields for my Index:
[SerializePropertyNamesAsCamelCase]
public partial class Candidate
{
[Key]
[IsFilterable, IsRetrievable(true), IsSearchable]
public string CandidateId { get; set; }
[IsFilterable, IsRetrievable(true), IsSearchable, IsSortable]
public string LastName { get; set; }
[IsFilterable, IsRetrievable(true), IsSearchable, IsSortable]
public string FirstName { get; set; }
[IsFilterable, IsRetrievable(true), IsSearchable, IsSortable]
public string Notes { get; set; }
public ResumeBlob[] ResumeBlobs { get; set; }
}
[SerializePropertyNamesAsCamelCase]
public class ResumeBlob
{
[IsRetrievable(true), IsSearchable]
[Analyzer(AnalyzerName.AsString.StandardLucene)]
public string content { get; set; }
[IsRetrievable(true)]
public string metadata_storage_content_type { get; set; }
public long metadata_storage_size { get; set; }
public DateTime metadata_storage_last_modified { get; set; }
public string metadata_storage_name { get; set; }
[Key]
[IsRetrievable(true)]
public string metadata_storage_path { get; set; }
[IsRetrievable(true)]
public string metadata_content_type { get; set; }
public string metadata_author { get; set; }
public DateTime metadata_creation_date { get; set; }
public DateTime metadata_last_modified { get; set; }
public string ResumeCandidateId { get; set; }
}
As you can see, one Candidate can have multiple Resumes. The challenge is to populate the ResumeBlobs property...
The data from the SQL DB is indexed and mapped correctly by the Indexer. When I run the Blobs Indexer, it loads documents, however it does not map them and they never show up in the search (ResumeBlobs is always empty). Here is the code used to create the Blobs Indexer:
var blobDataSource = DataSource.AzureBlobStorage(
name: "azure-blob-test02",
storageConnectionString: "DefaultEndpointsProtocol=https;AccountName=yyy;AccountKey=xxx;EndpointSuffix=core.windows.net",
containerName: "2019");
await searchService.DataSources.CreateOrUpdateAsync(blobDataSource);
List<FieldMapping> map = new List<FieldMapping> {
new FieldMapping("ResumeCandidateId", "CandidateId")
};
Indexer blobIndexer = new Indexer(
name: "hotel-rooms-blobs-indexer",
dataSourceName: blobDataSource.Name,
targetIndexName: indexName,
fieldMappings: map,
//parameters: new IndexingParameters().SetBlobExtractionMode(BlobExtractionMode.ContentAndMetadata).IndexFileNameExtensions(".DOC", ".DOCX", ".PDF", ".HTML", ".HTM"),
schedule: new IndexingSchedule(TimeSpan.FromDays(1)));
bool exists = await searchService.Indexers.ExistsAsync(blobIndexer.Name);
if (exists)
{
await searchService.Indexers.ResetAsync(blobIndexer.Name);
}
await searchService.Indexers.CreateOrUpdateAsync(blobIndexer);
try
{
await searchService.Indexers.RunAsync(blobIndexer.Name);
}
catch (CloudException e) when (e.Response.StatusCode == (HttpStatusCode)429)
{
Console.WriteLine("Failed to run indexer: {0}", e.Response.Content);
}
I commented the parameters for the blobIndexer but I get the same results even if it's not commented.
When I run a search, here is an example of what I get:
{
"#odata.context": "https://yyy.search.windows.net/indexes('index-test01')/$metadata#docs(*)",
"value": [
{
"#search.score": 1.2127206,
"candidateId": "363933d1-7e81-4ed2-b82e-d7496d98db50",
"lastName": "LAMLAST",
"firstName": "ZFIRST",
"notes": "MGA ; SQL ; T-SQL",
"resumeBlobs": []
}
]
}
"resumeBlobs" is empty. Any idea how to do such a mapping?
AFAIK, Azure Search doesn't support a collection merge feature that seems to be necessary to implement your scenario.
An alternative approach to this is to create a separate index for resumes and point the resume indexer to that index. That means that some of your search scenarios will have to hit two indexes, but it's a path forward.
I have a list created from a stored procedure using EF6.0
I have also created 3 classes
public class Resas
{
public string todo{ get; set; }
public string prop { get; set; }
public string Code { get; set; }
public string statusCode { get; set; }
public string checkin { get; set; }
public string checkout { get; set; }
public List<profiles> profiles { get; set; }
}
public class profiles
{
public string action { get; set; }
public string id { get; set; }
public string profileType { get; set; }
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public List<emailAddresses> emailAdresses { get; set; }
}
public class emailAddresses
{
public string emailAddress { get; set; }
public string emailAddress2 { get; set; }
}
I am doing a for-loop in the list and I need to get certain columns and put it in the array (I will put two, to keep it simple)
myEntities db = new myEntities();
List<rev_Result> revList = new List<rev_Result>();
revList.Clear();
revList = db.rev().ToList();
for (int i = 0; i < revList.Count(); i++)
{
Resas resas = new Resas();
profiles[] profiles = new profiles[1];
resas.todo = revList[i].todo;
resas.profiles[0].lastName = revList[i].lastName;
}
I am not familiar with C# as you can see from the psedo-code above.
I cannot figure out how to feed the Resas with data and then its Profile with data and then move to the next Resas entry.
Any help appreciated.
That's fairly simple using Linq:
Resas resas = new Resas();
resas.profiles = revList
.Select(x => new profiles() { action = x.todo, lastName = x.lastName })
.ToList();
What's happening here is: You loop through every entry in revList and get your wanted data structure (that's what Select is doing). x refers to the current entry in the loop, while the stuff to the right side of the arrow is you 'output': a new instance of your profiles class with the members assigned accordingly. The result of all of this is then converted to a list (before ToList(), think of it as a recipe to create the list) and assigned to resas.profiles.
By the way, a word on conventions: Usually, in C#, you would give your classes a name that starts with a capital letter. Also, your profiles class seems to contain data of exactly one profile, so a better name might be Profile. This also makes your data structure more clear, since List<profiles> seems to be a list of lists of profiles - but that's not what it actually is, is it?
Furthermore, Members generally start with a capital letter as well, so instead of action, lastName, you'd have: Action and LastName.
You can try with Linq. This is the code that should solve your issue, but Resas class doesn't have action property:
List<Resas> ls = revList.Select(x => new Resas() {
action = x.todo,
profiles = new List<profiles>() {
new profiles { lastName = x.lastName }
}
).ToList();
If you need to use action property of inprofiles` class:
List<Resas> ls = revList.Select(x => new Resas() {
profiles = new List<profiles>() {
new profiles {
action = x.todo,
lastName = x.lastName
}
}
).ToList();
UPDATE 1
i try to implement and but when i hover over my topic and i see the TopicId and TopicName are null and i see the data in myJSON string.
what else i have to do? what i am missing?
Topic topic = new Topic();
MemoryStream stream1 = new MemoryStream(Encoding.Unicode.GetBytes(myJSON));
//stream1.Position = 0;
DataContractJsonSerializer serialize = new DataContractJsonSerializer(typeof(Topic));
//topic = (Topic)serialize.ReadObject(stream1);
Topic p2 = (Topic)serialize.ReadObject(stream1);
stream1.Close(); //later i will use in `using statement`
stream1.Dispose();
PS: i just have only Topic class is that enough or do i have to create all the classes that jcolebrand showed below?
i have created a class called Topic and in it i have two prop
[DataContract]
public class Topic
{
[DataMember]
public string TopicId { get; set; }
[DataMember]
public string TopicName { get; set; }
}
UPDATE 1 END
I am working on a requirement that returns JSON data and I need a way to parse the data and load that data into a dropdownlist and I'm looking for the element in JSON called TopicName
after the TopicName is extracted I will load that data into a DropDownList asp.net control
(not using JQuery or JavaScript)
here is JSON data:
[{"NS":{"Count":1},
"Source":{"Acronym":"ABC","Name":"Name"},
"Item":[{"Id":"12312",
"Url":"http://sitename",
"ContentItem":[{"NS":{"Count":1},
"SourceUrl":"sitename",
"ContentType":"text/xml",
"PersistentUrl":"sitename",
"Title":"MY TITLE",
"SelectionSpec":{"ClassList":"","ElementList":"","XPath":null},
"Language":{"Value":"eng","Scheme":"ISO 639-2"},
"Source":{"Acronym":"ABC","Name":"Name","Id":null},
"Topics":[{"Scheme":"ABC",
"Topic":[{"TopicId":"6544","TopicName":"TOPIC NAME1"},
{"TopicId":"63453","TopicName":"TOPIC NAME2"},
{"TopicId":"2343","TopicName":"TOPIC NAME3"},
{"TopicId":"2342","TopicName":"TOPIC NAME4"}]
}],
"ContentBody":null
}]
}]
},
[{"NS":{"Count":1},"Source":{"Acronym":"ABC1","Name":"Name1"},"Item":[{"Id":"123121","Url":"http://sitename1","ContentItem":[{"NS":{"Count":1},"SourceUrl":"sitename","ContentType":"text/xml","PersistentUrl":"sitename1","Title":"MY TITLE1","SelectionSpec":{"ClassList":"","ElementList":"","XPath":null},"Language":{"Value":"eng","Scheme":"ISO 639-2"},
"Source":{"Acronym":"ABC1","Name":"Name1","Id":null},"Topics":[{"Scheme":"ABC1","Topic":[{"TopicId":"65441","TopicName":"TOPIC NAME11"},{"TopicId":"634531","TopicName":"TOPIC NAME21"},{"TopicId":"23431","TopicName":"TOPIC NAME31"},{"TopicId":"23421","TopicName":"TOPIC NAME41"}]}],"ContentBody":null}]}]},
Assuming the re-indent as applied above is correct, then you have the following classes (apparently)
public class OuterWrapper {
public NS NS { get; set; }
public Source Source { get; set; }
public ContentItemWrapper[] Item { get; set; }
}
public class ContentItemWrapper {
public int Id { get; set; }
public string Url { get; set; }
public ContentItem[] ContentItem { get; set; }
}
public class ContentItem {
public NS NS { get; set; }
public SourceUrl { get; set; }
// I'm gonna skip a bunch of fields, you get the idea
public Topics Topic { get; set; }
}
public class Topics {
public string Scheme { get; set; }
public Topic[] Topic { get; set; }
}
public class Topic {
public string TopicId { get; set; }
public string TopicName { get; set; }
}
And what you do is you use that set of type declarations (specifically the OuterWrapper) to DataContractJsonSerializer decode the JSON into a C# object that you can then query using strongly typed methods, etc. This is one of those times where C# doesn't have anywhere near the flexibility of Javascript, because everything has to be explicitly declared.
Try using built in serializer for JSON - http://msdn.microsoft.com/en-us/library/bb412179.aspx : new DataContractJsonSerializer(typeof(Person)).ReadObject(stream1);.
If it is not enough to read your objects consider using JSON.Net ( http://json.codeplex.com/) - JsonConvert.DeserializeObject<Labels>(json);