Why don't these ElasticSearch fields appear in "Fields"? - c#

I can't figure this one out. I'm creating a new ElasticSearch index using the ElasticProperty attributes/decorators. Here's how I create the index:
client = ClientFactory(indexName);
if (!client.IndexExists(indexName).Exists)
{
var set = new IndexSettings() { NumberOfShards = 1 };
var an = new CustomAnalyzer() { Tokenizer = "standard" };
an.Filter = new List<string>();
an.Filter.Add("standard");
an.Filter.Add("lowercase");
an.Filter.Add("stop");
an.Filter.Add("asciifolding");
set.Analysis.Analyzers.Add("nospecialchars", an);
client.CreateIndex(c => c
.Index(indexName)
.InitializeUsing(set)
.AddMapping<ItemSearchable>(m => m.MapFromAttributes())
);
}
And all of my fields are being created properly from the attributes specified on the class, except these two. They are C# enumerations, which is maybe part of the problem. I'm trying to save them in the index as numeric fields...
[Required, ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, IncludeInAll = false)]
public Enums.PlatformType PlatformType { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, OmitNorms = true, IncludeInAll = false)]
public Enums.ItemType ItemType { get; set; }
When I set up the index and check via Kibana, I don't see PlatformType or ItemType at all in the list of fields in the empty index.
When I insert a record, I can see the values in the source (JSON) as numbers (as expected), but the "Fields" are not there.
So I'm thinking it's either because they're C# enum type, or because I'm trying to store it as a number. But I'm stumped on why Elasticsearch is skipping these fields. Many thanks for any ideas you may have.
UPDATE 1 ... The search still works (even without those fields being shown in the Fields section). I'm thinking it might just be a Kibana problem. In the Table view, it shows my two fields like this...
and hovering over those triangle exclamation marks says "No cache mapping for this field. Refresh your mapping from the Settings > Indices page". But of course I can't find such a page within Kibana.
So I might be fine behind the scenes and this is a non-issue. Does anyone else have any insight on what might fix this, make it clearer, or whether this is known behaviour and I should just move on? Thanks.

Related

How to use AutonumberAttribute.getNextNumber()?

When I use the AutonumberAttribute.getNextNumber(), it gives me the next number of the sequence but it also make the next number to change.
IE if I call 2 time in a row:
nextNumber = AutoNumberAttribute.GetNextNumber(ARLetteringPiece.Cache, LetteringPiece, numbering, DateTime.Now);
first time i'll get "0000001"
second time i'll get "0000002"
I want to be able to know what the next number will be without modifying it's next value.
Is there a way to achieve this ?
Thanks a lot
Edit to answer the comments :
I have a custom table, my UI key is generated with Autonumbering, and I need to put this key in the lines of my other tables to "bind" them to my custom table. So I need to know what will be the autogenerated number.
It depends on the relationship between your DACs (tables).
You can solve this by using the PXDBChildIdentity in the fields of all the tables that need to store the new key.
For example, if your DAC's autonumber field is of type integer and is called MyDAC.MyAutonumberField.
You can add the attribute to all fields in your other DACs that need to store the value like this:
[PXDBInt()]
[PXDBChildIdentity(typeof(MyDAC.myAutonumberField))]
public virtual int? MyDACID { get; set; }
If the other DACs are "children" of your custom DAC you should use the PXParent attribute in all the child DACs on the field that references their parent like this:
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(MyDAC.myAutonumberField))]
[PXParent(typeof(Select<MyDAC,
Where<MyDAC.myAutonumberField,
Equal<Current<myAutonumberField>>>>))]
public virtual int? MyParentDacID { get; set; }
I managed to do it in another way : First I save my "header", then I update the lines with the value autogenerated for my header and then I save it again.
public static void createLettering(List<ARRegister> lines)
{
// We build a new LELettering piece
Lettrage graph = CreateInstance<Lettrage>();
LELettering piece = new LELettering();
piece.Status = ListStatus._OPEN;
piece.LetteringDateTime = DateTime.Now;
piece = graph.ARLetteringPiece.Insert(piece);
// We fill the checked lines with the autonumber of the piece
bool lineUpdated = false;
foreach (ARRegister line in lines)
{
if (line.Selected.Value)
{
if (!lineUpdated)
{
piece.BranchID = line.BranchID;
piece.AccountID = line.CustomerID;
piece = graph.ARLetteringPiece.Update(piece);
graph.Actions.PressSave();
}
line.GetExtension<ARRegisterLeExt>().LettrageCD = graph.ARLetteringPiece.Current.LetteringCD;
graph.ARlines.Update(line);
lineUpdated = true;
}
}
// If there are lines in our piece, we save it
// It saves our lettering piece and our modifications on the ARLines
if (lineUpdated)
{
graph.Actions.PressSave();
}
}

Realm + Xamarin Forms Update Query

I'm using Realm + Xamarin Forms to do what I figure is about the most basic operation possible: a list view shows a collection of items, with a search bar filtering the results as the user types.
I have a get only collection property used as the list view's items source, initially populated from a Realm query, and this gets updated automatically with any changes to data, but I can't figure out how to update the search text without adding a set and literally replacing the entire collection.
This is very inefficient--I assume this is triggering re-registration of a bunch of notify-changed event listeners for the collection and each item in it and generally causing mass chaos with each letter tapped.
In the past I've created my own wrapping observable collection with a search method to handle this and I suppose that is an option here as well, but is there any way to do this with Realm? That is, to update the query without recreating the entire collection, some way to re-run the original query?
Update: This technique not longer works.
https://github.com/realm/realm-dotnet/issues/1569
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...it also differs from the behavior of LINQ to Objects, where every iteration will reevaluate expressions, meaning that changes to both sides of a condition will affect the result. A Realm query will evaluate the right-hand sides of the conditions on the first run.
When you construct a query that contains Where parameters that are based upon non-Realm based conditions the query results do not update when those variable/parameters are changed unless you update/execute the query again.
Realm queries are live, in the sense that they will continue to represent the current state of the database.
So what I do is to create a filter class (RealmObject), then if you instance a "filter object", save it to Realm, you can base your Linq's Where parameters upon one or more of the "filter" properties. Updating this RealmObject filter via Realm.Add(filterObject, true) your queries based upon that object are also updated
Realm queries are live, in the sense that they will continue to represent the current state of the database.
The results are lighting fast filtering that works great in any UI Search routine.
Example Model:
public class ARealmClass : RealmObject
{
public int Key { get; set; }
public string KeyString { get; set; }
}
public class ARealmClassFilter : RealmObject
{
[PrimaryKey]
public int Key { get; set; }
public int FilterKeyBy { get; set; }
}
Populate a Realm with some test data
var realm = Realm.GetInstance();
var all = realm.All<ARealmClass>();
if (all.Count() == 0)
{
realm.Write(() =>
{
for (int i = 0; i < 1000; i++)
{
var obj = new ARealmClass { Key = i, KeyString = i.ToString() };
realm.Add(obj);
}
});
}
Dynamic Live Query Example:
var realm = Realm.GetInstance();
var all = realm.All<ARealmClass>();
Console.WriteLine(all.Count());
var filterItem = new ARealmClassFilter { Key = 1, FilterKeyBy = 500 };
realm.Write(() =>
{
realm.Add(filterItem);
});
var filtered = all.Where(_ => _.Key > filterItem.FilterKeyBy);
Console.WriteLine(filtered.Count());
realm.Write(() =>
{
filterItem.FilterKeyBy = 750;
realm.Add(filterItem, true);
});
Console.WriteLine(filtered.Count());
Output:
2017-04-24 11:53:20.376 ios_build_foo[24496:3239020] 1000
2017-04-24 11:53:20.423 ios_build_foo[24496:3239020] 499
2017-04-24 11:53:20.425 ios_build_foo[24496:3239020] 249
Note: Quoted text # https://realm.io/docs/xamarin/latest/api/linqsupport.html

MS Dynamics: retrieve only attributes with changed metadata

I'm trying to retrieve only the attributes for a certain entity, that have changed metadata since the last metadataquery - for example: if a user changes the requirement on a certain field of a certain entity, and saves and publishes this change, I want a plugin that fires on message Publish & PublishAll to let me know what attribute and which metadata of that attribute has changed.
This is the code I have so far, based on this example on MSDN: https://msdn.microsoft.com/en-us/library/jj863605.aspx?f=255&MSPPError=-2147217396
I get the attributes for the three entities that are listed in includedEntities, so no problem there.
I get values for RequiredLevel and IsValidForAdvancedSearch, the two attribute properties listed in attributeProperties and the ones I want to watch while the rest returns null, so again, no problem here.
The attributeFilter also does what it's supposed to do: I only get datafields (attributes that do not describe another attribute), so once again: no problem.
The clientversionstamp I pass on is retrieved from a configurationparameter I created, which I update after every query. By watching it during debug, I know that it's the correct value - so I'm quite sure that's not the problem either.
So what is the problem? For each entity, I still have some (about half) of the attributes that are added to the collection of changed attributes in the response, although I didn't change anything.
If I do change something in metadata, that attribute does get added to the response collection as well, so my code does pick up the change. However, I still get a lot more data than I want - the goal is to only get that one attribute that has changed. What am I missing?
MetadataFilterExpression EntityFilter = new MetadataFilterExpression(LogicalOperator.And);
EntityFilter.Conditions.Add(new MetadataConditionExpression("LogicalName", MetadataConditionOperator.In, includedEntities));
MetadataPropertiesExpression EntityProperties = new MetadataPropertiesExpression()
{
AllProperties = false
};
EntityProperties.PropertyNames.AddRange(new string[] { "Attributes" });
MetadataConditionExpression optionsetAttributeName = new MetadataConditionExpression("AttributeOf", MetadataConditionOperator.Equals, null);
MetadataFilterExpression AttributeFilter = new MetadataFilterExpression(LogicalOperator.And);
AttributeFilter.Conditions.Add(optionsetAttributeName);
MetadataPropertiesExpression AttributeProperties = new MetadataPropertiesExpression() { AllProperties = false };
foreach (string attrProperty in attributeProperties)
{
AttributeProperties.PropertyNames.Add(attrProperty);
}
EntityQueryExpression entityQueryExpression = new EntityQueryExpression()
{
Criteria = EntityFilter,
Properties = EntityProperties,
AttributeQuery = new AttributeQueryExpression()
{
Properties = AttributeProperties,
Criteria = AttributeFilter
}
};
RetrieveMetadataChangesRequest req = new RetrieveMetadataChangesRequest()
{
Query = entityQueryExpression,
ClientVersionStamp = clientVersionStamp
};
return (RetrieveMetadataChangesResponse)service.Execute(req);

Acumatica Dynamic MultiSelect Dropdown

I have a screen entry to store transaction data, I want to use dynamic with multiselect combobox to select status and status data is taken from the table, but when some of the data status is selected, the amount of stored data does not match that has been selected,
I have tried the following code, but it's doesn't work for me.
public class StatusMultiStringListAttribute : PXStringListAttribute
{
public StatusMultiStringListAttribute() : base()
{
PXResultset<StatusTable> rslt = PXSelect<StatusTable>.Select(new PXGraph());
List<string> values = new List<string>();
List<string> labels = new List<string>();
foreach (PXResult<StatusTable> item in rslt)
{
BSMTStatus e = (StatusTable)item;
values.Add(e.StatusID);
labels.Add(e.Description);
}
this._AllowedValues = values.ToArray();
this._AllowedLabels = labels.ToArray();
MultiSelect = true;
}
}
is there any other solution, sorry my English is bad, thanks.
I noticed your comment on http://asiablog.acumatica.com/2016/03/multiselect-combo-box.html and saw that you posted some additional code. Based on your sample code, I identified two problems:
First of all, the values you're loading from the StatusTable DAC contain trailing spaces which are not trimmed. You haven't provided the declaration of the StatusTable DAC, but it's safe to assume from your screenshot that this field has the IsFixed attribute set to true. With these settings, the system will add white space at the end of your value. To save space in the target field, I would recommend to add a Trim() to the constructor code:
foreach (PXResult<StatusTable> item in rslt)
{
BSMTStatus e = (StatusTable)item;
values.Add(e.StatusID.Trim()); //Remove any white-space
labels.Add(e.Description);
}
Second, the status field where you're storing the selected values is not long enough to accommodate multiple selections. It's currently defined as 20 characters ([PXDBString(20, IsFixed=true)]), and even assuming you remove the whitespace you would still be limited to 4 choices. I suggest you to change it to 255, and to also remove IsFixed=true since it's not needed for this field:
[PXDBString(255)]
[PXDefault]
[PXUIField(DisplayName = "Status")]
[StatusStringList]
public virtual string Status

LINQ to SharePoint 2010 getting error "All new entities within an object graph must be added/attached before changes are submitted."

I've been having a problem for some time, and I've exhausted all means of figuring this out for myself.
I have 2 lists in a MS Sharepoint 2010 environment that are holding personal physician data for a medical group...nothing special just mainly text fields and a few lookup choice fields.
I am trying to write a program that will migrate the data over from List A to List B. I am using LINQ to Sharepoint to accomplish this. Everything compiles just fine, but when it runs and hits the SubmitChanges() method, I get a runtime error that states:
"All new entities within an object graph must be added/attached before changes are submitted."
this issue must be outside of my realm of C# knowledge because I simply cannot find the solution for it. The problem is DEFINITELY stemming from the fact that some of the columns are of type "Lookup", because when I create a new "Physician" entity in my LINQ query, if I comment out the fields that deal with the lookup columns, everything runs perfectly.
With the lookup columns included, if I debug and hit breakpoints before the SubmitChanges() method, I can look at the new "Physician" entities created from the old list and the fields, including data from the lookup columns, looks good, the data is in there the way I want it to be, it just flakes out whenever it tries to actually update the new list with the new entities.
I have tried several methods of working around this error, all to no avail. In particular, I have tried created a brand new EntityList list and calling the Attach() method after each new "Physician" Entity is created, but to no avail, it just sends me around in a bunch of circles, chasing other errors such as "ID cannot be null", "Cannot insert entities that have been deleted" etc.,
I am no farther now than when I first got this error and any help that anyone can offer would certainly be appreciated.
Here is my code:
using (ProviderDataContext ctx = new ProviderDataContext("http://dev"))
{
SPSite sitecollection = new SPSite("http://dev");
SPWeb web = sitecollection.OpenWeb();
SPList theOldList = web.Lists.TryGetList("OldList_Physicians");
//Create new Physician entities.
foreach(SPListItem l in theOldList.Items)
{
PhysiciansItem p = new PhysiciansItem()
{
FirstName = (String)l["First Name"],
Title = (String)l["Last Name"],
MiddleInitial = (String)l["Middle Init"],
ProviderNumber = Convert.ToInt32(l["Provider No"]),
Gender = ConvertGender(l),
UndergraduateSchool =(String)l["UG_School"],
MedicalSchool = (String)l["Med_School"],
Residency = (String)l["Residency"],
Fellowship = (String)l["Fellowship"],
Internship = (String)l["Internship"],
PhysicianType = ConvertToPhysiciantype(l),
Specialty = ConvertSpecialties(l),
InsurancesAccepted = ConvertInsurance(l),
};
ctx.Physicians.InsertOnSubmit(p);
}
ctx.SubmitChanges(); //this is where it flakes out
}
}
//Theses are conversion functions that I wrote to convert the data from the old list to the new lookup columns.
private Gender ConvertGender(SPListItem l)
{
Gender g = new Gender();
if ((String)l["Sex"] == "M")
{
g = Gender.M;
}
else g = Gender.F;
return g;
}
//Process and convert the 'Physician Type', namely the distinction between MD (Medical Doctor) and
//DO (Doctor of Osteopathic Medicine). State Regualtions require this information to be attached
//to a physician's profile.
private ProviderTypesItem ConvertToPhysiciantype(SPListItem l)
{
ProviderTypesItem p = new ProviderTypesItem();
p.Title = (String)l["Provider_Title:Title"];
p.Intials = (String)l["Provider_Title"];
return p;
}
//Process and convert current Specialty and SubSpecialty data into the single multi-choice lookup column
private EntitySet<Item> ConvertSpecialties(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
Item i = new Item();
i.Title = (String)l["Provider Specialty"];
theEntityList.Add(i);
if ((String)l["Provider SubSpecialty"] != null)
{
Item theSubSpecialty = new Item();
theSubSpecialty.Title = (String)l["Provider SubSpecialty"];
theEntityList.Add(theSubSpecialty);
}
return theEntityList;
}
//Process and add insurance accepted.
//Note this is a conversion from 3 boolean columns in the SP Environment to a multi-select enabled checkbox
//list.
private EntitySet<Item> ConvertInsurance(SPListItem l)
{
EntitySet<Item> theEntityList = new EntitySet<Item>();
if ((bool)l["TennCare"] == true)
{
Item TenncareItem = new Item();
TenncareItem.Title = "TennCare";
theEntityList.Add(TenncareItem);
}
if ((bool)l["Medicare"] == true)
{
Item MedicareItem = new Item();
MedicareItem.Title = "Medicare";
theEntityList.Add(MedicareItem);
}
if ((bool)l["Commercial"] == true)
{
Item CommercialItem = new Item();
CommercialItem.Title = "Commercial";
theEntityList.Add(CommercialItem);
}
return theEntityList;
}
}
So this may not be the answer you're looking for, but it's what's worked for me in the past. I've found that updating lookup fields using Linq to Sharepoint to be quite frustrating. It frequently doesn't work, or doesn't work efficiently (forcing me to query an item by ID just to set the lookup value).
You can set up the entity so that it has an int property for the lookup id (for each lookup field) and a string property for the lookup value. If, when you generate the entities using SPMetal, you don't generate the list that is being looked up then it will do this on it's own. What I like to do is (using your entity as an example)
Generate the entity for just that one list (Physicians) in some temporary folder
Pull out the properties for lookup id & value (there will also be private backing fields that need to come along for the ride too) for each of the lookups (or the ones that I'm interested in)
Create a partial class file for Physicians in my actual project file, so that regenerating the entire SPMetal file normally (without restricting to just that list) doesn't overwrite changes
Paste the lookup id & value properties in this partial Physicians class.
Now you will have 3 properties for each lookup field. For example, for PhysicianType there will be:
PhysicianType, which is the one that is currently there. This is great when querying data, as you can perform joins and such very easily.
PhysicianTypeId which can be occasionally useful for queries if you only need ID as it makes it a bit simpler, but mostly I use it whenever setting the value. To set a lookup field you only need to set the ID. This is easy, and has a good track record of actually working (correctly) in my experiences.
PhysicianTypeValue which could be useful when performing queries if you just need the lookup value, as a string (meaning it will be the raw value, rather than something which is already parsed if it's a multivalued field, or a user field, etc. Sometimes I'd rather parse it myself, or maybe just see what the underlying value is when doing development. Even if you don't use it and use the first property, I often bring it along for the ride since I'm already doing most of the work to bring the PhysicianTypeId field over.
It seems a bit hacky, and contrary to the general design of linq-to-SharePoint. I agree, but it also has the advantage of actually working, and not actually being all that hard (once you get the rhythm of it down and learn what exactly needs to be copied over to move the properties from one file to another).

Categories