How to get Max() Value using linq - asp.net

I have two table cost and history table, both table have the same field name AccountingMonth, i would like to get Latest AccountingMonth from the two table
var accountingMonthCost= await _context.Cost
.Select(ic => new ProductDTO
{
LatestCostMonth = ic.AccountingMonth
})
.ToListAsync();
History table
var accountingMonthHistory = await _context.history
.Select(ic => new ProductDTO
{
LatestCostMonth = ic.AccountingMonth
})
.ToListAsync();
I need help combine AccountingMonth in both table and get the latest Accounting Month.

Concat both together and ask for the max.
var latestAccountingMonth = await _context.Cost.Select(c=>c.AccountingMonth)
.Concat(_context.history.Select(c=>c.AccountingMonth))
.MaxAsync();

Related

Flutter: How to loop through each field in `DocumentSnapshot.data()`

I have a collection where each user has their own document. In the document I create a map for a new entry. The document would look something like this:
In my app, I want to get the document and then turn it into a list where each map (titled by the time) would be an entry. I tried looping through it but the problem is that there is not a forEach() in the DocumentSnapshot.data() and it is a type of _JsonDocumentSnapshot.
How can I get this as a List where 2021-05-30 20:49:59.671705, 2021-05-30 20:50:00:600294, ... are individual elements in a list. I plan on building each entry on its own using something such as a ListView.builder() so I would like to have a list of all those entries.
Edit:
My code looks something like this:
journal is just a DocumentReference.
journal.snapshots().forEach((element) {
var data = element.data();
print(element.data());
(data ?? {}).forEach((key, value) {
return JournalEntryData(
date: key,
entryText: value['entryText'],
feeling: value['feeling']);
});
});
The problem was that the type of data which was set to element.data() was Object? hence the forEach() method wasn't defined. I tried casting the element.data() as a List which caused errors so I didn't go further down that path. What I should have done was to cast element.data() as a Map which worked. I also should have used journal.get() and not snapshots() as it is a stream so the await keyword would result in the journal.snapshots().forEach() to never end.
The final code looked something like this:
Future<List<JournalEntryData>> getJournalEntries() async {
List<JournalEntryData> entries = [];
await journal.get().then((document) {
Map data = (document.data() as Map);
data.forEach((key, value) {
print('adding entry');
entries.add(JournalEntryData(
date: key,
entryText: value['entryText'],
feeling: value['feeling'],
));
});
});
return entries;
}
and I would do the following to get the entries:
var entries = await getJournalEntries();
Hey for most of my problems like these I choose to go with this approach;
final result = await _db
.collection("collectionName")
.get();
List<Object> toReturn = [];
for (int i = 0; i < result.docs.length; i++) {
// add data to list you want to return.
toReturn.add();
}
Hopefully, this will be helpful to you.
List collectionElements = [];
void messagesStream() async {
await for (var snapshot in _firestore.collection('your collection').snapshots().where(
(snapshot) => snapshot.docs
.every((element) => element.author == currentUser.id),
)) {
collectionElements.add(snapshot);
}
print(collectionElements);
}
Each element would be added to the collectionElements list.
If you want to access only one field in your snapshot this works fine:
final entryText= element.data()['entryText'];
print(entryText); // this would print "lorem ipsum..."

How can I update map data that's in a array in firebase? (Flutter)

I'm using flutter web and firebase for a project and got stuck on a problem. I'm trying to update a map in an array in firestore.
using this:
var val = [];
val.add({'groupUID': groupUID, 'inviteStatus': status});
var userInviteUID;
await users
.document(uid)
.get()
.then((value) => userInviteUID = value.data['inviteUID']);
await invites
.document(userInviteUID)
.updateData({'invites': FieldValue.arrayUnion(val)});
I got this result:
firestore structure
What I want to do is just change the 1 to a 2 in the map. I thought that it would update since its the same value but it just adds it to the array.
I looked around on stack and saw some ways to do it like copying the entire array and changing it where I need to, then adding it back.
But I wanted to know if there was a way to avoid that by adding some modifications to my code. Also let me know if there's a better structure I should use. Appreciate the help!
UPDATE:
var ref = invites.document(userData.inviteUID);
ref.get().then((value) async {
var invitesList = value.data['invites'];
switch (status) {
case 1:
break;
case 2:
var index;
invitesList.asMap().forEach((key, value) {
if (value['groupUID'] == groupUID) index = key;
});
invitesList.removeAt(index);
await invites
.document(userData.inviteUID)
.updateData({'invites': FieldValue.arrayUnion(invitesList)});
break;
default:
}
So I looked at some print statements and seen that the elements with the matching group uid is removed, but looking at firebase, the array isn't overwritten anything...any ideas?
FINAL UPDATE:
var ref = invites.document(userData.inviteUID);
ref.get().then((value) async {
var invitesList = value.data['invites'];
switch (status) {
case 1:
break;
case 2:
var index;
invitesList.asMap().forEach((key, value) {
if (value['groupUID'] == groupUID) index = key;
});
invitesList.removeAt(index);
await invites
.document(userData.inviteUID)
.setData({'invites': FieldValue.arrayUnion(invitesList)});
break;
default:
}
Fixed it by changing updateData to setData.
I looked around on stack and saw some ways to do it like copying the entire array and changing it where I need to, then adding it back.
That's exactly how you are supposed to modify the contents of arrays in Firestore documents. Firestore doesn't support updating array elements by index.
setData creates a new document if it already doesn't exist but if the document exists, the data will be overwritten. To prevent this from happening, you could use SetOptions(merge: true) if you wish to append the data:
set(someData, SetOptions(merge: true))
You can also use update method and provide it the updated data, the pseudo code could look like this:
List<Map<String, dynamic>> updatedList = [...];
Map<String, dynamic> updatedData = {
'existing_map': updatedList,
};
var collection = FirebaseFirestore.instance.collection('collection');
collection
.doc('doc_id')
.update(updatedData);

Using IN operator in DynamoDB from Boto3

I'm new to DynamoDB and trying to query a table based off the presence of a list of certain values for a field.
I have a field doc_id, which is also a secondary index, and I'd like to return all results where doc_id is contained in a list of values.
I'm trying something like this:
response = table.query(
IndexName='doc_id-index',
FilterExpression=In(['27242226'])
)
But clearly that is not correct.
Can anyone point me in the right direction?
Thanks!
with Query operation
A FilterExpression does not allow key attributes. You cannot define a filter expression based on a partition key or a sort key.
So, Your doc_id field is the partition key of the doc_id-index and cannot be used in FilterExpression.
Note
A FilterExpression is applied after the items have already been read; the process of filtering does not consume any additional read capacity units.
I'm assuming you have another field like userId, just to show how to implement IN operation.(Query)
var params = {
TableName: 'tbl',
IndexName: 'doc_id-index',
KeyConditionExpression: 'doc_id= :doc_id',
FilterExpression: 'userId IN (:userId1,:userId2)',//you can add more userId here
ExpressionAttributeValues: {
':doc_id':100,
':userId1':11,
':userId2':12
}
};
If you have more userId you should pass to FilterExpression dynamically.
but in your case, you can use Scan operation
var params = {
TableName : "tbl",
FilterExpression : "doc_id IN (:doc_id1, :doc_id2)",
ExpressionAttributeValues : {
":doc_id1" :100,
":doc_id2" :101
}
};
and even pass to FilterExpression dynamically like below
var documentsId = ["100", "101","200",...];
var documentsObj = {};
var index = 0;
documentsId.forEach((value)=> {
index++;
var documentKey = ":doc_id"+index;
documentsObj[documentKey.toString()] = value;
});
var params = {
TableName: 'job',
FilterExpression: 'doc_id IN ('+Object.keys(documentsObj).toString()+')',
ExpressionAttributeValues: documentsObj,
};
Note:be careful while using Scan operation, less efficient than Query.

Query List of Maps in DynamoDB

I am trying to filter list of maps from a dynamodb table which is of the following format.
{
id: "Number",
users: {
{ userEmail: abc#gmail.com, age:"23" },
{ userEmail: de#gmail.com, age:"41" }
}
}
I need to get the data of the user with userEmail as "abc#gmail.com". Currently I am doing it using the following dynamodb query. Is there any another efficient way to solve this issue ?
var params = {
TableName: 'users',
Key:{
'id': id
}
};
var docClient = new AWS.DynamoDB.DocumentClient();
docClient.get(params, function (err, data) {
if (!err) {
const users = data.Item.users;
const user = users.filter(function (user) {
return user.email == userEmail;
});
// filtered has the required user in it
});
The only way you can get a single item in dynamo by id if you have a table with a partition key. So you need to have a table that looks like:
Email (string) - partition key
Id (some-type) - user id
...other relevant user data
Unfortunately, since a nested field cannot be a partition key you will have to maintain a separate table here and won't be able to use an index in DynamoDB (neither LSI, nor GSI).
It's a common pattern in NoSQL to duplicate data, so there is nothing unusual in it. If you were using Java, you could use transactions library, to ensure that both tables are in sync.
If you are not going to use Java you could read DynamoDB stream of the original database (where emails are nested fields) and update the new table (where emails are partition keys) when an original table is updated.

Delete duplicate documents from DocumentDB

How do I delete duplicate documents based on a document attribute value? For example, documents in a collection are as given below
[ {
"ProductIdentifier": "A100",
"ProductTitle": "Product A",
"_ts": 1491664477
},
{
"ProductIdentifier": "A100",
"ProductTitle": "Product A"
"_ts": 1491664466
}
{
"ProductIdentifier": "B100",
"ProductTitle": "Product B"
"_ts": 1491664477
}
]
I want to delete the second document as it is the same as first document (based on ProductIdentifier) and has a lower timestamp (based on _ts)
There are quite a lot of such duplicate documents in the collection. What is the efficient way to do it in bulk?
It seems that you’d like to group your data and delete duplicates (that with same ProductIdentifier value and lower timestamp) from each group. As far as I know, currently GroupBy does not be supported in DocumentDB. But you can group the data and get the ProductIdentifier from each group via LINQ, and then query documents with same ProductIdentifier and delete the duplicates.
var query = client.CreateDocumentQuery<MyDoc>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll")).Where(d => d.ProductIdentifier != "");
List<MyDoc> list1 = query.ToList();
var result = list1.GroupBy(item => new
{
ProductIdentifier = item.ProductIdentifier,
ProductTitle = item.ProductTitle
})
.Select(group => new
{
ProductIdentifier = group.Key.ProductIdentifier,
ProductTitle = group.Key.ProductTitle
});
foreach (var item in result)
{
var query1 = client.CreateDocumentQuery<MyDoc>(UriFactory.CreateDocumentCollectionUri("testdb", "testcoll")).Where(d => d.ProductIdentifier == item.ProductIdentifier && d.ProductTitle == item.ProductTitle);
if (query1.Count() > 1)
{
//delete duplicates from a group
}
}
Besides, as Larry Maccherone said in this thread, documentdb-lumenize is an aggregation library for DocumentDB written as a stored procedure, which can help us perform GroupBy.
string configString = #"{
cubeConfig: {
groupBy: 'ProductIdentifier',
field: '_ts',
f: 'max'
},
filterQuery: 'SELECT * FROM c'
}";
Object config = JsonConvert.DeserializeObject<Object>(configString);
dynamic result = await client.ExecuteStoredProcedureAsync<dynamic>(UriFactory.CreateStoredProcedureUri("testdb", "testcoll", "cube"), config);
//get group info form result.Response

Resources