Using CreateDocumentQuery to Azure DocumentDB to pass both field and value to query a collection - c#-4.0

Assume I have a collection, with many documents, and I want to query that collection , where a field has a particular value. However, I do NOT know ahead of time which field we will be querying.
Here is an example of what I am trying to accomplish:
Document azureDocuments = client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(sDatabaseName, sCollectionCollection)).Where(f => f.MyField=MyValue).AsEnumerable().FirstOrDefault();
Has anyone done this successfully ?

Using CreateDocumentQuery to Azure DocumentDB to pass both field and value to query a collection
You could try to dynamically generate a predicate base on the fieldname and fieldvalue that user provides, and do filter based on the predicate. The following sample code is for your reference.
private async Task QueryDocs(string fieldname, string value)
{
Func<MyDoc, bool> criteria = d => d.id == d.id;
switch (fieldname)
{
case "id":
criteria = d => d.id == value;
break;
case "name":
criteria = d => d.name == value;
break;
//other cases...
}
string EndpointUri = "https://mydocumentdbtest.documents.azure.com:443/";
string PrimaryKey = "{primary key}";
DocumentClient client;
client = new DocumentClient(new Uri(EndpointUri), PrimaryKey);
var query = client.CreateDocumentQuery<MyDoc>(UriFactory.CreateDocumentCollectionUri("{databaseid}", "{collectionid}")).Where(criteria);
List<MyDoc> list = query.ToList();
}
public class MyDoc
{
public string id { get; set; }
public string name { get; set; }
}

Related

Is there a way to iterate through the fields in a row of a PXResultSet?

Is it possible to use a foreach loop in a BLC to iterate through the fields of a PXResultSet to get the FieldNames?
Is this doable? I can't seem to find a good way.
Thanks...
The PXResultset records are selected from a view. You can get the field names from the View.
Here's a full example:
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
// Get field list from data view
var dataView = new PXSelect<SOOrder>(Base);
string fieldNames = string.Join(",", GetFieldNames(dataView.View, Base.Caches));
// You don't need result set to get field names
PXResultset<SOOrder> resultSet = dataView.Select();
throw new PXException(fieldNames);
}
public string[] GetFieldNames(PXView view, PXCacheCollection caches)
{
var list = new List<string>();
var set = new HashSet<string>();
foreach (Type t in view.GetItemTypes())
{
if (list.Count == 0)
{
list.AddRange(caches[t].Fields);
set.AddRange(list);
}
else
{
foreach (string field in caches[t].Fields)
{
string s = String.Format("{0}__{1}", t.Name, field);
if (set.Add(s))
{
list.Add(s);
}
}
}
}
return list.ToArray();
}
}
When run, this example will show the fields names used in the data view in Sales Order screen SO301000 as an exception.
Field names are contained in Cache object. If you really need to get field names from PXResultset you need to iterate the cache types in the result set.
Example for first DacType (0) of result set:
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
var dataView = new PXSelect<SOOrder>(Base);
PXResultset<SOOrder> resultSet = dataView.Select();
foreach (PXResult result in resultSet)
{
Type dacType = result.GetItemType(0);
foreach (var field in Base.Caches[dacType].Fields)
PXTrace.WriteInformation(field);
}
}
}

Returning a dataset / array from a Generic Inquiry in BLC / graph code

I need to obtain a dataset or array of the results provided by a Generic Inquiry inside of BLC / graph logic.
I've been given the following as an example, but this obviously only returns a count.
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public PXAction<SOOrder> Test;
[PXButton]
[PXUIField]
protected void test()
{
var dataGraph = PXGenericInqGrph.CreateInstance("GI000010");
if (dataGraph != null)
{
var count = dataGraph.Views["Results"].SelectMulti().Count;
}
}
}
When I use an index in the returned variable, I don't get anything that resembles a row of the actual data in the GI, for example:
dataGraph.Views["Results"].SelectMulti()[0]
This doesn't return the actual data. I've tried the several methods / properties provided by the dataGraph.Views["Results"] object, but none give me the results I need.
Is there a method or property that simply returns a dataset or array of the actual results of the Generic Inquiry?
The following custom button I developed for Non-Stock Items will call the specified Generic Inquiry, update its parameter value and retrieve all result rows and corresponding field results without needing to know the specific DAC.
public class NonStockItemMaintExtension : PXGraphExtension<NonStockItemMaint>
{
#region Buttons
public PXAction<InventoryItem> RunSummary;
[PXButton]
[PXUIField(DisplayName = "Run Summary")]
public virtual IEnumerable runSummary(PXAdapter adapter)
{
InventoryItem item = Base.Item.Current;
GIDesign design = PXSelectReadonly<GIDesign, Where<GIDesign.name, Equal<Required<GIDesign.name>>>>.Select(this.Base, "ItemTrans");
if (design != null)
{
PXLongOperation.StartOperation(this.Base, delegate ()
{
//Creates Generic Inquiry Graph for the specified inquiry
PXGenericInqGrph graph = PXGenericInqGrph.CreateInstance(design.DesignID.Value);
if (design != null)
{
//Sets parameter value
graph.Caches[typeof(GenericFilter)].SetValueExt(graph.Filter.Current, "Item", item.InventoryCD);
//Loops through each returned result row of Generic Inquiry
foreach (GenericResult resultRow in graph.Views["Results"].SelectMulti())
{
//Loops through objects returned from one - not an object per field
foreach (string key in resultRow.Values.Keys)
{
//Loops through list of each object and the fields we need values from for each data key
foreach (GIResult resultMap in PXSelectReadonly<GIResult, Where<GIResult.designID, Equal<Required<GIResult.designID>>, And<GIResult.objectName, Equal<Required<GIResult.objectName>>>>>.Select(graph, new object[] { design.DesignID.Value, key }))
{
//retrieves field value from data object specified
var result = graph.Caches[resultRow.Values[key].GetType()].GetValue(resultRow.Values[key], resultMap.Field);
}
}
}
}
});
}
return adapter.Get();
}
#endregion
}
Tables referenced in Generic Inquiry :
Data fields to retrieve from the inquiry :
Parameters being populated :
Value being retrieved :
This will give you a list of the results. Each list element will contain the records involved with 1 row of the GI result (each joined table). Only those fields included in the GI will have values I believe.
public PXAction<SOOrder> Test;
[PXButton]
[PXUIField]
protected void test()
{
// Using "Invoiced Items" (GI000008) GI in 2017R2
var dataGraph = PXGenericInqGrph.CreateInstance("GI000008");
if (dataGraph == null)
{
return;
}
var resultList = dataGraph.Results.Select().FirstTableItems.ToList();
foreach (GenericResult genericResult in resultList)
{
// Note: not all values are pulled into the DAC, only those used in the query
var arInvoice = GetDac<PX.Objects.AR.ARInvoice>(genericResult);
var arTran = GetDac<PX.Objects.AR.ARTran>(genericResult);
var customer = GetDac<PX.Objects.AR.Customer>(genericResult);
var customerClass = GetDac<PX.Objects.AR.Customer>(genericResult);
var address = GetDac<PX.Objects.AR.Customer>(genericResult);
var bAccount = GetDac<PX.Objects.CR.BAccount>(genericResult);
var inventoryItem = GetDac<PX.Objects.IN.InventoryItem>(genericResult);
var formulaValues = genericResult.Values.Last();
}
}
protected virtual T GetDac<T>(GenericResult genericResult) where T : class, PX.Data.IBqlTable
{
// Example:
//var customer = (PX.Objects.AR.Customer)genericResult.Values["Customer"]
return (T)(genericResult?.Values != null &&
genericResult.Values.ContainsKey(typeof(T).Name)
? genericResult.Values[typeof(T).Name]
: null);
}

Azure Cosmos Db document - want to use replacedocumentasync with a removed property in the document

// I am using code like below
Document doc = client.CreateDocumentQuery<Document>(collectionLink)
.Where(r => r.Id == "doc id")
.AsEnumerable()
.SingleOrDefault();
doc.SetPropertyValue("MyProperty1", "updated value");
Document updated = await client.ReplaceDocumentAsync(doc);
Want to remove "MyProperty2" from the document. How to do this?
It seems that you'd like to update MyProperty1 property and remove MyProperty2 property from your document, the following sample code is for your reference.
private async Task updateDoc()
{
string EndpointUri = "xxxxx";
string PrimaryKey = "xxxxx";
DocumentClient client;
client = new DocumentClient(new Uri(EndpointUri), PrimaryKey);
Document doc = client.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll"))
.Where(r => r.Id == "doc5")
.AsEnumerable()
.SingleOrDefault();
//dynamically cast doc back to your MyPoco
MyPoco poco = (dynamic)doc;
//Update MyProperty1 of the poco object
poco.MyProperty1 = "updated value";
//replace document
Document updateddoc = await client.ReplaceDocumentAsync(doc.SelfLink, poco);
Console.WriteLine(updateddoc);
}
public class MyPoco
{
public string id { get; set; }
public string MyProperty1 { get; set; }
}
My document:
Updated:
Edit:
this would remove "MyProperty3" and "MyProperty4" as well.
As you mentioned, setting property with null would be also a approach.
Document doc = client.CreateDocumentQuery<Document>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll"))
.Where(r => r.Id == "doc5")
.AsEnumerable()
.SingleOrDefault();
doc.SetPropertyValue("MyProperty2", null);
//replace document
Document updateddoc = await client.ReplaceDocumentAsync(doc.SelfLink, doc);
Thanks for replying. You got it what I was asking for.
I did not want to use MyPoco as using Microsoft.Azure.Document is the most flexible. Also, using this approach would also remove any MyProperty3 if it exists with MyPoco.
Using Document class only, following worked:
eachDoc.SetPropertyValue("MyProperty2", null);

Querying DocumentDB for a list of propertiers using Linq's Select

Using Azure's DocumentDb and the .NET API, I have the following method which works great for retrieving lists of entire documents:
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = _Client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
var item = await query.ExecuteNextAsync<T>();
results.AddRange(item);
}
return results;
}
Now, I don't always want to return the entire document (especially considering the DocumentDb RU pricing model), so I thought I should be able to add a .Select projection like so:
public async Task<List<TResult>> GetItemsAsync<T, TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> select)
{
IDocumentQuery<TResult> query = _Client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(_DatabaseId, _Collection),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.Select(select)
.AsDocumentQuery();
List<TResult> results = new List<TResult>();
while (query.HasMoreResults)
{
var item = await query.ExecuteNextAsync<TResult>();
results.AddRange(item);
}
return results;
}
Usage:
var rez = await _docs.GetItemsAsync<ApolloAssetDoc, Guid?>(x => x.MyVal == 5, x => x.ID);
But the second method always return 0 results. Obviously I'm barking up the wrong tree.
Any idea what the correct way to return a list of either dynamic objects for queries where more than one property is selected (eg "SELECT d.id, d.MyVal FROM Items d WHERE d.DocType=0")or a simple list where only a single property is selected (eg "SELECT d.id FROM Items d WHERE d.DocType=0")?
I could repro the issue, if there is no [JsonProperty(PropertyName = "id")] for ID property in the entity Class. If it is not included, please have a try to use the following code:
public class ApolloAssetDoc
{
[JsonProperty(PropertyName = "id")]
public Guid ID { get; set; }
public string MyVal { get; set; }
}
Note: The field is case sensitive.

How to retrieve only a predefined number of results in Azure Tables

I am trying to perform an azure table query.
My table (that saves logs) has thousands of rows of data, and it gets populated with more each second.
Right now i have only 1 partition key, but it doesn't affect the next question.
How can i get back lets say only the 100 latest results.
this is my Entity:
public MainServerLogEntity(string Message)
{
this.PartitionKey = "mainserverlogs";
this.RowKey = (DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks).ToString();
this.Message = Message;
this.Date = DateTime.UtcNow;
}
public MainServerLogEntity() { }
public string Message { get; set; }
public DateTime Date { get; set; }
Right now this is the query i am performing inside a web api i have:
[Route("MainServerLogs")]
[HttpGet]
public IEnumerable<MainServerLogEntity> GetMainServerLogs()
{
CloudTable table = AzureStorageHelpers.GetWebApiTable(connectionString, "mainserverlogs");
TableQuery<MainServerLogEntity> query = new TableQuery<MainServerLogEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "mainserverlogs"));
return table.ExecuteQuery(query);
}
But the problem is that i am getting alot of data, and i am requesting this api every few seconds in order to update the ui.
What should i do? is it possible to define in the query that i only want the 100 first rows?
If it is not possible then what other technique should i use?
Try implementing a .Take(100) on the query like so:
[Route("MainServerLogs")]
[HttpGet]
public IEnumerable<MainServerLogEntity> GetMainServerLogs()
{
CloudTable table = AzureStorageHelpers.GetWebApiTable(connectionString, "mainserverlogs");
TableQuery<MainServerLogEntity> query = new TableQuery<MainServerLogEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "mainserverlogs")).Take(100);
return table.ExecuteQuery(query);
}

Resources