Dynamically add named mapping to Index - c#

I would like to add mappings to an index after I've created it. I've created the index as such:
client.CreateIndex("typeaheads", c => c
.Settings(t => t.Analysis(m => m.TokenFilters(fl => fl.EdgeNGram("edge_ngram_filter", ad => ad.MinGram(2).MaxGram(20)))
.Analyzers(anz => anz.Custom("edge_ngram_analyzer", an => an.Filters("lowercase", "edge_ngram_filter").Tokenizer("standard"))))));
The variable typeName, is the name I want for the mapping.
When I execute this:
var map = new CreateIndexDescriptor("typeaheads")
.Mappings(ms => ms
.Map(typeName, d => d.Properties(ps => ps.String(s => s.Name("countryCode")))
.Properties(ps => ps.String(s => s.Name("display_ID")))
.Properties(ps => ps.String(s => s.Name("display_String")))
.Properties(ps => ps.String(s => s.Name("id")))
.Properties(ps => ps.String(s => s.Name("languageCode")))
.Properties(ps => ps.String(s => s.Name("match_String").SearchAnalyzer("standard").Index(FieldIndexOption.Analyzed).Analyzer("edge_ngram_analyzer")))
.Properties(ps => ps.String(s => s.Name("type")))
.Properties(ps => ps.Number(s => s.Name("boostFactor").Type(NumberType.Long)))));
var response = client.Index(map);
I get this output on my ES service:
Wrong Mapping
I would like to get this: Correct Mapping
Any ideas?

If you have an existing index and wish to add a mapping to it, this can be done with the Put Mapping API, exposed in NEST as client.Map<T>() and client.MapAsync<T>()
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
var client = new ElasticClient(connectionSettings);
var typeName = "my-type";
var mappingResponse = client.Map<object>(d => d
.Index("typeaheads")
.Type(typeName)
.Properties(ps => ps
.String(s => s.Name("countryCode"))
.String(s => s.Name("display_ID"))
.String(s => s.Name("display_String"))
.String(s => s.Name("id"))
.String(s => s.Name("languageCode"))
.String(s => s
.Name("match_String")
.SearchAnalyzer("standard")
.Index(FieldIndexOption.Analyzed)
.Analyzer("edge_ngram_analyzer")
)
.String(s => s.Name("type"))
.Number(s => s
.Name("boostFactor")
.Type(NumberType.Long)
)
)
);
which sends the following request
PUT http://localhost:9200/typeaheads/my-type/_mapping?pretty=true
{
"properties": {
"countryCode": {
"type": "string"
},
"display_ID": {
"type": "string"
},
"display_String": {
"type": "string"
},
"id": {
"type": "string"
},
"languageCode": {
"type": "string"
},
"match_String": {
"type": "string",
"index": "analyzed",
"analyzer": "edge_ngram_analyzer",
"search_analyzer": "standard"
},
"type": {
"type": "string"
},
"boostFactor": {
"type": "long"
}
}
}

Related

Adding a filter on a $lookup stage in Mongo using C#

I have the following Mongo shell query:
db.getCollection('users').aggregate([
{ "$match": { "Email": "user1#example.com" } },
{ "$lookup": {
"from": "groups",
"localField": "_id",
"foreignField": "Users",
"as": "userGroups"
}},
{ "$graphLookup": {
"from": "groups",
"startWith": "$userGroups._id",
"connectFromField": "Groups",
"connectToField": "_id",
"as": "ancestorGroups"
}},
{ "$unwind": "$ancestorGroups" },
{ "$group": {
"_id": "$_id",
"Email": { "$first": "$email" },
"Groups": { "$addToSet": "$ancestorGroups._id" }
}}
])
The query matches a user by their email address, and then transitively finds all the groups that the user belongs to.
I converted this query to C# like so:
var opts = new AggregateLookupOptions<GroupDto, UserDto>();
var options = new AggregateGraphLookupOptions<GroupDto, GroupDto, UserDto>()
{
};
var filter = Builders<UserDto>.Filter.Eq(x => x.Email, email);
var query = _collection.Aggregate()
.Match(filter)
.Lookup(_groups, x => x.Id, x => x.Users, x => x.UserGroups, opts)
.GraphLookup(_groups, x => x.Groups, x => x.Id, x => x.UserGroups.Select(y => y.Id), x => x.AncestorGroups, options);
This works great, however, I want to filter the joined groups collection. In Mongo shell, I can throw in a pipeline parameter in the $lookup stage like so:
{ "$lookup": {
"from": "groups",
"localField": "_id",
"foreignField": "Users",
"as": "userGroups",
"pipeline": [
{ "$match": { "LinkId": "1337" } },
],
}},
However, I'm not sure how to achieve this in C#. The only overload for the Lookup method that takes a lookupPipeline parameter takes let and BsonDocument and won't support typed arguments.
Any ideas?

Flatten Json & Ignore array index Using ChoETL

I have 1 json file and these lines of code:
Here's my code:
using (var r = new ChoJSONReader("data.json")
.Configure(c => c.ThrowAndStopOnMissingField = true)
.Configure(c => c.DefaultArrayHandling = true)
.Configure(c => c.FlattenNode = true)
.Configure(c => c.IgnoreArrayIndex = false)
.Configure(c => c.NestedKeySeparator = '.')
.Configure(c => c.NestedColumnSeparator = '.')
)
{
var dt = r.AsDataTable();
Console.WriteLine(dt.DumpAsJson());
}
My data.json file:
{
"BrandId": "998877665544332211",
"Categories": [
"112233445566778899"
],
"Contact": {
"Phone": [
{
"Value": "12346789",
"Description": {
"vi": "Phone"
},
"Type": 1
},
{
"Value": "987654321",
"Description": {
"vi": "Phone"
},
"Type": 1
}
]
}
}
After running this code, I got the output like this:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.0.Value": "12346789",
"Contact.Phone.0.Description.vi": "Phone",
"Contact.Phone.0.Type": 1,
"Contact.Phone.1.Value": "987654321",
"Contact.Phone.1.Description.vi": "Phone",
"Contact.Phone.1.Type": 1,
"Category0": "112233445566778899"
}
]
The question here is how can I get some kind of output json without "0" at the flattened key node
Expected output:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "12346789",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
},
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "987654321",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
}
]
I've research by many ways but it's results doesn't as same as my expected result.
Thanks for any kind of help
As your json is nested/complex in nature, you need to unpack and flatten into multiple simple data element rows using ChoETL/Linq as below.
ChoETLSettings.KeySeparator = '-';
using (var r = ChoJSONReader.LoadText(json)
.WithField("BrandId")
.WithField("Category", jsonPath: "Categories[0]", isArray: false)
.WithField("Phone", jsonPath: "Contact.Phone[*]")
)
{
var dt = r.SelectMany(rec => ((IList)rec.Phone).OfType<dynamic>().Select(rec1 =>
{
dynamic ret = new ChoDynamicObject();
ret["BrandId"] = rec.BrandId;
ret["Contact.Phone.Value"] = rec1.Value;
ret["Contact.Phone.Description.vi"] = rec1.Description.vi;
ret["Contact.Phone.Type"] = rec1.Type;
ret["Category"] = rec.Category;
return ret;
})).AsDataTable();
dt.DumpAsJson().Print();
}
Sample fiddle: https://dotnetfiddle.net/PHK8LO

C# Linq Group by with Dictionary

I have following data
{
"id": "0012",
"Name": "User01",
"Status": "NEW",
"Urgency": "Urgent"
}, {
"id : "0013",
"Name": "User01",
"Status": "NEW",
"Urgency": "Urgent"
}, {
"id : "0014"
"Name": "User01",
"Status": "REJECTED",
"Urgency": "Urgent"
} {
"id : "0015"
"Name": "User02",
"Status": "NEW",
"Urgency": "PastDue"
}
I am trying to get the below output using Linq GroupBy. But, not able to get the exactly as per the output.
var groupedUrgency = sampleData.GroupBy(x => new { x.Urgency });
var data = groupedUrgency.Select(x => new
{
Name = "NeedToGetTheName also",
NewItems = x.Where(z => z.Status == "NEW").ToDictionary(gdc => x.Key.Urgency, gdc => x.Count()),
RejectedItems = x.Where(z => z.Status == "REJECTED").ToDictionary(gdc => x.Key.Urgency, gdc => x.Count())
})
.ToList();
Is there any way we can the output below mentioned table. I need to get the User name and It's count for given Urgency property for each status.
You can first group by Name and then select count group by Urgency as follows:
var data = sampleData.GroupBy(x => x.Name).Select(x => new
{
Name = x.Key,
NewItems = x.Where(n => n.Status == "NEW").GroupBy(g => g.Urgency).Select(s => new { UrgentType = s.Key, Count = s.Count() }),
RejectedItems = x.Where(n => n.Status == "REJECTED").GroupBy(g => g.Urgency).Select(s => new { UrgentType = s.Key, Count = s.Count() }),
}).ToList();

How to get termvectors when using ingest plugin in ElasticSearch 5.5?

All,
I have the following code to index a file using ingest plugin in elasticsearch
public class Document
{
public string Id { get; set; }
public string Content { get; set; }
public Attachment Attachment { get; set; }
}
var indexResponse = client.CreateIndex("documents", c => c
.Settings(s => s
.Analysis(a => a
.TokenFilters(f=>f.Stemmer("english_stem",st=>st.Language("english")).Stop("english_stop",sp=>sp.StopWords("_english_")))
.CharFilters(cf => cf.PatternReplace("num_filter", nf => nf.Pattern("(\\d+)").Replacement(" ")))
.Analyzers(an => an.Custom("tm_analyzer", ta => ta.CharFilters("num_filter").Tokenizer("standard").Filters("english_stem","english_stop","lowercase")))))
.Mappings(m => m
.Map<Document>(mm => mm
.AllField(al=>al.Enabled(false))
.Properties(p => p
.Object<Attachment>(o=>o
.Name(n=>n.Attachment)
.Properties(ps=>ps
.Text(s => s
.Name(nm => nm.Content)
.TermVector(TermVectorOption.Yes)
.Store(true)
.Analyzer("tm_analyzer")))))));
client.PutPipeline("attachments", p => p
.Description("Document attachment pipeline")
.Processors(pr => pr
.Attachment<Document>(a => a
.Field(f => f.Content)
.TargetField(f => f.Attachment)
)
.Remove<Document>(r => r
.Field(f => f.Content)
)
)
);
var base64File = Convert.ToBase64String(File.ReadAllBytes("file1.xml"));
client.Index(new Document
{
Id = "file1.xml",
Content = base64File
}, i => i.Pipeline("attachments"));
As you can see i have set the termvector otpion to yes on the Content field.
But when i query like below using postman or in C# Nest i get nothing
POST /documents/document/_mtermvectors
{
"ids" : ["1.xml"],
"parameters": {
"fields": [
"content"
],
"term_statistics": true
}
}
Any ideas what I am doing wrong? Thanks for the help!
You're removing the content field in the ingest processor here
.Remove<Document>(r => r
.Field(f => f.Content)
)
This is probably what you want because it'll contain the base64 encoded attachment. I think your API call should be looking at the attachment.content field, which will contain the extracted content from the attachment
POST /documents/document/_mtermvectors
{
"ids" : ["1.xml"],
"parameters": {
"fields": [
"attachment.content"
],
"term_statistics": true
}
}

Kendo UI grid column editable false using GridColumnSettings

I have a kendo grid like this.
#(Html.Kendo().Grid(Model.GridView.DataSource)
.Name("grid").Columns(columns => columns.LoadSettings(Model.GridView.ColumnSettings))
.Editable(editable => editable.Mode(GridEditMode.InLine))
.ToolBar(toolbar => toolbar.Create().Text("Add User"))
.DataSource(dataSource => dataSource
.Ajax().ServerOperation(true)
.Model(model =>
{
model.Id(p => p.Id);
model.Field(p => p.Id).Editable(false);
})
.Read(read => read.Action("OnGridRead", "Manage"))
)
)
And I'm using Kendo GridColumnSettings to define the columns in my model which is a GridView(Model) here like below.
public class GridView
{
public List<GridColumnSettings> ColumnSettings
{
get
{
var items = new List<GridColumnSettings>
{
new GridCommandColumnSettings
{
Commands =
{
new GridEditActionCommand()
},
Width = "70px"
},
new GridColumnSettings
{
Member = "Id",
Hidden = true
},
new GridColumnSettings
{
Member = "FirstName"
},
new GridColumnSettings
{
Member = "LastName"
},
new GridColumnSettings
{
Member = "UserName"
},
new GridColumnSettings
{
Member = "Password",
ClientTemplate = "***",
}
};
return items;
}
}
}
And here I need to disable Username field only in inline edit mode of the grid.
Currently there is no property available in GridColumnSettings class as editable. How can I disable the username field in edit mode of the grid using GridColumnSettings class.
Please try with the below code snippet. I have disabled the StudentID field in below demo.
#(Html.Kendo().Grid<MvcApplication1.Models.Student>()
.Name("Grid")
//.Columns(columns =>
// {
// columns.Bound(c => c.StudentID);
// columns.Bound(c => c.StudentName);
// })
.Columns(cols => cols.LoadSettings(ViewBag.cols))
.Scrollable()
.Groupable()
.Sortable()
.Editable(editable => editable.Mode(GridEditMode.InLine)).ToolBar(toolbar => toolbar.Create())
.Pageable(pageable => pageable
.Refresh(true)
.PageSizes(true)
.ButtonCount(5))
.Events(events => events.Edit("onEdit"))
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Grid_Read", "Home"))
.Update(update => update.Action("EditingInline_Update", "Grid"))
.Create(update => update.Action("EditingInline_Create", "Grid"))
.Model(model =>
{
model.Id(p => p.StudentID);
model.Field(p => p.StudentID).Editable(true);
})
)
)
Method 1:-
<script>
function onEdit(e) {
if (e.model.isNew() == false) {
$(e.container).find("input[name='StudentID']").attr('disabled', 'disabled');
}
}
</script>
Method 2:-
<script>
function onEdit(e) {
if (e.model.isNew() == false) {
$(e.container).find("input[name='StudentID']").parent().html($(e.container).find("input[name='StudentID']").val()).removeAttr('data-container-for');
}
}
</script>
Let me know if any concern.
Please try below solution to disable the username field in edit mode of the grid.
.Model(model =>
{
model.Id(p => p.Id);
model.Field(p => p.Id).Editable(false);
model.Field(p => p.UserName).Editable(false);
})

Categories

Resources